Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +44 −24 Original line number Diff line number Diff line Loading @@ -219,6 +219,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private boolean mIsDropEntering; private boolean mIsExiting; private boolean mIsRootTranslucent; @VisibleForTesting int mTopStageAfterFoldDismiss; private DefaultMixedHandler mMixedHandler; private final Toast mSplitUnsupportedToast; Loading Loading @@ -1310,6 +1312,24 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final StageTaskListener toTop = mainStageVisible ? mMainStage : mSideStage; exitSplitScreen(toTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP); } // Dismiss split if the flag record any side of stages. if (mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) { if (ENABLE_SHELL_TRANSITIONS) { // Need manually clear here due to this transition might be aborted due to keyguard // on top and lead to no visible change. clearSplitPairedInRecents(EXIT_REASON_DEVICE_FOLDED); final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct); mSplitTransitions.startDismissTransition(wct, this, mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED); } else { exitSplitScreen( mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage, EXIT_REASON_DEVICE_FOLDED); } mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; } } void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { Loading Loading @@ -1346,15 +1366,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!mMainStage.isActive() || mIsExiting) return; onSplitScreenExit(); clearSplitPairedInRecents(exitReason); 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(exitReason) && mShouldUpdateRecents) { recentTasks.removeSplitPair(mMainStage.getTopVisibleChildTaskId()); recentTasks.removeSplitPair(mSideStage.getTopVisibleChildTaskId()); } }); mShouldUpdateRecents = false; mIsDividerRemoteAnimating = false; mSplitRequest = null; Loading Loading @@ -1481,6 +1494,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } private void clearSplitPairedInRecents(@ExitReason int exitReason) { if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) return; 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 mMainStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId)); mSideStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId)); }); } /** * Unlike exitSplitScreen, this takes a stagetype vs an actual stage-reference and populates * an existing WindowContainerTransaction (rather than applying immediately). This is intended Loading Loading @@ -2208,7 +2232,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } void updateSurfaces(SurfaceControl.Transaction transaction) { /** * Update surfaces of the split screen layout based on the current state * @param transaction to write the updates to */ public void updateSurfaces(SurfaceControl.Transaction transaction) { updateSurfaceBounds(mSplitLayout, transaction, /* applyResizingOffset */ false); mSplitLayout.update(transaction); } Loading @@ -2227,26 +2255,18 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @VisibleForTesting void onFoldedStateChanged(boolean folded) { int topStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; if (!folded) return; if (!mMainStage.isActive()) return; if (!isSplitActive() || !isSplitScreenVisible()) return; // To avoid split dismiss when user fold the device and unfold to use later, we only // record the flag here and try to dismiss on wakeUp callback to ensure split dismiss // when user interact on phone folded. if (mMainStage.isFocused()) { topStageAfterFoldDismiss = STAGE_TYPE_MAIN; mTopStageAfterFoldDismiss = STAGE_TYPE_MAIN; } else if (mSideStage.isFocused()) { topStageAfterFoldDismiss = STAGE_TYPE_SIDE; } if (ENABLE_SHELL_TRANSITIONS) { final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(topStageAfterFoldDismiss, wct); mSplitTransitions.startDismissTransition(wct, this, topStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED); } else { exitSplitScreen( topStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage, EXIT_REASON_DEVICE_FOLDED); mTopStageAfterFoldDismiss = STAGE_TYPE_SIDE; } } Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +8 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.io.PrintWriter; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Predicate; /** Loading Loading @@ -347,6 +348,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { wct.reorder(mChildrenTaskInfo.get(taskId).token, onTop /* onTop */); } void doForAllChildTasks(Consumer<Integer> consumer) { for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) { final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i); consumer.accept(taskInfo.taskId); } } /** Collects all the current child tasks and prepares transaction to evict them to display. */ void evictAllChildren(WindowContainerTransaction wct) { for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +3 −0 Original line number Diff line number Diff line Loading @@ -704,6 +704,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, if (mPipHandler != null) { mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); } if (mSplitHandler != null && mSplitHandler.isSplitActive()) { mSplitHandler.updateSurfaces(startTransaction); } return mUnfoldHandler.startAnimation( mixed.mTransition, info, startTransaction, finishTransaction, finishCB); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java +0 −1 Original line number Diff line number Diff line Loading @@ -270,7 +270,6 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator, @Override public void prepareStartTransaction(Transaction transaction) { mUnfoldBackgroundController.ensureBackground(transaction); mSplitScreenController.get().get().updateSplitScreenSurfaces(transaction); } @Override Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +9 −2 Original line number Diff line number Diff line Loading @@ -347,13 +347,20 @@ public class StageCoordinatorTests extends ShellTestCase { } @Test public void testExitSplitScreenAfterFolded() { when(mMainStage.isActive()).thenReturn(true); public void testExitSplitScreenAfterFoldedAndWakeUp() { when(mMainStage.isFocused()).thenReturn(true); when(mMainStage.getTopVisibleChildTaskId()).thenReturn(INVALID_TASK_ID); mSideStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().setVisible(true).build(); mMainStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().setVisible(true).build(); when(mStageCoordinator.isSplitActive()).thenReturn(true); when(mStageCoordinator.isSplitScreenVisible()).thenReturn(true); mStageCoordinator.onFoldedStateChanged(true); assertEquals(mStageCoordinator.mTopStageAfterFoldDismiss, STAGE_TYPE_MAIN); mStageCoordinator.onFinishedWakingUp(); if (Transitions.ENABLE_SHELL_TRANSITIONS) { verify(mTaskOrganizer).startNewTransition(eq(TRANSIT_SPLIT_DISMISS), notNull()); } else { Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +44 −24 Original line number Diff line number Diff line Loading @@ -219,6 +219,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private boolean mIsDropEntering; private boolean mIsExiting; private boolean mIsRootTranslucent; @VisibleForTesting int mTopStageAfterFoldDismiss; private DefaultMixedHandler mMixedHandler; private final Toast mSplitUnsupportedToast; Loading Loading @@ -1310,6 +1312,24 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final StageTaskListener toTop = mainStageVisible ? mMainStage : mSideStage; exitSplitScreen(toTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP); } // Dismiss split if the flag record any side of stages. if (mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) { if (ENABLE_SHELL_TRANSITIONS) { // Need manually clear here due to this transition might be aborted due to keyguard // on top and lead to no visible change. clearSplitPairedInRecents(EXIT_REASON_DEVICE_FOLDED); final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct); mSplitTransitions.startDismissTransition(wct, this, mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED); } else { exitSplitScreen( mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage, EXIT_REASON_DEVICE_FOLDED); } mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; } } void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { Loading Loading @@ -1346,15 +1366,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!mMainStage.isActive() || mIsExiting) return; onSplitScreenExit(); clearSplitPairedInRecents(exitReason); 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(exitReason) && mShouldUpdateRecents) { recentTasks.removeSplitPair(mMainStage.getTopVisibleChildTaskId()); recentTasks.removeSplitPair(mSideStage.getTopVisibleChildTaskId()); } }); mShouldUpdateRecents = false; mIsDividerRemoteAnimating = false; mSplitRequest = null; Loading Loading @@ -1481,6 +1494,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } private void clearSplitPairedInRecents(@ExitReason int exitReason) { if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) return; 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 mMainStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId)); mSideStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId)); }); } /** * Unlike exitSplitScreen, this takes a stagetype vs an actual stage-reference and populates * an existing WindowContainerTransaction (rather than applying immediately). This is intended Loading Loading @@ -2208,7 +2232,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } void updateSurfaces(SurfaceControl.Transaction transaction) { /** * Update surfaces of the split screen layout based on the current state * @param transaction to write the updates to */ public void updateSurfaces(SurfaceControl.Transaction transaction) { updateSurfaceBounds(mSplitLayout, transaction, /* applyResizingOffset */ false); mSplitLayout.update(transaction); } Loading @@ -2227,26 +2255,18 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @VisibleForTesting void onFoldedStateChanged(boolean folded) { int topStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; if (!folded) return; if (!mMainStage.isActive()) return; if (!isSplitActive() || !isSplitScreenVisible()) return; // To avoid split dismiss when user fold the device and unfold to use later, we only // record the flag here and try to dismiss on wakeUp callback to ensure split dismiss // when user interact on phone folded. if (mMainStage.isFocused()) { topStageAfterFoldDismiss = STAGE_TYPE_MAIN; mTopStageAfterFoldDismiss = STAGE_TYPE_MAIN; } else if (mSideStage.isFocused()) { topStageAfterFoldDismiss = STAGE_TYPE_SIDE; } if (ENABLE_SHELL_TRANSITIONS) { final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(topStageAfterFoldDismiss, wct); mSplitTransitions.startDismissTransition(wct, this, topStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED); } else { exitSplitScreen( topStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage, EXIT_REASON_DEVICE_FOLDED); mTopStageAfterFoldDismiss = STAGE_TYPE_SIDE; } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +8 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.io.PrintWriter; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Predicate; /** Loading Loading @@ -347,6 +348,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { wct.reorder(mChildrenTaskInfo.get(taskId).token, onTop /* onTop */); } void doForAllChildTasks(Consumer<Integer> consumer) { for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) { final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i); consumer.accept(taskInfo.taskId); } } /** Collects all the current child tasks and prepares transaction to evict them to display. */ void evictAllChildren(WindowContainerTransaction wct) { for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +3 −0 Original line number Diff line number Diff line Loading @@ -704,6 +704,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, if (mPipHandler != null) { mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); } if (mSplitHandler != null && mSplitHandler.isSplitActive()) { mSplitHandler.updateSurfaces(startTransaction); } return mUnfoldHandler.startAnimation( mixed.mTransition, info, startTransaction, finishTransaction, finishCB); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java +0 −1 Original line number Diff line number Diff line Loading @@ -270,7 +270,6 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator, @Override public void prepareStartTransaction(Transaction transaction) { mUnfoldBackgroundController.ensureBackground(transaction); mSplitScreenController.get().get().updateSplitScreenSurfaces(transaction); } @Override Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +9 −2 Original line number Diff line number Diff line Loading @@ -347,13 +347,20 @@ public class StageCoordinatorTests extends ShellTestCase { } @Test public void testExitSplitScreenAfterFolded() { when(mMainStage.isActive()).thenReturn(true); public void testExitSplitScreenAfterFoldedAndWakeUp() { when(mMainStage.isFocused()).thenReturn(true); when(mMainStage.getTopVisibleChildTaskId()).thenReturn(INVALID_TASK_ID); mSideStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().setVisible(true).build(); mMainStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().setVisible(true).build(); when(mStageCoordinator.isSplitActive()).thenReturn(true); when(mStageCoordinator.isSplitScreenVisible()).thenReturn(true); mStageCoordinator.onFoldedStateChanged(true); assertEquals(mStageCoordinator.mTopStageAfterFoldDismiss, STAGE_TYPE_MAIN); mStageCoordinator.onFinishedWakingUp(); if (Transitions.ENABLE_SHELL_TRANSITIONS) { verify(mTaskOrganizer).startNewTransition(eq(TRANSIT_SPLIT_DISMISS), notNull()); } else { Loading