Merge "Improve shell transition of dismiss by divider"

This commit is contained in:
Chilun Huang
2023-02-13 07:49:26 +00:00
committed by Android (Google) Code Review
3 changed files with 100 additions and 25 deletions

View File

@@ -22,6 +22,8 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
@@ -179,6 +181,33 @@ class SplitScreenTransitions {
onFinish(null /* wct */, null /* wctCB */);
}
void applyDismissTransition(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken topRoot,
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
@NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
if (mPendingDismiss.mDismissTop != STAGE_TYPE_UNDEFINED) {
mFinishCallback = finishCallback;
mAnimatingTransition = transition;
mFinishTransaction = finishTransaction;
final SplitDecorManager topDecor = mPendingDismiss.mDismissTop == STAGE_TYPE_MAIN
? mainDecor : sideDecor;
topDecor.fadeOutDecor(() -> {
mTransitions.getMainExecutor().execute(() -> {
onFinish(null /* wct */, null /* wctCB */);
});
});
startTransaction.apply();
} else {
playAnimation(transition, info, startTransaction, finishTransaction,
finishCallback, mainRoot, sideRoot, topRoot);
}
}
void applyResizeTransition(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@@ -200,8 +229,11 @@ class SplitScreenTransitions {
SplitDecorManager decor = mainRoot.equals(change.getContainer())
? mainDecor : sideDecor;
// This is to ensure onFinished be called after all animations ended.
ValueAnimator va = new ValueAnimator();
mAnimations.add(va);
decor.setScreenshotIfNeeded(change.getSnapshot(), startTransaction);
decor.onResized(startTransaction, () -> {
mTransitions.getMainExecutor().execute(() -> {

View File

@@ -1354,8 +1354,18 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
if (!mMainStage.isActive()) return;
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
// Set the dismiss-to-top side to fullscreen for dismiss transition.
// Reparent the non-dismiss-to-top side to properly update its visibility.
if (stageToTop == STAGE_TYPE_MAIN) {
wct.setBounds(mMainStage.mRootTaskInfo.token, null /* bounds */);
mSideStage.removeAllTasks(wct, false /* toTop */);
} else if (stageToTop == STAGE_TYPE_SIDE) {
wct.setBounds(mSideStage.mRootTaskInfo.token, null /* bounds */);
mMainStage.deactivate(wct, false /* toTop */);
} else {
mSideStage.removeAllTasks(wct, false /* toTop */);
mMainStage.deactivate(wct, false /* toTop */);
}
}
private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
@@ -2273,6 +2283,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
} else if (mSplitTransitions.isPendingDismiss(transition)) {
shouldAnimate = startPendingDismissAnimation(
mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
if (shouldAnimate) {
mSplitTransitions.applyDismissTransition(transition, info,
startTransaction, finishTransaction, finishCallback, mRootTaskInfo.token,
mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token,
mMainStage.getSplitDecorManager(), mSideStage.getSplitDecorManager());
return true;
}
} else if (mSplitTransitions.isPendingResize(transition)) {
mSplitTransitions.applyResizeTransition(transition, info, startTransaction,
finishTransaction, finishCallback, mMainStage.mRootTaskInfo.token,
@@ -2403,37 +2420,44 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// aren't serialized with transition callbacks.
// TODO(b/184679596): Find a way to either include task-org information in
// the transition, or synchronize task-org callbacks.
if (mMainStage.getChildCount() != 0) {
final StringBuilder tasksLeft = new StringBuilder();
for (int i = 0; i < mMainStage.getChildCount(); ++i) {
tasksLeft.append(i != 0 ? ", " : "");
tasksLeft.append(mMainStage.mChildrenTaskInfo.keyAt(i));
if (toStage == STAGE_TYPE_UNDEFINED) {
if (mMainStage.getChildCount() != 0) {
final StringBuilder tasksLeft = new StringBuilder();
for (int i = 0; i < mMainStage.getChildCount(); ++i) {
tasksLeft.append(i != 0 ? ", " : "");
tasksLeft.append(mMainStage.mChildrenTaskInfo.keyAt(i));
}
Log.w(TAG, "Expected onTaskVanished on " + mMainStage
+ " to have been called with [" + tasksLeft.toString()
+ "] before startAnimation().");
}
Log.w(TAG, "Expected onTaskVanished on " + mMainStage
+ " to have been called with [" + tasksLeft.toString()
+ "] before startAnimation().");
}
if (mSideStage.getChildCount() != 0) {
final StringBuilder tasksLeft = new StringBuilder();
for (int i = 0; i < mSideStage.getChildCount(); ++i) {
tasksLeft.append(i != 0 ? ", " : "");
tasksLeft.append(mSideStage.mChildrenTaskInfo.keyAt(i));
if (mSideStage.getChildCount() != 0) {
final StringBuilder tasksLeft = new StringBuilder();
for (int i = 0; i < mSideStage.getChildCount(); ++i) {
tasksLeft.append(i != 0 ? ", " : "");
tasksLeft.append(mSideStage.mChildrenTaskInfo.keyAt(i));
}
Log.w(TAG, "Expected onTaskVanished on " + mSideStage
+ " to have been called with [" + tasksLeft.toString()
+ "] before startAnimation().");
}
Log.w(TAG, "Expected onTaskVanished on " + mSideStage
+ " to have been called with [" + tasksLeft.toString()
+ "] before startAnimation().");
}
mRecentTasks.ifPresent(recentTasks -> {
// Notify recents if we are exiting in a way that breaks the pair, and disable further
// updates to splits in the recents until we enter split again
if (shouldBreakPairedTaskInRecents(dismissReason) && mShouldUpdateRecents) {
for (TransitionInfo.Change change : info.getChanges()) {
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (taskInfo != null
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
recentTasks.removeSplitPair(taskInfo.taskId);
if (toStage == STAGE_TYPE_UNDEFINED) {
for (TransitionInfo.Change change : info.getChanges()) {
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (taskInfo != null
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
recentTasks.removeSplitPair(taskInfo.taskId);
}
}
} else {
recentTasks.removeSplitPair(mMainStage.getTopVisibleChildTaskId());
recentTasks.removeSplitPair(mSideStage.getTopVisibleChildTaskId());
}
}
});
@@ -2446,6 +2470,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Reset crops so they don't interfere with subsequent launches
t.setCrop(mMainStage.mRootLeash, null);
t.setCrop(mSideStage.mRootLeash, null);
// Hide the non-top stage and set the top one to the fullscreen position.
if (toStage != STAGE_TYPE_UNDEFINED) {
t.hide(toStage == STAGE_TYPE_MAIN ? mSideStage.mRootLeash : mMainStage.mRootLeash);
t.setPosition(toStage == STAGE_TYPE_MAIN
? mMainStage.mRootLeash : mSideStage.mRootLeash, 0, 0);
}
if (toStage == STAGE_TYPE_UNDEFINED) {
logExit(dismissReason);
@@ -2472,6 +2502,18 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.release(t);
mSplitTransitions.mPendingDismiss = null;
return false;
} else {
final @SplitScreen.StageType int dismissTop = dismissTransition.mDismissTop;
// Reparent all tasks after dismiss transition finished.
dismissTransition.setFinishedCallback(
new SplitScreenTransitions.TransitionFinishedCallback() {
@Override
public void onFinished(WindowContainerTransaction wct,
SurfaceControl.Transaction t) {
mSideStage.removeAllTasks(wct, dismissTop == STAGE_TYPE_SIDE);
mMainStage.deactivate(wct, dismissTop == STAGE_TYPE_MAIN);
}
});
}
addDividerBarToTransition(info, finishT, false /* show */);

View File

@@ -371,7 +371,8 @@ public class SplitTransitionTests extends ShellTestCase {
IBinder transition = mock(IBinder.class);
WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
assertTrue(containsSplitExit(result));
// Don't reparent tasks until the animation is complete.
assertFalse(containsSplitExit(result));
// make sure we haven't made any local changes yet (need to wait until transition is ready)
assertTrue(mStageCoordinator.isSplitScreenVisible());