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

Commit d7485418 authored by Chris Li's avatar Chris Li
Browse files

Allow launching placeholder when the TaskFragment is visible

Before, we only launch placeholder from the top most
TaskFragmentContainer. Now we also launch placeholder from the one
that is not the top most, but is visible. This can happen when the
top most is transparent.

Bug: 261550242
Test: atest WMJetpackUnitTests:SplitControllerTest
Change-Id: I6e3c4122d5d618b8a9ac054c19b0a3d19e7d3b20
parent 8f35f7b0
Loading
Loading
Loading
Loading
+37 −17
Original line number Diff line number Diff line
@@ -431,12 +431,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        if (container != null) {
            // Cleanup if the TaskFragment vanished is not requested by the organizer.
            removeContainer(container);
            // Make sure the top container is updated.
            final TaskFragmentContainer newTopContainer = getTopActiveContainer(
                    container.getTaskId());
            if (newTopContainer != null) {
                updateContainer(wct, newTopContainer);
            }
            // Make sure the containers in the Task are up-to-date.
            updateContainersInTaskIfVisible(wct, container.getTaskId());
        }
        cleanupTaskFragment(taskFragmentInfo.getFragmentToken());
    }
@@ -476,6 +472,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        updateContainersInTask(wct, taskContainer);
    }

    void updateContainersInTaskIfVisible(@NonNull WindowContainerTransaction wct, int taskId) {
        final TaskContainer taskContainer = getTaskContainer(taskId);
        if (taskContainer != null && taskContainer.isVisible()) {
            updateContainersInTask(wct, taskContainer);
        }
    }

    private void updateContainersInTask(@NonNull WindowContainerTransaction wct,
            @NonNull TaskContainer taskContainer) {
        // Update all TaskFragments in the Task. Make a copy of the list since some may be
@@ -1328,9 +1331,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    void removeContainer(@NonNull TaskFragmentContainer container) {
        // Remove all split containers that included this one
        final TaskContainer taskContainer = container.getTaskContainer();
        if (taskContainer == null) {
            return;
        }
        taskContainer.mContainers.remove(container);
        // Marked as a pending removal which will be removed after it is actually removed on the
        // server side (#onTaskFragmentVanished).
@@ -1523,14 +1523,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }

        final TaskFragmentContainer container = getContainerWithActivity(activity);
        // Don't launch placeholder if the container is occluded.
        if (container != null && container != getTopActiveContainer(container.getTaskId())) {
            return false;
        }

        final SplitContainer splitContainer = getActiveSplitForContainer(container);
        if (splitContainer != null && container.equals(splitContainer.getPrimaryContainer())) {
            // Don't launch placeholder in primary split container
        if (container != null && !allowLaunchPlaceholder(container)) {
            // We don't allow activity in this TaskFragment to launch placeholder.
            return false;
        }

@@ -1558,6 +1552,32 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        return true;
    }

    /** Whether or not to allow activity in this container to launch placeholder. */
    @GuardedBy("mLock")
    private boolean allowLaunchPlaceholder(@NonNull TaskFragmentContainer container) {
        final TaskFragmentContainer topContainer = getTopActiveContainer(container.getTaskId());
        if (container != topContainer) {
            // The container is not the top most.
            if (!container.isVisible()) {
                // In case the container is visible (the one on top may be transparent), we may
                // still want to launch placeholder even if it is not the top most.
                return false;
            }
            if (topContainer.isWaitingActivityAppear()) {
                // When the top container appeared info is not sent by the server yet, the visible
                // check above may not be reliable.
                return false;
            }
        }

        final SplitContainer splitContainer = getActiveSplitForContainer(container);
        if (splitContainer != null && container.equals(splitContainer.getPrimaryContainer())) {
            // Don't launch placeholder for primary split container.
            return false;
        }
        return true;
    }

    /**
     * Gets the activity options for starting the placeholder activity. In case the placeholder is
     * launched when the Task is in the background, we don't want to bring the Task to the front.
+2 −6
Original line number Diff line number Diff line
@@ -154,12 +154,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
    void cleanupContainer(@NonNull WindowContainerTransaction wct,
            @NonNull TaskFragmentContainer container, boolean shouldFinishDependent) {
        container.finish(shouldFinishDependent, this, wct, mController);

        final TaskFragmentContainer newTopContainer = mController.getTopActiveContainer(
                container.getTaskId());
        if (newTopContainer != null) {
            mController.updateContainer(wct, newTopContainer);
        }
        // Make sure the containers in the Task is up-to-date.
        mController.updateContainersInTaskIfVisible(wct, container.getTaskId());
    }

    /**
+5 −0
Original line number Diff line number Diff line
@@ -184,6 +184,11 @@ class TaskFragmentContainer {
        return allActivities;
    }

    /** Whether this TaskFragment is visible. */
    boolean isVisible() {
        return mInfo != null && mInfo.isVisible();
    }

    /** Whether the TaskFragment is in an intermediate state waiting for the server update.*/
    boolean isInIntermediateState() {
        if (mInfo == null) {
+7 −1
Original line number Diff line number Diff line
@@ -163,11 +163,17 @@ public class EmbeddingTestUtils {
    /** Creates a mock TaskFragmentInfo for the given TaskFragment. */
    static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
            @NonNull Activity activity) {
        return createMockTaskFragmentInfo(container, activity, true /* isVisible */);
    }

    /** Creates a mock TaskFragmentInfo for the given TaskFragment. */
    static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
            @NonNull Activity activity, boolean isVisible) {
        return new TaskFragmentInfo(container.getTaskFragmentToken(),
                mock(WindowContainerToken.class),
                new Configuration(),
                1,
                true /* isVisible */,
                isVisible,
                Collections.singletonList(activity.getActivityToken()),
                new Point(),
                false /* isTaskClearedForReuse */,
+62 −0
Original line number Diff line number Diff line
@@ -1254,6 +1254,68 @@ public class SplitControllerTest {
        verify(mEmbeddingCallback).accept(any());
    }

    @Test
    public void testLaunchPlaceholderIfNecessary_nonEmbeddedActivity() {
        // Launch placeholder for non embedded activity.
        setupPlaceholderRule(mActivity);
        mTransactionManager.startNewTransaction();
        mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity,
                true /* isOnCreated */);

        verify(mTransaction).startActivityInTaskFragment(any(), any(), eq(PLACEHOLDER_INTENT),
                any());
    }

    @Test
    public void testLaunchPlaceholderIfNecessary_embeddedInTopTaskFragment() {
        // Launch placeholder for activity in top TaskFragment.
        setupPlaceholderRule(mActivity);
        mTransactionManager.startNewTransaction();
        final TaskFragmentContainer container = mSplitController.newContainer(mActivity, TASK_ID);
        mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity,
                true /* isOnCreated */);

        assertTrue(container.hasActivity(mActivity.getActivityToken()));
        verify(mTransaction).startActivityInTaskFragment(any(), any(), eq(PLACEHOLDER_INTENT),
                any());
    }

    @Test
    public void testLaunchPlaceholderIfNecessary_embeddedBelowTaskFragment() {
        // Do not launch placeholder for invisible activity below the top TaskFragment.
        setupPlaceholderRule(mActivity);
        mTransactionManager.startNewTransaction();
        final TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID);
        final TaskFragmentContainer topTf = mSplitController.newContainer(new Intent(), mActivity,
                TASK_ID);
        bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity,
                false /* isVisible */));
        topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity()));
        assertFalse(bottomTf.isVisible());
        mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity,
                true /* isOnCreated */);

        verify(mTransaction, never()).startActivityInTaskFragment(any(), any(), any(), any());
    }

    @Test
    public void testLaunchPlaceholderIfNecessary_embeddedBelowTransparentTaskFragment() {
        // Launch placeholder for visible activity below the top TaskFragment.
        setupPlaceholderRule(mActivity);
        mTransactionManager.startNewTransaction();
        final TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID);
        final TaskFragmentContainer topTf = mSplitController.newContainer(new Intent(), mActivity,
                TASK_ID);
        bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity,
                true /* isVisible */));
        topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity()));
        assertTrue(bottomTf.isVisible());
        mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity,
                true /* isOnCreated */);

        verify(mTransaction).startActivityInTaskFragment(any(), any(), any(), any());
    }

    /** Creates a mock activity in the organizer process. */
    private Activity createMockActivity() {
        return createMockActivity(TASK_ID);
Loading