diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 91a77fe1f714f..bfc6417f10988 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3295,7 +3295,7 @@ package android.window { method @NonNull public android.window.WindowContainerTransaction reparentTasks(@Nullable android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, @Nullable int[], @Nullable int[], boolean); method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect); method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int); - method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken); + method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken, boolean); method @NonNull public android.window.WindowContainerTransaction setAdjacentTaskFragments(@NonNull android.os.IBinder, @Nullable android.os.IBinder, @Nullable android.window.WindowContainerTransaction.TaskFragmentAdjacentParams); method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect); method @NonNull public android.window.WindowContainerTransaction setBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect); diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index bbf8138913871..6864ccaa7c8a3 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -368,10 +368,12 @@ public final class WindowContainerTransaction implements Parcelable { */ @NonNull public WindowContainerTransaction setAdjacentRoots( - @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) { + @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2, + boolean moveTogether) { mHierarchyOps.add(HierarchyOp.createForAdjacentRoots( root1.asBinder(), - root2.asBinder())); + root2.asBinder(), + moveTogether)); return this; } @@ -975,6 +977,9 @@ public final class WindowContainerTransaction implements Parcelable { private boolean mReparentTopOnly; + // TODO(b/207185041): Remove this once having a single-top root for split screen. + private boolean mMoveAdjacentTogether; + @Nullable private int[] mWindowingModes; @@ -1033,10 +1038,13 @@ public final class WindowContainerTransaction implements Parcelable { .build(); } - public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) { + /** Create a hierarchy op for setting adjacent root tasks. */ + public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2, + boolean moveTogether) { return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS) .setContainer(root1) .setReparentContainer(root2) + .setMoveAdjacentTogether(moveTogether) .build(); } @@ -1070,6 +1078,7 @@ public final class WindowContainerTransaction implements Parcelable { mReparent = copy.mReparent; mToTop = copy.mToTop; mReparentTopOnly = copy.mReparentTopOnly; + mMoveAdjacentTogether = copy.mMoveAdjacentTogether; mWindowingModes = copy.mWindowingModes; mActivityTypes = copy.mActivityTypes; mLaunchOptions = copy.mLaunchOptions; @@ -1084,6 +1093,7 @@ public final class WindowContainerTransaction implements Parcelable { mReparent = in.readStrongBinder(); mToTop = in.readBoolean(); mReparentTopOnly = in.readBoolean(); + mMoveAdjacentTogether = in.readBoolean(); mWindowingModes = in.createIntArray(); mActivityTypes = in.createIntArray(); mLaunchOptions = in.readBundle(); @@ -1128,6 +1138,10 @@ public final class WindowContainerTransaction implements Parcelable { return mReparentTopOnly; } + public boolean getMoveAdjacentTogether() { + return mMoveAdjacentTogether; + } + public int[] getWindowingModes() { return mWindowingModes; } @@ -1175,7 +1189,8 @@ public final class WindowContainerTransaction implements Parcelable { return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}"; case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "{SetAdjacentRoot: container=" + mContainer - + " adjacentRoot=" + mReparent + "}"; + + " adjacentRoot=" + mReparent + " mMoveAdjacentTogether=" + + mMoveAdjacentTogether + "}"; case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "{LaunchTask: " + mLaunchOptions + "}"; case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: @@ -1212,6 +1227,7 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeStrongBinder(mReparent); dest.writeBoolean(mToTop); dest.writeBoolean(mReparentTopOnly); + dest.writeBoolean(mMoveAdjacentTogether); dest.writeIntArray(mWindowingModes); dest.writeIntArray(mActivityTypes); dest.writeBundle(mLaunchOptions); @@ -1251,6 +1267,8 @@ public final class WindowContainerTransaction implements Parcelable { private boolean mReparentTopOnly; + private boolean mMoveAdjacentTogether; + @Nullable private int[] mWindowingModes; @@ -1293,6 +1311,11 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + Builder setMoveAdjacentTogether(boolean moveAdjacentTogether) { + mMoveAdjacentTogether = moveAdjacentTogether; + return this; + } + Builder setWindowingModes(@Nullable int[] windowingModes) { mWindowingModes = windowingModes; return this; @@ -1336,6 +1359,7 @@ public final class WindowContainerTransaction implements Parcelable { : null; hierarchyOp.mToTop = mToTop; hierarchyOp.mReparentTopOnly = mReparentTopOnly; + hierarchyOp.mMoveAdjacentTogether = mMoveAdjacentTogether; hierarchyOp.mLaunchOptions = mLaunchOptions; hierarchyOp.mActivityIntent = mActivityIntent; hierarchyOp.mPendingIntent = mPendingIntent; 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 aaf210dd715d6..8ce58ae333f8a 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 @@ -793,7 +793,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) { final WindowContainerTransaction wct = new WindowContainerTransaction(); // Make the stages adjacent to each other so they occlude what's behind them. - wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); + wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token, + true /* moveTogether */); wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); mTaskOrganizer.applyTransaction(wct); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java index 60a6cd78c4fc6..47eb271800c3a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java @@ -634,7 +634,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mUseLegacySplit = mContext.getResources().getBoolean(R.bool.config_useLegacySplit); final WindowContainerTransaction wct = new WindowContainerTransaction(); // Make the stages adjacent to each other so they occlude what's behind them. - wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); + wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token, + true /* moveTogether */); // Only sets side stage as launch-adjacent-flag-root when the device is not using legacy // split to prevent new split behavior confusing users. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 44e59660752c5..a21fc1942cc9d 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4650,23 +4650,14 @@ class Task extends TaskFragment { moveToFront(reason, null); } - /** - * @param reason The reason for moving the root task to the front. - * @param task If non-null, the task will be moved to the top of the root task. - */ void moveToFront(String reason, Task task) { - if (!isAttached()) { - return; - } - - final TaskDisplayArea taskDisplayArea = getDisplayArea(); - if (inSplitScreenSecondaryWindowingMode()) { // If the root task is in split-screen secondary mode, we need to make sure we move the // primary split-screen root task forward in the case it is currently behind a // fullscreen root task so both halves of the split-screen appear on-top and the // fullscreen root task isn't cutting between them. // TODO(b/70677280): This is a workaround until we can fix as part of b/70677280. + final TaskDisplayArea taskDisplayArea = getDisplayArea(); final Task topFullScreenRootTask = taskDisplayArea.getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN); if (topFullScreenRootTask != null) { @@ -4674,10 +4665,30 @@ class Task extends TaskFragment { taskDisplayArea.getRootSplitScreenPrimaryTask(); if (primarySplitScreenRootTask != null && topFullScreenRootTask.compareTo(primarySplitScreenRootTask) > 0) { - primarySplitScreenRootTask.moveToFront(reason + " splitScreenToTop"); + primarySplitScreenRootTask.moveToFrontInner(reason + " splitScreenToTop", + null /* task */); } } + } else if (mMoveAdjacentTogether && getAdjacentTaskFragment() != null) { + final Task adjacentTask = getAdjacentTaskFragment().asTask(); + if (adjacentTask != null) { + adjacentTask.moveToFrontInner(reason + " adjacentTaskToTop", null /* task */); + } } + moveToFrontInner(reason, task); + } + + /** + * @param reason The reason for moving the root task to the front. + * @param task If non-null, the task will be moved to the top of the root task. + */ + @VisibleForTesting + void moveToFrontInner(String reason, Task task) { + if (!isAttached()) { + return; + } + + final TaskDisplayArea taskDisplayArea = getDisplayArea(); if (!isActivityTypeHome() && returnsToHomeRootTask()) { // Make sure the root home task is behind this root task since that is where we diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index f32ab1e53318d..985128aaec35a 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -168,6 +168,14 @@ class TaskFragment extends WindowContainer { @Nullable private TaskFragment mAdjacentTaskFragment; + /** + * Whether to move adjacent task fragment together when re-positioning. + * + * @see #mAdjacentTaskFragment + */ + // TODO(b/207185041): Remove this once having a single-top root for split screen. + boolean mMoveAdjacentTogether; + /** * Prevents duplicate calls to onTaskAppeared. */ @@ -313,14 +321,15 @@ class TaskFragment extends WindowContainer { return service.mWindowOrganizerController.getTaskFragment(token); } - void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment) { + void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment, boolean moveTogether) { if (mAdjacentTaskFragment == taskFragment) { return; } resetAdjacentTaskFragment(); if (taskFragment != null) { mAdjacentTaskFragment = taskFragment; - taskFragment.setAdjacentTaskFragment(this); + mMoveAdjacentTogether = moveTogether; + taskFragment.setAdjacentTaskFragment(this, moveTogether); } } @@ -329,9 +338,11 @@ class TaskFragment extends WindowContainer { if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) { mAdjacentTaskFragment.mAdjacentTaskFragment = null; mAdjacentTaskFragment.mDelayLastActivityRemoval = false; + mAdjacentTaskFragment.mMoveAdjacentTogether = false; } mAdjacentTaskFragment = null; mDelayLastActivityRemoval = false; + mMoveAdjacentTogether = false; } void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid, diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 877965e5bfde1..42766bdb07320 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -665,7 +665,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } - tf1.setAdjacentTaskFragment(tf2); + tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */); effects |= TRANSACT_EFFECTS_LIFECYCLE; final Bundle bundle = hop.getLaunchOptions(); @@ -978,7 +978,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by" + " organizer root1=" + root1 + " root2=" + root2); } - root1.setAdjacentTaskFragment(root2); + root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether()); return TRANSACT_EFFECTS_LIFECYCLE; } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index d68edbafb5925..cdf6b59d4737d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -84,7 +84,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); adjacentRootTask.mCreatedByOrganizer = true; final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); - adjacentRootTask.setAdjacentTaskFragment(rootTask); + adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */); taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask); Task actualRootTask = taskDisplayArea.getLaunchRootTask( @@ -110,7 +110,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final Task adjacentRootTask = createTask( mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); adjacentRootTask.mCreatedByOrganizer = true; - adjacentRootTask.setAdjacentTaskFragment(rootTask); + adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */); taskDisplayArea.setLaunchRootTask(rootTask, new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD}); @@ -131,7 +131,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); adjacentRootTask.mCreatedByOrganizer = true; final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); - adjacentRootTask.setAdjacentTaskFragment(rootTask); + adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */); taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask); final Task actualRootTask = taskDisplayArea.getLaunchRootTask( diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 7014851ee2103..dcaf9d7ae4342 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -330,7 +330,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { // Throw exception if the transaction is trying to change a window that is not organized by // the organizer. - mTransaction.setAdjacentRoots(mFragmentWindowToken, token2); + mTransaction.setAdjacentRoots(mFragmentWindowToken, token2, false /* moveTogether */); assertThrows(SecurityException.class, () -> { try { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index ce568f152a5f2..8c1045d995d68 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -59,6 +59,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.clearInvocations; @@ -1365,6 +1366,25 @@ public class TaskTests extends WindowTestsBase { assertNotNull(activity.getTask().getDimmer()); } + @Test + public void testMoveToFront_moveAdjacentTask() { + final Task task1 = + createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + final Task task2 = + createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + spyOn(task2); + + task1.setAdjacentTaskFragment(task2, false /* moveTogether */); + task1.moveToFront("" /* reason */); + verify(task2, never()).moveToFrontInner(anyString(), isNull()); + + // Reset adjacent tasks to move together. + task1.setAdjacentTaskFragment(null, false /* moveTogether */); + task1.setAdjacentTaskFragment(task2, true /* moveTogether */); + task1.moveToFront("" /* reason */); + verify(task2).moveToFrontInner(anyString(), isNull()); + } + private Task getTestTask() { final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); return task.getBottomMostTask(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 1eed79f189a83..75a87ba9e04d9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -554,7 +554,7 @@ public class WindowOrganizerTests extends WindowTestsBase { final RunningTaskInfo info2 = task2.getTaskInfo(); WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.setAdjacentRoots(info1.token, info2.token); + wct.setAdjacentRoots(info1.token, info2.token, false /* moveTogether */); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertEquals(task1.getAdjacentTaskFragment(), task2); assertEquals(task2.getAdjacentTaskFragment(), task1); @@ -564,8 +564,8 @@ public class WindowOrganizerTests extends WindowTestsBase { mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1); - task1.setAdjacentTaskFragment(null); - task2.setAdjacentTaskFragment(null); + task1.setAdjacentTaskFragment(null, false /* moveTogether */); + task2.setAdjacentTaskFragment(null, false /* moveTogether */); wct = new WindowContainerTransaction(); wct.clearLaunchAdjacentFlagRoot(info1.token); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java index 2d3e5c002c2dc..049966c7310d8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java @@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; @@ -490,8 +489,8 @@ public class ZOrderingTests extends WindowTestsBase { createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); final WindowState splitWindow2 = createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2"); - splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2); - splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1); + splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2, true /* moveTogether */); + splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1, true /* moveTogether */); final Task aboveTask = createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); @@ -529,8 +528,8 @@ public class ZOrderingTests extends WindowTestsBase { createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); final WindowState splitWindow2 = createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2"); - splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2); - splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1); + splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2, true /* moveTogether */); + splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1, true /* moveTogether */); mDisplayContent.assignChildLayers(mTransaction);