Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 77a74ecc authored by Jiaming Liu's avatar Jiaming Liu Committed by Android (Google) Code Review
Browse files

Merge "Allow TaskFragment to move to bottom when clear top" into main

parents f825eb94 f646ee11
Loading
Loading
Loading
Loading
+43 −4
Original line number Diff line number Diff line
@@ -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,
@@ -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 {}
@@ -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;
@@ -188,6 +201,7 @@ public final class TaskFragmentOperation implements Parcelable {
        mAnimationParams = animationParams;
        mIsolatedNav = isolatedNav;
        mDimOnTask = dimOnTask;
        mMoveToBottomIfClearWhenLaunch = moveToBottomIfClearWhenLaunch;
    }

    private TaskFragmentOperation(Parcel in) {
@@ -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
@@ -213,6 +228,7 @@ public final class TaskFragmentOperation implements Parcelable {
        dest.writeTypedObject(mAnimationParams, flags);
        dest.writeBoolean(mIsolatedNav);
        dest.writeBoolean(mDimOnTask);
        dest.writeBoolean(mMoveToBottomIfClearWhenLaunch);
    }

    @NonNull
@@ -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();
@@ -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();
@@ -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
@@ -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
@@ -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}.
         */
@@ -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}.
         */
@@ -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);
        }
    }
}
+47 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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),
@@ -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.
+14 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;
+17 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;
    }
@@ -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,
+29 −0
Original line number Diff line number Diff line
@@ -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();
    }