diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 095be8c00419e..380032b83db6c 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -442,7 +442,7 @@ public final class WindowContainerTransaction implements Parcelable { /** * Starts an activity in the TaskFragment. * @param fragmentToken client assigned unique token to create TaskFragment with specified in - * {@link TaskFragmentCreationParams#fragmentToken}. + * {@link TaskFragmentCreationParams#getFragmentToken()}. * @param callerToken the activity token that initialized the activity launch. * @param activityIntent intent to start the activity. * @param activityOptions ActivityOptions to start the activity with. @@ -468,7 +468,7 @@ public final class WindowContainerTransaction implements Parcelable { /** * Moves an activity into the TaskFragment. * @param fragmentToken client assigned unique token to create TaskFragment with specified in - * {@link TaskFragmentCreationParams#fragmentToken}. + * {@link TaskFragmentCreationParams#getFragmentToken()}. * @param activityToken activity to be reparented. * @hide */ @@ -505,6 +505,30 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + /** + * Sets to TaskFragments adjacent to each other. Containers below two visible adjacent + * TaskFragments will be made invisible. This is similar to + * {@link #setAdjacentRoots(WindowContainerToken, WindowContainerToken)}, but can be used with + * fragmentTokens when that TaskFragments haven't been created (but will be created in the same + * {@link WindowContainerTransaction}). + * @param fragmentToken1 client assigned unique token to create TaskFragment with specified + * in {@link TaskFragmentCreationParams#getFragmentToken()}. + * @param fragmentToken2 client assigned unique token to create TaskFragment with specified + * in {@link TaskFragmentCreationParams#getFragmentToken()}. + * @hide + */ + @NonNull + public WindowContainerTransaction setAdjacentTaskFragments( + @NonNull IBinder fragmentToken1, @NonNull IBinder fragmentToken2) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS) + .setContainer(fragmentToken1) + .setReparentContainer(fragmentToken2) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + /** * When this {@link WindowContainerTransaction} failed to finish on the server side, it will * trigger callback with this {@param errorCallbackToken}. @@ -908,6 +932,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT = 10; public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11; public static final int HIERARCHY_OP_TYPE_PENDING_INTENT = 12; + public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS = 13; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -1136,6 +1161,9 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: return "{ReparentChildren: oldParent=" + mContainer + " newParent=" + mReparent + "}"; + case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: + return "{SetAdjacentTaskFragments: container=" + mContainer + + " adjacentContainer=" + mReparent + "}"; default: return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 1788a527147bb..0be21cbd1bcee 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -29,6 +29,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT; @@ -715,6 +716,19 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } reparentTaskFragment(oldParent, newParent, errorCallbackToken); break; + case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: + fragmentToken = hop.getContainer(); + final IBinder adjacentFragmentToken = hop.getAdjacentRoot(); + final TaskFragment tf1 = mLaunchTaskFragments.get(fragmentToken); + final TaskFragment tf2 = mLaunchTaskFragments.get(adjacentFragmentToken); + if (tf1 == null || tf2 == null) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to set adjacent on invalid fragment tokens"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + break; + } + tf1.setAdjacentTaskFragment(tf2); + break; } return effects; } @@ -1047,9 +1061,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // valid. case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: + case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: // We are allowing organizer to start/reparent activity to a TaskFragment it - // created. Nothing to check here because the TaskFragment may not be created - // yet, but will be created in the same transaction. + // created, or set two TaskFragments adjacent to each other. Nothing to check + // here because the TaskFragment may not be created yet, but will be created in + // the same transaction. break; case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: enforceTaskFragmentOrganized(func, 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 025da846245dc..f83f1cf0ad1b2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -321,6 +321,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { mTransaction.startActivityInTaskFragment( mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */); mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class)); + mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class)); // It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are // testing the security check here.