diff options
8 files changed, 146 insertions, 25 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 9e88a260ac44..99a89a6b884f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -300,7 +300,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter, mTaskOrganizer, mDisplayController, mDisplayImeController, mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider, mMainExecutor, mMainHandler, mRecentTasksOptional, mLaunchAdjacentController, - mWindowDecorViewModel, mSplitState, mDesktopTasksController); + mWindowDecorViewModel, mSplitState, mDesktopTasksController, mRootTDAOrganizer); } @Override @@ -441,7 +441,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter, */ public void prepareExitSplitScreen(WindowContainerTransaction wct, @StageType int stageToTop, @ExitReason int reason) { - mStageCoordinator.prepareExitSplitScreen(stageToTop, wct); + mStageCoordinator.prepareExitSplitScreen(stageToTop, wct, reason); mStageCoordinator.clearSplitPairedInRecents(reason); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 37c93518998a..511e426cc681 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -130,6 +130,7 @@ import com.android.internal.policy.FoldLockSettingsObserver; import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.ComponentUtils; import com.android.wm.shell.common.DisplayController; @@ -168,6 +169,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.Executor; @@ -218,6 +220,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final SplitscreenEventLogger mLogger; private final ShellExecutor mMainExecutor; private final Handler mMainHandler; + private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer; // Cache live tile tasks while entering recents, evict them from stages in finish transaction // if user is opening another task(s). private final ArrayList<Integer> mPausingTasks = new ArrayList<>(); @@ -354,7 +357,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, Handler mainHandler, Optional<RecentTasksController> recentTasks, LaunchAdjacentController launchAdjacentController, Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState, - Optional<DesktopTasksController> desktopTasksController) { + Optional<DesktopTasksController> desktopTasksController, + RootTaskDisplayAreaOrganizer rootTDAOrganizer) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; @@ -367,6 +371,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mWindowDecorViewModel = windowDecorViewModel; mSplitState = splitState; mDesktopTasksController = desktopTasksController; + mRootTDAOrganizer = rootTDAOrganizer; taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */); @@ -425,7 +430,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, Handler mainHandler, Optional<RecentTasksController> recentTasks, LaunchAdjacentController launchAdjacentController, Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState, - Optional<DesktopTasksController> desktopTasksController) { + Optional<DesktopTasksController> desktopTasksController, + RootTaskDisplayAreaOrganizer rootTDAOrganizer) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; @@ -447,6 +453,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mWindowDecorViewModel = windowDecorViewModel; mSplitState = splitState; mDesktopTasksController = desktopTasksController; + mRootTDAOrganizer = rootTDAOrganizer; mDisplayController.addDisplayWindowListener(this); transitions.addHandler(this); @@ -880,7 +887,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void startSingleTask(int taskId, Bundle options, WindowContainerTransaction wct, RemoteTransition remoteTransition) { if (mMainStage.containsTask(taskId) || mSideStage.containsTask(taskId)) { - prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); + prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct, EXIT_REASON_FULLSCREEN_REQUEST); } if (mRecentTasks.isPresent()) { mRecentTasks.get().removeSplitPair(taskId); @@ -1433,7 +1440,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // on top and lead to no visible change. clearSplitPairedInRecents(reason); final WindowContainerTransaction wct = new WindowContainerTransaction(); - prepareExitSplitScreen(mLastActiveStage, wct); + prepareExitSplitScreen(mLastActiveStage, wct, reason); mSplitTransitions.startDismissTransition(wct, this, mLastActiveStage, reason); setSplitsVisible(false); mBreakOnNextWake = false; @@ -1527,7 +1534,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!isSplitActive()) return; final int stage = getStageOfTask(toTopTaskId); final WindowContainerTransaction wct = new WindowContainerTransaction(); - prepareExitSplitScreen(stage, wct); + prepareExitSplitScreen(stage, wct, exitReason); mSplitTransitions.startDismissTransition(wct, this, stage, exitReason); // reset stages to their default sides. setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, null); @@ -1646,7 +1653,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * to be used when exiting split might be bundled with other window operations. */ void prepareExitSplitScreen(@StageType int stageToTop, - @NonNull WindowContainerTransaction wct) { + @NonNull WindowContainerTransaction wct, @ExitReason int exitReason) { if (!isSplitActive()) return; ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%s", stageTypeToString(stageToTop)); @@ -1657,6 +1664,26 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } else { mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); } + + if (exitReason != EXIT_REASON_DESKTOP_MODE) { + StageTaskListener toTopStage = stageToTop == STAGE_TYPE_MAIN ? mMainStage : mSideStage; + if (enableFlexibleSplit()) { + toTopStage = mStageOrderOperator.getAllStages().stream() + .filter(stage -> stage.getId() == stageToTop) + .findFirst().orElse(null); + } + final DisplayAreaInfo tdaInfo = mRootTDAOrganizer.getDisplayAreaInfo(mDisplayId); + Objects.requireNonNull(tdaInfo); + final int displayWindowingMode = + tdaInfo.configuration.windowConfiguration.getWindowingMode(); + // In freeform-first env, we need to explicitly set the windowing mode when leaving + // the split-screen to be fullscreen. + final int targetWindowingMode = displayWindowingMode == WINDOWING_MODE_FREEFORM + ? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_UNDEFINED; + toTopStage.doForAllChildTaskInfos(taskInfo -> { + wct.setWindowingMode(taskInfo.token, targetWindowingMode); + }); + } deactivateSplit(wct, stageToTop); mSplitState.exit(); } @@ -2331,7 +2358,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; } final WindowContainerTransaction wct = new WindowContainerTransaction(); - prepareExitSplitScreen(stageType, wct); + prepareExitSplitScreen(stageType, wct, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); clearSplitPairedInRecents(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); @@ -2363,10 +2390,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } final WindowContainerTransaction wct = new WindowContainerTransaction(); toTopStage.resetBounds(wct); - prepareExitSplitScreen(dismissTop, wct); + prepareExitSplitScreen(dismissTop, wct, EXIT_REASON_DRAG_DIVIDER); if (mRootTaskInfo != null) { wct.setDoNotPip(mRootTaskInfo.token); } + mSplitTransitions.startDismissTransition(wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER); } @@ -2801,7 +2829,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // The top should be the opposite side that is closing: int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; - prepareExitSplitScreen(dismissTop, out); + prepareExitSplitScreen(dismissTop, out, EXIT_REASON_APP_FINISHED); mSplitTransitions.setDismissTransition(transition, dismissTop, EXIT_REASON_APP_FINISHED); } else if (isOpening && !mPausingTasks.isEmpty()) { @@ -2809,7 +2837,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // recents, which means to dismiss the split pair to this task. int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; - prepareExitSplitScreen(dismissTop, out); + prepareExitSplitScreen(dismissTop, out, EXIT_REASON_APP_FINISHED); mSplitTransitions.setDismissTransition(transition, dismissTop, EXIT_REASON_APP_FINISHED); } else if (!isSplitScreenVisible() && isOpening) { @@ -2822,7 +2850,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // If the trigger task is in fullscreen and in split, exit split and place // task on top final int stageType = getStageOfTask(triggerTask.taskId); - prepareExitSplitScreen(stageType, out); + prepareExitSplitScreen(stageType, out, EXIT_REASON_FULLSCREEN_REQUEST); mSplitTransitions.setDismissTransition(transition, stageType, EXIT_REASON_FULLSCREEN_REQUEST); } @@ -2850,7 +2878,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (anyStageContainsSingleFullscreenTask) { // A splitting task is opening to fullscreen causes one side of the split empty, // so appends operations to exit split. - prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out); + prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out, + EXIT_REASON_FULLSCREEN_REQUEST); } } else if (type == TRANSIT_KEYGUARD_OCCLUDE && triggerTask.topActivity != null && isSplitScreenVisible()) { @@ -2858,7 +2887,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // stage and move it to the top. int top = triggerTask.topActivity.equals(mMainStage.mRootTaskInfo.topActivity) ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; - prepareExitSplitScreen(top, out); + prepareExitSplitScreen(top, out, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP); mSplitTransitions.setDismissTransition(transition, top, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP); } @@ -2940,7 +2969,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, topStage = STAGE_TYPE_SIDE; } } - prepareExitSplitScreen(topStage, outWCT); + prepareExitSplitScreen(topStage, outWCT, EXIT_REASON_UNKNOWN); } } @@ -3127,7 +3156,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // If there is a fullscreen opening change, we should not bring stage to top. prepareExitSplitScreen( !record.mContainShowFullscreenChange && isSplitScreenVisible() - ? dismissTop : STAGE_TYPE_UNDEFINED, wct); + ? dismissTop : STAGE_TYPE_UNDEFINED, wct, EXIT_REASON_APP_FINISHED); mSplitTransitions.startDismissTransition(wct, this, dismissTop, EXIT_REASON_APP_FINISHED); // This can happen in some pathological cases. For example: @@ -3368,7 +3397,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, (sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED); pendingEnter.cancel( (cancelWct, cancelT) -> { - prepareExitSplitScreen(dismissTop, cancelWct); + prepareExitSplitScreen(dismissTop, cancelWct, EXIT_REASON_UNKNOWN); logExit(EXIT_REASON_UNKNOWN); }); Log.w(TAG, splitFailureMessage("startPendingEnterAnimation", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 021f6595d984..194114db0169 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -379,6 +379,13 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { } } + void doForAllChildTaskInfos(Consumer<ActivityManager.RunningTaskInfo> consumer) { + for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) { + final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i); + consumer.accept(taskInfo); + } + } + /** 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--) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java index c5e158c6b452..d71b7a1183f4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java @@ -62,6 +62,7 @@ public class TvSplitScreenController extends SplitScreenController { private final Optional<RecentTasksController> mRecentTasksOptional; private final LaunchAdjacentController mLaunchAdjacentController; private final SplitState mSplitState; + private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer; private final Handler mMainHandler; private final SystemWindows mSystemWindows; @@ -109,6 +110,7 @@ public class TvSplitScreenController extends SplitScreenController { mMainHandler = mainHandler; mSystemWindows = systemWindows; + mRootTDAOrganizer = rootTDAOrganizer; } /** @@ -121,7 +123,8 @@ public class TvSplitScreenController extends SplitScreenController { mTaskOrganizer, mDisplayController, mDisplayImeController, mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider, mMainExecutor, mMainHandler, - mRecentTasksOptional, mLaunchAdjacentController, mSplitState, mSystemWindows); + mRecentTasksOptional, mLaunchAdjacentController, mSplitState, mSystemWindows, + mRootTDAOrganizer); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java index e1bf12fc6082..d7f1ced1b432 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java @@ -20,6 +20,7 @@ import android.content.Context; import android.os.Handler; import com.android.launcher3.icons.IconProvider; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; @@ -55,11 +56,11 @@ public class TvStageCoordinator extends StageCoordinator Optional<RecentTasksController> recentTasks, LaunchAdjacentController launchAdjacentController, SplitState splitState, - SystemWindows systemWindows) { + SystemWindows systemWindows, RootTaskDisplayAreaOrganizer rootTDAOrganizer) { super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController, displayInsetsController, transitions, transactionPool, iconProvider, mainExecutor, mainHandler, recentTasks, launchAdjacentController, - Optional.empty(), splitState, Optional.empty()); + Optional.empty(), splitState, Optional.empty(), rootTDAOrganizer); mTvSplitMenuController = new TvSplitMenuController(context, this, systemWindows, mainHandler); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java index ae0c9d6cbf7c..414c0147660c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java @@ -26,6 +26,7 @@ import android.os.Handler; import android.view.SurfaceControl; import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; @@ -83,12 +84,13 @@ public class SplitTestUtils { Optional<RecentTasksController> recentTasks, LaunchAdjacentController launchAdjacentController, Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState, - Optional<DesktopTasksController> desktopTasksController) { + Optional<DesktopTasksController> desktopTasksController, + RootTaskDisplayAreaOrganizer rootTDAOrganizer) { super(context, displayId, syncQueue, taskOrganizer, mainStage, sideStage, displayController, imeController, insetsController, splitLayout, transitions, transactionPool, mainExecutor, mainHandler, recentTasks, launchAdjacentController, windowDecorViewModel, splitState, - desktopTasksController); + desktopTasksController, rootTDAOrganizer); // Prepare root task for testing. mRootTask = new TestRunningTaskInfoBuilder().build(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index b0fdfacd7d83..4211e4682810 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -47,6 +47,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.app.ActivityManager; @@ -54,6 +55,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.view.SurfaceControl; +import android.window.DisplayAreaInfo; import android.window.IRemoteTransition; import android.window.RemoteTransition; import android.window.TransitionInfo; @@ -65,6 +67,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.icons.IconProvider; +import com.android.wm.shell.MockToken; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; @@ -121,6 +124,8 @@ public class SplitTransitionTests extends ShellTestCase { private StageTaskListener mSideStage; private StageCoordinator mStageCoordinator; private SplitScreenTransitions mSplitScreenTransitions; + private final DisplayAreaInfo mDisplayAreaInfo = new DisplayAreaInfo(new MockToken().token(), + DEFAULT_DISPLAY, 0); private ActivityManager.RunningTaskInfo mMainChild; private ActivityManager.RunningTaskInfo mSideChild; @@ -146,7 +151,9 @@ public class SplitTransitionTests extends ShellTestCase { mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mMainExecutor, mMainHandler, Optional.empty(), - mLaunchAdjacentController, Optional.empty(), mSplitState, Optional.empty()); + mLaunchAdjacentController, Optional.empty(), mSplitState, Optional.empty(), + mRootTDAOrganizer); + when(mRootTDAOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(mDisplayAreaInfo); mStageCoordinator.setMixedHandler(mMixedHandler); mSplitScreenTransitions = mStageCoordinator.getSplitTransitions(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index 7a88ace3f85f..5851cbf9b933 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -17,6 +17,8 @@ package com.android.wm.shell.splitscreen; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; @@ -27,11 +29,14 @@ import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSIT import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; +import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; +import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -39,6 +44,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -56,6 +62,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.view.SurfaceControl; +import android.window.DisplayAreaInfo; import android.window.RemoteTransition; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -64,6 +71,8 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.wm.shell.MockToken; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; @@ -94,6 +103,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Optional; +import java.util.function.Consumer; /** * Tests for {@link StageCoordinator} @@ -125,6 +135,8 @@ public class StageCoordinatorTests extends ShellTestCase { private DefaultMixedHandler mDefaultMixedHandler; @Mock private SplitState mSplitState; + @Mock + private RootTaskDisplayAreaOrganizer mRootTDAOrganizer; private final Rect mBounds1 = new Rect(10, 20, 30, 40); private final Rect mBounds2 = new Rect(5, 10, 15, 20); @@ -138,6 +150,10 @@ public class StageCoordinatorTests extends ShellTestCase { private final TestShellExecutor mMainExecutor = new TestShellExecutor(); private final ShellExecutor mAnimExecutor = new TestShellExecutor(); private final Handler mMainHandler = new Handler(Looper.getMainLooper()); + private final DisplayAreaInfo mDisplayAreaInfo = new DisplayAreaInfo(new MockToken().token(), + DEFAULT_DISPLAY, 0); + private final ActivityManager.RunningTaskInfo mMainChildTaskInfo = + new TestRunningTaskInfoBuilder().setVisible(true).build(); @Before @UiThreadTest @@ -148,9 +164,10 @@ public class StageCoordinatorTests extends ShellTestCase { mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mMainExecutor, mMainHandler, Optional.empty(), mLaunchAdjacentController, - Optional.empty(), mSplitState, Optional.empty())); + Optional.empty(), mSplitState, Optional.empty(), mRootTDAOrganizer)); mDividerLeash = new SurfaceControl.Builder().setName("fakeDivider").build(); + when(mRootTDAOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(mDisplayAreaInfo); when(mSplitLayout.getTopLeftBounds()).thenReturn(mBounds1); when(mSplitLayout.getBottomRightBounds()).thenReturn(mBounds2); @@ -167,6 +184,12 @@ public class StageCoordinatorTests extends ShellTestCase { mMainStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build(); doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager(); doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager(); + + doAnswer(invocation -> { + Consumer<ActivityManager.RunningTaskInfo> consumer = invocation.getArgument(0); + consumer.accept(mMainChildTaskInfo); + return null; + }).when(mMainStage).doForAllChildTaskInfos(any()); } @Test @@ -454,6 +477,55 @@ public class StageCoordinatorTests extends ShellTestCase { int windowingMode = wctCaptor.getValue().getChanges().get(binder).getWindowingMode(); assertEquals(windowingMode, WINDOWING_MODE_UNDEFINED); } + @Test + public void testDismiss_freeformDisplay() { + mDisplayAreaInfo.configuration.windowConfiguration.setWindowingMode( + WINDOWING_MODE_FREEFORM); + when(mStageCoordinator.isSplitActive()).thenReturn(true); + + WindowContainerTransaction wct = new WindowContainerTransaction(); + mStageCoordinator.prepareExitSplitScreen(STAGE_TYPE_MAIN, wct, EXIT_REASON_DRAG_DIVIDER); + + assertEquals(wct.getChanges().get(mMainChildTaskInfo.token.asBinder()).getWindowingMode(), + WINDOWING_MODE_FULLSCREEN); + } + + @Test + public void testDismiss_freeformDisplayToDesktop() { + mDisplayAreaInfo.configuration.windowConfiguration.setWindowingMode( + WINDOWING_MODE_FREEFORM); + when(mStageCoordinator.isSplitActive()).thenReturn(true); + + WindowContainerTransaction wct = new WindowContainerTransaction(); + mStageCoordinator.prepareExitSplitScreen(STAGE_TYPE_MAIN, wct, EXIT_REASON_DESKTOP_MODE); + + WindowContainerTransaction.Change c = + wct.getChanges().get(mMainChildTaskInfo.token.asBinder()); + assertFalse(c != null && c.getWindowingMode() == WINDOWING_MODE_FULLSCREEN); + } + + @Test + public void testDismiss_fullscreenDisplay() { + when(mStageCoordinator.isSplitActive()).thenReturn(true); + + WindowContainerTransaction wct = new WindowContainerTransaction(); + mStageCoordinator.prepareExitSplitScreen(STAGE_TYPE_MAIN, wct, EXIT_REASON_DRAG_DIVIDER); + + assertEquals(wct.getChanges().get(mMainChildTaskInfo.token.asBinder()).getWindowingMode(), + WINDOWING_MODE_UNDEFINED); + } + + @Test + public void testDismiss_fullscreenDisplayToDesktop() { + when(mStageCoordinator.isSplitActive()).thenReturn(true); + + WindowContainerTransaction wct = new WindowContainerTransaction(); + mStageCoordinator.prepareExitSplitScreen(STAGE_TYPE_MAIN, wct, EXIT_REASON_DESKTOP_MODE); + + WindowContainerTransaction.Change c = + wct.getChanges().get(mMainChildTaskInfo.token.asBinder()); + assertFalse(c != null && c.getWindowingMode() == WINDOWING_MODE_FULLSCREEN); + } private Transitions createTestTransitions() { ShellInit shellInit = new ShellInit(mMainExecutor); |