Loading core/java/android/window/TaskFragmentOperation.java +43 −4 Original line number Diff line number Diff line Loading @@ -125,6 +125,16 @@ public final class TaskFragmentOperation implements Parcelable { */ public static final int OP_TYPE_SET_DIM_ON_TASK = 16; /** * Sets this TaskFragment to move to bottom of the Task if any of the activities below it is * launched in a mode requiring clear top. * * This is only allowed for system organizers. See * {@link com.android.server.wm.TaskFragmentOrganizerController#registerOrganizer( * ITaskFragmentOrganizer, boolean)} */ public static final int OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH = 17; @IntDef(prefix = { "OP_TYPE_" }, value = { OP_TYPE_UNKNOWN, OP_TYPE_CREATE_TASK_FRAGMENT, Loading @@ -144,6 +154,7 @@ public final class TaskFragmentOperation implements Parcelable { OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE, OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE, OP_TYPE_SET_DIM_ON_TASK, OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH, }) @Retention(RetentionPolicy.SOURCE) public @interface OperationType {} Loading Loading @@ -173,12 +184,14 @@ public final class TaskFragmentOperation implements Parcelable { private final boolean mDimOnTask; private final boolean mMoveToBottomIfClearWhenLaunch; private TaskFragmentOperation(@OperationType int opType, @Nullable TaskFragmentCreationParams taskFragmentCreationParams, @Nullable IBinder activityToken, @Nullable Intent activityIntent, @Nullable Bundle bundle, @Nullable IBinder secondaryFragmentToken, @Nullable TaskFragmentAnimationParams animationParams, boolean isolatedNav, boolean dimOnTask) { boolean isolatedNav, boolean dimOnTask, boolean moveToBottomIfClearWhenLaunch) { mOpType = opType; mTaskFragmentCreationParams = taskFragmentCreationParams; mActivityToken = activityToken; Loading @@ -188,6 +201,7 @@ public final class TaskFragmentOperation implements Parcelable { mAnimationParams = animationParams; mIsolatedNav = isolatedNav; mDimOnTask = dimOnTask; mMoveToBottomIfClearWhenLaunch = moveToBottomIfClearWhenLaunch; } private TaskFragmentOperation(Parcel in) { Loading @@ -200,6 +214,7 @@ public final class TaskFragmentOperation implements Parcelable { mAnimationParams = in.readTypedObject(TaskFragmentAnimationParams.CREATOR); mIsolatedNav = in.readBoolean(); mDimOnTask = in.readBoolean(); mMoveToBottomIfClearWhenLaunch = in.readBoolean(); } @Override Loading @@ -213,6 +228,7 @@ public final class TaskFragmentOperation implements Parcelable { dest.writeTypedObject(mAnimationParams, flags); dest.writeBoolean(mIsolatedNav); dest.writeBoolean(mDimOnTask); dest.writeBoolean(mMoveToBottomIfClearWhenLaunch); } @NonNull Loading Loading @@ -300,6 +316,14 @@ public final class TaskFragmentOperation implements Parcelable { return mDimOnTask; } /** * Returns whether the TaskFragment should move to bottom of task when any activity below it * is launched in clear top mode. */ public boolean isMoveToBottomIfClearWhenLaunch() { return mMoveToBottomIfClearWhenLaunch; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); Loading @@ -324,6 +348,7 @@ public final class TaskFragmentOperation implements Parcelable { } sb.append(", isolatedNav=").append(mIsolatedNav); sb.append(", dimOnTask=").append(mDimOnTask); sb.append(", moveToBottomIfClearWhenLaunch=").append(mMoveToBottomIfClearWhenLaunch); sb.append('}'); return sb.toString(); Loading @@ -332,7 +357,8 @@ public final class TaskFragmentOperation implements Parcelable { @Override public int hashCode() { return Objects.hash(mOpType, mTaskFragmentCreationParams, mActivityToken, mActivityIntent, mBundle, mSecondaryFragmentToken, mAnimationParams, mIsolatedNav, mDimOnTask); mBundle, mSecondaryFragmentToken, mAnimationParams, mIsolatedNav, mDimOnTask, mMoveToBottomIfClearWhenLaunch); } @Override Loading @@ -349,7 +375,8 @@ public final class TaskFragmentOperation implements Parcelable { && Objects.equals(mSecondaryFragmentToken, other.mSecondaryFragmentToken) && Objects.equals(mAnimationParams, other.mAnimationParams) && mIsolatedNav == other.mIsolatedNav && mDimOnTask == other.mDimOnTask; && mDimOnTask == other.mDimOnTask && mMoveToBottomIfClearWhenLaunch == other.mMoveToBottomIfClearWhenLaunch; } @Override Loading Loading @@ -385,6 +412,8 @@ public final class TaskFragmentOperation implements Parcelable { private boolean mDimOnTask; private boolean mMoveToBottomIfClearWhenLaunch; /** * @param opType the {@link OperationType} of this {@link TaskFragmentOperation}. */ Loading Loading @@ -465,6 +494,16 @@ public final class TaskFragmentOperation implements Parcelable { return this; } /** * Sets whether the TaskFragment should move to bottom of task when any activity below it * is launched in clear top mode. */ @NonNull public Builder setMoveToBottomIfClearWhenLaunch(boolean moveToBottomIfClearWhenLaunch) { mMoveToBottomIfClearWhenLaunch = moveToBottomIfClearWhenLaunch; return this; } /** * Constructs the {@link TaskFragmentOperation}. */ Loading @@ -472,7 +511,7 @@ public final class TaskFragmentOperation implements Parcelable { public TaskFragmentOperation build() { return new TaskFragmentOperation(mOpType, mTaskFragmentCreationParams, mActivityToken, mActivityIntent, mBundle, mSecondaryFragmentToken, mAnimationParams, mIsolatedNav, mDimOnTask); mIsolatedNav, mDimOnTask, mMoveToBottomIfClearWhenLaunch); } } } services/core/java/com/android/server/wm/Task.java +47 −0 Original line number Diff line number Diff line Loading @@ -208,6 +208,7 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Predicate; Loading Loading @@ -1702,6 +1703,8 @@ class Task extends TaskFragment { final ActivityRecord r = findActivityInHistory(newR.mActivityComponent, newR.mUserId); if (r == null) return null; moveTaskFragmentsToBottomIfNeeded(r, finishCount); final PooledPredicate f = PooledLambda.obtainPredicate( (ActivityRecord ar, ActivityRecord boundaryActivity) -> finishActivityAbove(ar, boundaryActivity, finishCount), Loading @@ -1722,6 +1725,50 @@ class Task extends TaskFragment { return r; } /** * Moves {@link TaskFragment}s to the bottom if the flag * {@link TaskFragment#isMoveToBottomIfClearWhenLaunch} is {@code true}. */ @VisibleForTesting void moveTaskFragmentsToBottomIfNeeded(@NonNull ActivityRecord r, @NonNull int[] finishCount) { final int activityIndex = mChildren.indexOf(r); if (activityIndex < 0) { return; } List<TaskFragment> taskFragmentsToMove = null; // Find the TaskFragments that need to be moved for (int i = mChildren.size() - 1; i > activityIndex; i--) { final TaskFragment taskFragment = mChildren.get(i).asTaskFragment(); if (taskFragment != null && taskFragment.isMoveToBottomIfClearWhenLaunch()) { if (taskFragmentsToMove == null) { taskFragmentsToMove = new ArrayList<>(); } taskFragmentsToMove.add(taskFragment); } } if (taskFragmentsToMove == null) { return; } // Move the TaskFragments to the bottom of the Task. Their relative orders are preserved. final int size = taskFragmentsToMove.size(); for (int i = 0; i < size; i++) { final TaskFragment taskFragment = taskFragmentsToMove.get(i); // The visibility of the TaskFragment may change. Collect it in the transition so that // transition animation can be properly played. mTransitionController.collect(taskFragment); positionChildAt(POSITION_BOTTOM, taskFragment, false /* includeParents */); } // Treat it as if the TaskFragments are finished so that a transition animation can be // played to send the TaskFragments back and bring the activity to front. finishCount[0] += size; } private static boolean finishActivityAbove(ActivityRecord r, ActivityRecord boundaryActivity, @NonNull int[] finishCount) { // Stop operation once we reach the boundary activity. Loading services/core/java/com/android/server/wm/TaskFragment.java +14 −0 Original line number Diff line number Diff line Loading @@ -363,6 +363,12 @@ class TaskFragment extends WindowContainer<WindowContainer> { */ private boolean mIsolatedNav; /** * Whether the TaskFragment should move to bottom of task when any activity below it is * launched in clear top mode. */ private boolean mMoveToBottomIfClearWhenLaunch; /** When set, will force the task to report as invisible. */ static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1; static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1; Loading Loading @@ -3045,6 +3051,14 @@ class TaskFragment extends WindowContainer<WindowContainer> { mEmbeddedDimArea = embeddedDimArea; } void setMoveToBottomIfClearWhenLaunch(boolean moveToBottomIfClearWhenLaunch) { mMoveToBottomIfClearWhenLaunch = moveToBottomIfClearWhenLaunch; } boolean isMoveToBottomIfClearWhenLaunch() { return mMoveToBottomIfClearWhenLaunch; } @VisibleForTesting boolean isDimmingOnParentTask() { return mEmbeddedDimArea == EMBEDDED_DIM_AREA_PARENT_TASK; Loading services/core/java/com/android/server/wm/WindowOrganizerController.java +17 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS; import static android.window.TaskFragmentOperation.OP_TYPE_SET_COMPANION_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION; import static android.window.TaskFragmentOperation.OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH; import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS; import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN; Loading Loading @@ -1514,6 +1515,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub : EMBEDDED_DIM_AREA_TASK_FRAGMENT); break; } case OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH: { taskFragment.setMoveToBottomIfClearWhenLaunch( operation.isMoveToBottomIfClearWhenLaunch()); break; } } return effects; } Loading Loading @@ -1566,6 +1572,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return false; } if ((opType == OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH) && !mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) { final Throwable exception = new SecurityException( "Only a system organizer can perform " + "OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH." ); sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, opType, exception); return false; } final IBinder secondaryFragmentToken = operation.getSecondaryFragmentToken(); return secondaryFragmentToken == null || validateTaskFragment(mLaunchTaskFragments.get(secondaryFragmentToken), opType, Loading services/tests/wmtests/src/com/android/server/wm/TaskTests.java +29 −0 Original line number Diff line number Diff line Loading @@ -1822,6 +1822,35 @@ public class TaskTests extends WindowTestsBase { verify(fragment2).assignLayer(t, 2); } @Test public void testMoveTaskFragmentsToBottomIfNeeded() { final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord unembeddedActivity = task.getTopMostActivity(); final TaskFragment fragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer); final TaskFragment fragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer); final TaskFragment fragment3 = createTaskFragmentWithEmbeddedActivity(task, organizer); doReturn(true).when(fragment1).isMoveToBottomIfClearWhenLaunch(); doReturn(false).when(fragment2).isMoveToBottomIfClearWhenLaunch(); doReturn(true).when(fragment3).isMoveToBottomIfClearWhenLaunch(); assertEquals(unembeddedActivity, task.mChildren.get(0)); assertEquals(fragment1, task.mChildren.get(1)); assertEquals(fragment2, task.mChildren.get(2)); assertEquals(fragment3, task.mChildren.get(3)); final int[] finishCount = {0}; task.moveTaskFragmentsToBottomIfNeeded(unembeddedActivity, finishCount); // fragment1 and fragment3 should be moved to the bottom of the task assertEquals(fragment1, task.mChildren.get(0)); assertEquals(fragment3, task.mChildren.get(1)); assertEquals(unembeddedActivity, task.mChildren.get(2)); assertEquals(fragment2, task.mChildren.get(3)); assertEquals(2, finishCount[0]); } private Task getTestTask() { return new TaskBuilder(mSupervisor).setCreateActivity(true).build(); } Loading Loading
core/java/android/window/TaskFragmentOperation.java +43 −4 Original line number Diff line number Diff line Loading @@ -125,6 +125,16 @@ public final class TaskFragmentOperation implements Parcelable { */ public static final int OP_TYPE_SET_DIM_ON_TASK = 16; /** * Sets this TaskFragment to move to bottom of the Task if any of the activities below it is * launched in a mode requiring clear top. * * This is only allowed for system organizers. See * {@link com.android.server.wm.TaskFragmentOrganizerController#registerOrganizer( * ITaskFragmentOrganizer, boolean)} */ public static final int OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH = 17; @IntDef(prefix = { "OP_TYPE_" }, value = { OP_TYPE_UNKNOWN, OP_TYPE_CREATE_TASK_FRAGMENT, Loading @@ -144,6 +154,7 @@ public final class TaskFragmentOperation implements Parcelable { OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE, OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE, OP_TYPE_SET_DIM_ON_TASK, OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH, }) @Retention(RetentionPolicy.SOURCE) public @interface OperationType {} Loading Loading @@ -173,12 +184,14 @@ public final class TaskFragmentOperation implements Parcelable { private final boolean mDimOnTask; private final boolean mMoveToBottomIfClearWhenLaunch; private TaskFragmentOperation(@OperationType int opType, @Nullable TaskFragmentCreationParams taskFragmentCreationParams, @Nullable IBinder activityToken, @Nullable Intent activityIntent, @Nullable Bundle bundle, @Nullable IBinder secondaryFragmentToken, @Nullable TaskFragmentAnimationParams animationParams, boolean isolatedNav, boolean dimOnTask) { boolean isolatedNav, boolean dimOnTask, boolean moveToBottomIfClearWhenLaunch) { mOpType = opType; mTaskFragmentCreationParams = taskFragmentCreationParams; mActivityToken = activityToken; Loading @@ -188,6 +201,7 @@ public final class TaskFragmentOperation implements Parcelable { mAnimationParams = animationParams; mIsolatedNav = isolatedNav; mDimOnTask = dimOnTask; mMoveToBottomIfClearWhenLaunch = moveToBottomIfClearWhenLaunch; } private TaskFragmentOperation(Parcel in) { Loading @@ -200,6 +214,7 @@ public final class TaskFragmentOperation implements Parcelable { mAnimationParams = in.readTypedObject(TaskFragmentAnimationParams.CREATOR); mIsolatedNav = in.readBoolean(); mDimOnTask = in.readBoolean(); mMoveToBottomIfClearWhenLaunch = in.readBoolean(); } @Override Loading @@ -213,6 +228,7 @@ public final class TaskFragmentOperation implements Parcelable { dest.writeTypedObject(mAnimationParams, flags); dest.writeBoolean(mIsolatedNav); dest.writeBoolean(mDimOnTask); dest.writeBoolean(mMoveToBottomIfClearWhenLaunch); } @NonNull Loading Loading @@ -300,6 +316,14 @@ public final class TaskFragmentOperation implements Parcelable { return mDimOnTask; } /** * Returns whether the TaskFragment should move to bottom of task when any activity below it * is launched in clear top mode. */ public boolean isMoveToBottomIfClearWhenLaunch() { return mMoveToBottomIfClearWhenLaunch; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); Loading @@ -324,6 +348,7 @@ public final class TaskFragmentOperation implements Parcelable { } sb.append(", isolatedNav=").append(mIsolatedNav); sb.append(", dimOnTask=").append(mDimOnTask); sb.append(", moveToBottomIfClearWhenLaunch=").append(mMoveToBottomIfClearWhenLaunch); sb.append('}'); return sb.toString(); Loading @@ -332,7 +357,8 @@ public final class TaskFragmentOperation implements Parcelable { @Override public int hashCode() { return Objects.hash(mOpType, mTaskFragmentCreationParams, mActivityToken, mActivityIntent, mBundle, mSecondaryFragmentToken, mAnimationParams, mIsolatedNav, mDimOnTask); mBundle, mSecondaryFragmentToken, mAnimationParams, mIsolatedNav, mDimOnTask, mMoveToBottomIfClearWhenLaunch); } @Override Loading @@ -349,7 +375,8 @@ public final class TaskFragmentOperation implements Parcelable { && Objects.equals(mSecondaryFragmentToken, other.mSecondaryFragmentToken) && Objects.equals(mAnimationParams, other.mAnimationParams) && mIsolatedNav == other.mIsolatedNav && mDimOnTask == other.mDimOnTask; && mDimOnTask == other.mDimOnTask && mMoveToBottomIfClearWhenLaunch == other.mMoveToBottomIfClearWhenLaunch; } @Override Loading Loading @@ -385,6 +412,8 @@ public final class TaskFragmentOperation implements Parcelable { private boolean mDimOnTask; private boolean mMoveToBottomIfClearWhenLaunch; /** * @param opType the {@link OperationType} of this {@link TaskFragmentOperation}. */ Loading Loading @@ -465,6 +494,16 @@ public final class TaskFragmentOperation implements Parcelable { return this; } /** * Sets whether the TaskFragment should move to bottom of task when any activity below it * is launched in clear top mode. */ @NonNull public Builder setMoveToBottomIfClearWhenLaunch(boolean moveToBottomIfClearWhenLaunch) { mMoveToBottomIfClearWhenLaunch = moveToBottomIfClearWhenLaunch; return this; } /** * Constructs the {@link TaskFragmentOperation}. */ Loading @@ -472,7 +511,7 @@ public final class TaskFragmentOperation implements Parcelable { public TaskFragmentOperation build() { return new TaskFragmentOperation(mOpType, mTaskFragmentCreationParams, mActivityToken, mActivityIntent, mBundle, mSecondaryFragmentToken, mAnimationParams, mIsolatedNav, mDimOnTask); mIsolatedNav, mDimOnTask, mMoveToBottomIfClearWhenLaunch); } } }
services/core/java/com/android/server/wm/Task.java +47 −0 Original line number Diff line number Diff line Loading @@ -208,6 +208,7 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Predicate; Loading Loading @@ -1702,6 +1703,8 @@ class Task extends TaskFragment { final ActivityRecord r = findActivityInHistory(newR.mActivityComponent, newR.mUserId); if (r == null) return null; moveTaskFragmentsToBottomIfNeeded(r, finishCount); final PooledPredicate f = PooledLambda.obtainPredicate( (ActivityRecord ar, ActivityRecord boundaryActivity) -> finishActivityAbove(ar, boundaryActivity, finishCount), Loading @@ -1722,6 +1725,50 @@ class Task extends TaskFragment { return r; } /** * Moves {@link TaskFragment}s to the bottom if the flag * {@link TaskFragment#isMoveToBottomIfClearWhenLaunch} is {@code true}. */ @VisibleForTesting void moveTaskFragmentsToBottomIfNeeded(@NonNull ActivityRecord r, @NonNull int[] finishCount) { final int activityIndex = mChildren.indexOf(r); if (activityIndex < 0) { return; } List<TaskFragment> taskFragmentsToMove = null; // Find the TaskFragments that need to be moved for (int i = mChildren.size() - 1; i > activityIndex; i--) { final TaskFragment taskFragment = mChildren.get(i).asTaskFragment(); if (taskFragment != null && taskFragment.isMoveToBottomIfClearWhenLaunch()) { if (taskFragmentsToMove == null) { taskFragmentsToMove = new ArrayList<>(); } taskFragmentsToMove.add(taskFragment); } } if (taskFragmentsToMove == null) { return; } // Move the TaskFragments to the bottom of the Task. Their relative orders are preserved. final int size = taskFragmentsToMove.size(); for (int i = 0; i < size; i++) { final TaskFragment taskFragment = taskFragmentsToMove.get(i); // The visibility of the TaskFragment may change. Collect it in the transition so that // transition animation can be properly played. mTransitionController.collect(taskFragment); positionChildAt(POSITION_BOTTOM, taskFragment, false /* includeParents */); } // Treat it as if the TaskFragments are finished so that a transition animation can be // played to send the TaskFragments back and bring the activity to front. finishCount[0] += size; } private static boolean finishActivityAbove(ActivityRecord r, ActivityRecord boundaryActivity, @NonNull int[] finishCount) { // Stop operation once we reach the boundary activity. Loading
services/core/java/com/android/server/wm/TaskFragment.java +14 −0 Original line number Diff line number Diff line Loading @@ -363,6 +363,12 @@ class TaskFragment extends WindowContainer<WindowContainer> { */ private boolean mIsolatedNav; /** * Whether the TaskFragment should move to bottom of task when any activity below it is * launched in clear top mode. */ private boolean mMoveToBottomIfClearWhenLaunch; /** When set, will force the task to report as invisible. */ static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1; static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1; Loading Loading @@ -3045,6 +3051,14 @@ class TaskFragment extends WindowContainer<WindowContainer> { mEmbeddedDimArea = embeddedDimArea; } void setMoveToBottomIfClearWhenLaunch(boolean moveToBottomIfClearWhenLaunch) { mMoveToBottomIfClearWhenLaunch = moveToBottomIfClearWhenLaunch; } boolean isMoveToBottomIfClearWhenLaunch() { return mMoveToBottomIfClearWhenLaunch; } @VisibleForTesting boolean isDimmingOnParentTask() { return mEmbeddedDimArea == EMBEDDED_DIM_AREA_PARENT_TASK; Loading
services/core/java/com/android/server/wm/WindowOrganizerController.java +17 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS; import static android.window.TaskFragmentOperation.OP_TYPE_SET_COMPANION_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION; import static android.window.TaskFragmentOperation.OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH; import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS; import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN; Loading Loading @@ -1514,6 +1515,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub : EMBEDDED_DIM_AREA_TASK_FRAGMENT); break; } case OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH: { taskFragment.setMoveToBottomIfClearWhenLaunch( operation.isMoveToBottomIfClearWhenLaunch()); break; } } return effects; } Loading Loading @@ -1566,6 +1572,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return false; } if ((opType == OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH) && !mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) { final Throwable exception = new SecurityException( "Only a system organizer can perform " + "OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH." ); sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, opType, exception); return false; } final IBinder secondaryFragmentToken = operation.getSecondaryFragmentToken(); return secondaryFragmentToken == null || validateTaskFragment(mLaunchTaskFragments.get(secondaryFragmentToken), opType, Loading
services/tests/wmtests/src/com/android/server/wm/TaskTests.java +29 −0 Original line number Diff line number Diff line Loading @@ -1822,6 +1822,35 @@ public class TaskTests extends WindowTestsBase { verify(fragment2).assignLayer(t, 2); } @Test public void testMoveTaskFragmentsToBottomIfNeeded() { final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord unembeddedActivity = task.getTopMostActivity(); final TaskFragment fragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer); final TaskFragment fragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer); final TaskFragment fragment3 = createTaskFragmentWithEmbeddedActivity(task, organizer); doReturn(true).when(fragment1).isMoveToBottomIfClearWhenLaunch(); doReturn(false).when(fragment2).isMoveToBottomIfClearWhenLaunch(); doReturn(true).when(fragment3).isMoveToBottomIfClearWhenLaunch(); assertEquals(unembeddedActivity, task.mChildren.get(0)); assertEquals(fragment1, task.mChildren.get(1)); assertEquals(fragment2, task.mChildren.get(2)); assertEquals(fragment3, task.mChildren.get(3)); final int[] finishCount = {0}; task.moveTaskFragmentsToBottomIfNeeded(unembeddedActivity, finishCount); // fragment1 and fragment3 should be moved to the bottom of the task assertEquals(fragment1, task.mChildren.get(0)); assertEquals(fragment3, task.mChildren.get(1)); assertEquals(unembeddedActivity, task.mChildren.get(2)); assertEquals(fragment2, task.mChildren.get(3)); assertEquals(2, finishCount[0]); } private Task getTestTask() { return new TaskBuilder(mSupervisor).setCreateActivity(true).build(); } Loading