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

Commit 88671a05 authored by Chris Li's avatar Chris Li Committed by Android (Google) Code Review
Browse files

Merge "Notify organizer when the last activity in TaskFragment enter PiP" into tm-dev

parents 96770f25 63ece2d2
Loading
Loading
Loading
Loading
+20 −12
Original line number Diff line number Diff line
@@ -52,9 +52,6 @@ public final class TaskFragmentInfo implements Parcelable {
    @NonNull
    private final Configuration mConfiguration = new Configuration();

    /** Whether the TaskFragment contains any child Window Container. */
    private final boolean mIsEmpty;

    /** The number of the running activities in the TaskFragment. */
    private final int mRunningActivityCount;

@@ -77,21 +74,27 @@ public final class TaskFragmentInfo implements Parcelable {
     */
    private final boolean mIsTaskClearedForReuse;

    /**
     * Whether the last running activity in the TaskFragment was reparented to a different Task
     * because it is entering PiP.
     */
    private final boolean mIsTaskFragmentClearedForPip;

    /** @hide */
    public TaskFragmentInfo(
            @NonNull IBinder fragmentToken, @NonNull WindowContainerToken token,
            @NonNull Configuration configuration, boolean isEmpty, int runningActivityCount,
            @NonNull Configuration configuration, int runningActivityCount,
            boolean isVisible, @NonNull List<IBinder> activities, @NonNull Point positionInParent,
            boolean isTaskClearedForReuse) {
            boolean isTaskClearedForReuse, boolean isTaskFragmentClearedForPip) {
        mFragmentToken = requireNonNull(fragmentToken);
        mToken = requireNonNull(token);
        mConfiguration.setTo(configuration);
        mIsEmpty = isEmpty;
        mRunningActivityCount = runningActivityCount;
        mIsVisible = isVisible;
        mActivities.addAll(activities);
        mPositionInParent = requireNonNull(positionInParent);
        mIsTaskClearedForReuse = isTaskClearedForReuse;
        mIsTaskFragmentClearedForPip = isTaskFragmentClearedForPip;
    }

    @NonNull
@@ -110,7 +113,7 @@ public final class TaskFragmentInfo implements Parcelable {
    }

    public boolean isEmpty() {
        return mIsEmpty;
        return mRunningActivityCount == 0;
    }

    public boolean hasRunningActivity() {
@@ -140,6 +143,11 @@ public final class TaskFragmentInfo implements Parcelable {
        return mIsTaskClearedForReuse;
    }

    /** @hide */
    public boolean isTaskFragmentClearedForPip() {
        return mIsTaskFragmentClearedForPip;
    }

    @WindowingMode
    public int getWindowingMode() {
        return mConfiguration.windowConfiguration.getWindowingMode();
@@ -156,25 +164,25 @@ public final class TaskFragmentInfo implements Parcelable {

        return mFragmentToken.equals(that.mFragmentToken)
                && mToken.equals(that.mToken)
                && mIsEmpty == that.mIsEmpty
                && mRunningActivityCount == that.mRunningActivityCount
                && mIsVisible == that.mIsVisible
                && getWindowingMode() == that.getWindowingMode()
                && mActivities.equals(that.mActivities)
                && mPositionInParent.equals(that.mPositionInParent)
                && mIsTaskClearedForReuse == that.mIsTaskClearedForReuse;
                && mIsTaskClearedForReuse == that.mIsTaskClearedForReuse
                && mIsTaskFragmentClearedForPip == that.mIsTaskFragmentClearedForPip;
    }

    private TaskFragmentInfo(Parcel in) {
        mFragmentToken = in.readStrongBinder();
        mToken = in.readTypedObject(WindowContainerToken.CREATOR);
        mConfiguration.readFromParcel(in);
        mIsEmpty = in.readBoolean();
        mRunningActivityCount = in.readInt();
        mIsVisible = in.readBoolean();
        in.readBinderList(mActivities);
        mPositionInParent = requireNonNull(in.readTypedObject(Point.CREATOR));
        mIsTaskClearedForReuse = in.readBoolean();
        mIsTaskFragmentClearedForPip = in.readBoolean();
    }

    /** @hide */
@@ -183,12 +191,12 @@ public final class TaskFragmentInfo implements Parcelable {
        dest.writeStrongBinder(mFragmentToken);
        dest.writeTypedObject(mToken, flags);
        mConfiguration.writeToParcel(dest, flags);
        dest.writeBoolean(mIsEmpty);
        dest.writeInt(mRunningActivityCount);
        dest.writeBoolean(mIsVisible);
        dest.writeBinderList(mActivities);
        dest.writeTypedObject(mPositionInParent, flags);
        dest.writeBoolean(mIsTaskClearedForReuse);
        dest.writeBoolean(mIsTaskFragmentClearedForPip);
    }

    @NonNull
@@ -210,12 +218,12 @@ public final class TaskFragmentInfo implements Parcelable {
        return "TaskFragmentInfo{"
                + " fragmentToken=" + mFragmentToken
                + " token=" + mToken
                + " isEmpty=" + mIsEmpty
                + " runningActivityCount=" + mRunningActivityCount
                + " isVisible=" + mIsVisible
                + " activities=" + mActivities
                + " positionInParent=" + mPositionInParent
                + " isTaskClearedForReuse=" + mIsTaskClearedForReuse
                + " isTaskFragmentClearedForPip" + mIsTaskFragmentClearedForPip
                + "}";
    }

+12 −7
Original line number Diff line number Diff line
@@ -155,13 +155,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        // Check if there are no running activities - consider the container empty if there are no
        // non-finishing activities left.
        if (!taskFragmentInfo.hasRunningActivity()) {
            // TODO(b/225371112): Don't finish dependent if the last activity is moved to the PIP
            // Task.
            if (taskFragmentInfo.isTaskFragmentClearedForPip()) {
                // Do not finish the dependents if the last activity is reparented to PiP.
                // Instead, the original split should be cleanup, and the dependent may be expanded
                // to fullscreen.
                cleanupForEnterPip(container);
                mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
            } else {
                // Do not finish the dependents if this TaskFragment was cleared due to launching
                // activity in the Task.
            final boolean shouldFinishDependent =
                    !taskFragmentInfo.isTaskClearedForReuse();
                final boolean shouldFinishDependent = !taskFragmentInfo.isTaskClearedForReuse();
                mPresenter.cleanupContainer(container, shouldFinishDependent);
            }
        } else if (wasInPip && isInPip) {
            // No update until exit PIP.
            return;
+16 −0
Original line number Diff line number Diff line
@@ -2006,6 +2006,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
            // of the activity entering PIP
            r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);

            final TaskFragment organizedTf = r.getOrganizedTaskFragment();
            // TODO: Does it make sense to only count non-finishing activities?
            final boolean singleActivity = task.getActivityCount() == 1;
            final Task rootTask;
@@ -2043,6 +2044,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
                    task.clearLastRecentsAnimationTransaction(false /* forceRemoveOverlay */);
                }

                // The organized TaskFragment is becoming empty because this activity is reparented
                // to a new PIP Task. In this case, we should notify the organizer about why the
                // TaskFragment becomes empty.
                if (organizedTf != null && organizedTf.getNonFinishingActivityCount() == 1
                        && organizedTf.getTopNonFinishingActivity() == r) {
                    organizedTf.mClearedTaskFragmentForPip = true;
                }

                // There are multiple activities in the task and moving the top activity should
                // reveal/leave the other activities in their original task.
                // On the other hand, ActivityRecord#onParentChanged takes care of setting the
@@ -2107,6 +2116,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
            // Reset the state that indicates it can enter PiP while pausing after we've moved it
            // to the root pinned task
            r.supportsEnterPipOnTaskSwitch = false;

            if (organizedTf != null && organizedTf.mClearedTaskFragmentForPip) {
                // Dispatch the pending info to TaskFragmentOrganizer before PIP animation.
                // Otherwise, it will keep waiting for the empty TaskFragment to be non-empty.
                mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
                        organizedTf);
            }
        } finally {
            mService.continueWindowLayout();
        }
+20 −9
Original line number Diff line number Diff line
@@ -192,6 +192,12 @@ class TaskFragment extends WindowContainer<WindowContainer> {
     */
    boolean mClearedTaskForReuse;

    /**
     * The last running activity of the TaskFragment was reparented to a different Task because it
     * is entering PiP.
     */
    boolean mClearedTaskFragmentForPip;

    /**
     * When we are in the process of pausing an activity, before starting the
     * next one, this variable holds the activity that is currently being paused.
@@ -847,6 +853,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
        return getActivity((r) -> r.canBeTopRunning() && r.getTask() == this.getTask());
    }

    int getNonFinishingActivityCount() {
        final int[] runningActivityCount = new int[1];
        forAllActivities(a -> {
            if (!a.finishing) {
                runningActivityCount[0]++;
            }
        });
        return runningActivityCount[0];
    }

    boolean isTopActivityFocusable() {
        final ActivityRecord r = topRunningActivity();
        return r != null ? r.isFocusable()
@@ -1709,6 +1725,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
    void addChild(WindowContainer child, int index) {
        ActivityRecord r = topRunningActivity();
        mClearedTaskForReuse = false;
        mClearedTaskFragmentForPip = false;

        boolean isAddingActivity = child.asActivityRecord() != null;
        final Task task = isAddingActivity ? getTask() : null;
@@ -2253,22 +2270,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
        }
        final Point positionInParent = new Point();
        getRelativePosition(positionInParent);
        final int[] runningActivityCount = new int[1];
        forAllActivities(a -> {
            if (!a.finishing) {
                runningActivityCount[0]++;
            }
        });
        return new TaskFragmentInfo(
                mFragmentToken,
                mRemoteToken.toWindowContainerToken(),
                getConfiguration(),
                runningActivityCount[0] == 0,
                runningActivityCount[0],
                getNonFinishingActivityCount(),
                isVisible(),
                childActivities,
                positionInParent,
                mClearedTaskForReuse);
                mClearedTaskForReuse,
                mClearedTaskFragmentForPip);
    }

    @Nullable
+33 −1
Original line number Diff line number Diff line
@@ -209,7 +209,7 @@ public class TaskFragmentTest extends WindowTestsBase {
    }

    @Test
    public void testEmbeddedTaskFragmentEnterPip_resetOrganizerOverrideConfig() {
    public void testEmbeddedTaskFragmentEnterPip_singleActivity_resetOrganizerOverrideConfig() {
        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
                .setOrganizer(mOrganizer)
                .setFragmentToken(new Binder())
@@ -241,6 +241,38 @@ public class TaskFragmentTest extends WindowTestsBase {
        assertEquals(Configuration.EMPTY, taskFragment.getRequestedOverrideConfiguration());
    }

    @Test
    public void testEmbeddedTaskFragmentEnterPip_multiActivities_notifyOrganizer() {
        final Task task = createTask(mDisplayContent);
        final TaskFragment taskFragment0 = new TaskFragmentBuilder(mAtm)
                .setParentTask(task)
                .setOrganizer(mOrganizer)
                .setFragmentToken(new Binder())
                .createActivityCount(1)
                .build();
        final TaskFragment taskFragment1 = new TaskFragmentBuilder(mAtm)
                .setParentTask(task)
                .setOrganizer(mOrganizer)
                .setFragmentToken(new Binder())
                .createActivityCount(1)
                .build();
        final ActivityRecord activity0 = taskFragment0.getTopMostActivity();
        spyOn(mAtm.mTaskFragmentOrganizerController);

        // Move activity to pinned.
        mRootWindowContainer.moveActivityToPinnedRootTask(activity0,
                null /* launchIntoPipHostActivity */, "test");

        // Ensure taskFragment requested config is reset.
        assertTrue(taskFragment0.mClearedTaskFragmentForPip);
        assertFalse(taskFragment1.mClearedTaskFragmentForPip);
        final TaskFragmentInfo info = taskFragment0.getTaskFragmentInfo();
        assertTrue(info.isTaskFragmentClearedForPip());
        assertTrue(info.isEmpty());
        verify(mAtm.mTaskFragmentOrganizerController)
                .dispatchPendingInfoChangedEvent(taskFragment0);
    }

    @Test
    public void testActivityHasOverlayOverUntrustedModeEmbedded() {
        final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,