Improve shell transition of dismiss by divider
This is to avoid jank or flicker when dismiss split by divider. Bug: 258241450 Test: manual Test: atest WMShellFlickerTests:DismissSplitScreenByDivider Test: atest SplitTransitionTests Change-Id: I9036c8cdf4a844f4b8572fe76df2dbc5aa8c4e49
This commit is contained in:
@@ -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(() -> {
|
||||
|
||||
@@ -1296,8 +1296,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) {
|
||||
@@ -2214,6 +2224,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,
|
||||
@@ -2317,37 +2334,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());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2360,6 +2384,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);
|
||||
@@ -2386,6 +2416,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 */);
|
||||
|
||||
@@ -385,7 +385,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());
|
||||
|
||||
Reference in New Issue
Block a user