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

Commit 23c0f4b9 authored by Chris Li's avatar Chris Li Committed by Automerger Merge Worker
Browse files

Merge "Don't bring the Task to front when launching placeholder in background"...

Merge "Don't bring the Task to front when launching placeholder in background" into tm-dev am: 721a2e8f

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18451946



Change-Id: If58cf081d42fcacaa354f7e44eef5251703157f7
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents ff52fa4d 721a2e8f
Loading
Loading
Loading
Loading
+43 −23
Original line number Diff line number Diff line
@@ -241,7 +241,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            // launching to top. We allow split as primary for activity reparent because the
            // activity may be split as primary before it is reparented out. In that case, we want
            // to show it as primary again when it is reparented back.
            if (!resolveActivityToContainer(activity, true /* canSplitAsPrimary */)) {
            if (!resolveActivityToContainer(activity, true /* isOnReparent */)) {
                // When there is no embedding rule matched, try to place it in the top container
                // like a normal launch.
                placeActivityInTopContainer(activity);
@@ -353,21 +353,21 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    void onActivityCreated(@NonNull Activity launchedActivity) {
        // TODO(b/229680885): we don't support launching into primary yet because we want to always
        // launch the new activity on top.
        resolveActivityToContainer(launchedActivity, false /* canSplitAsPrimary */);
        resolveActivityToContainer(launchedActivity, false /* isOnReparent */);
        updateCallbackIfNecessary();
    }

    /**
     * Checks if the new added activity should be routed to a particular container. It can create a
     * new container for the activity and a new split container if necessary.
     * @param launchedActivity  the new launched activity.
     * @param canSplitAsPrimary whether we can put the new launched activity into primary split.
     * @param activity      the activity that is newly added to the Task.
     * @param isOnReparent  whether the activity is reparented to the Task instead of new launched.
     *                      We only support to split as primary for reparented activity for now.
     * @return {@code true} if the activity was placed in TaskFragment container.
     */
    @VisibleForTesting
    boolean resolveActivityToContainer(@NonNull Activity launchedActivity,
            boolean canSplitAsPrimary) {
        if (isInPictureInPicture(launchedActivity) || launchedActivity.isFinishing()) {
    boolean resolveActivityToContainer(@NonNull Activity activity, boolean isOnReparent) {
        if (isInPictureInPicture(activity) || activity.isFinishing()) {
            // We don't embed activity when it is in PIP, or finishing. Return true since we don't
            // want any extra handling.
            return true;
@@ -385,33 +385,32 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
         */

        // 1. Whether the new launched activity should always expand.
        if (shouldExpand(launchedActivity, null /* intent */)) {
            expandActivity(launchedActivity);
        if (shouldExpand(activity, null /* intent */)) {
            expandActivity(activity);
            return true;
        }

        // 2. Whether the new launched activity should launch a placeholder.
        if (launchPlaceholderIfNecessary(launchedActivity)) {
        if (launchPlaceholderIfNecessary(activity, !isOnReparent)) {
            return true;
        }

        // 3. Whether the new launched activity has already been in a split with a rule matched.
        if (isNewActivityInSplitWithRuleMatched(launchedActivity)) {
        if (isNewActivityInSplitWithRuleMatched(activity)) {
            return true;
        }

        // 4. Whether the activity below (if any) should be split with the new launched activity.
        final Activity activityBelow = findActivityBelow(launchedActivity);
        final Activity activityBelow = findActivityBelow(activity);
        if (activityBelow == null) {
            // Can't find any activity below.
            return false;
        }
        if (putActivitiesIntoSplitIfNecessary(activityBelow, launchedActivity)) {
        if (putActivitiesIntoSplitIfNecessary(activityBelow, activity)) {
            // Have split rule of [ activityBelow | launchedActivity ].
            return true;
        }
        if (canSplitAsPrimary
                && putActivitiesIntoSplitIfNecessary(launchedActivity, activityBelow)) {
        if (isOnReparent && putActivitiesIntoSplitIfNecessary(activity, activityBelow)) {
            // Have split rule of [ launchedActivity | activityBelow].
            return true;
        }
@@ -430,17 +429,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                        ? topSplit.getSecondaryContainer()
                        : topSplit.getPrimaryContainer();
        final Activity otherTopActivity = otherTopContainer.getTopNonFinishingActivity();
        if (otherTopActivity == null || otherTopActivity == launchedActivity) {
        if (otherTopActivity == null || otherTopActivity == activity) {
            // Can't find the top activity on the other split TaskFragment.
            return false;
        }
        if (putActivitiesIntoSplitIfNecessary(otherTopActivity, launchedActivity)) {
        if (putActivitiesIntoSplitIfNecessary(otherTopActivity, activity)) {
            // Have split rule of [ otherTopActivity | launchedActivity ].
            return true;
        }
        // Have split rule of [ launchedActivity | otherTopActivity].
        return canSplitAsPrimary
                && putActivitiesIntoSplitIfNecessary(launchedActivity, otherTopActivity);
        return isOnReparent && putActivitiesIntoSplitIfNecessary(activity, otherTopActivity);
    }

    /**
@@ -603,7 +601,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }

        // Check if activity requires a placeholder
        launchPlaceholderIfNecessary(activity);
        launchPlaceholderIfNecessary(activity, false /* isOnCreated */);
    }

    @VisibleForTesting
@@ -1043,10 +1041,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            return false;
        }

        return launchPlaceholderIfNecessary(topActivity);
        return launchPlaceholderIfNecessary(topActivity, false /* isOnCreated */);
    }

    boolean launchPlaceholderIfNecessary(@NonNull Activity activity) {
    boolean launchPlaceholderIfNecessary(@NonNull Activity activity, boolean isOnCreated) {
        final TaskFragmentContainer container = getContainerWithActivity(activity);
        // Don't launch placeholder if the container is occluded.
        if (container != null && container != getTopActiveContainer(container.getTaskId())) {
@@ -1067,11 +1065,33 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }

        // TODO(b/190433398): Handle failed request
        startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), null /* options */,
        final Bundle options = getPlaceholderOptions(activity, isOnCreated);
        startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), options,
                placeholderRule, null /* failureCallback */, true /* isPlaceholder */);
        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.
     * @param primaryActivity   the primary activity to launch the placeholder from.
     * @param isOnCreated       whether this happens during the primary activity onCreated.
     */
    @VisibleForTesting
    @Nullable
    Bundle getPlaceholderOptions(@NonNull Activity primaryActivity, boolean isOnCreated) {
        // Setting avoid move to front will also skip the animation. We only want to do that when
        // the Task is currently in background.
        // Check if the primary is resumed or if this is called when the primary is onCreated
        // (not resumed yet).
        if (isOnCreated || primaryActivity.isResumed()) {
            return null;
        }
        final ActivityOptions options = ActivityOptions.makeBasic();
        options.setAvoidMoveToFront();
        return options.toBundle();
    }

    @VisibleForTesting
    boolean dismissPlaceholderIfNecessary(@NonNull SplitContainer splitContainer) {
        if (!splitContainer.isPlaceholderContainer()) {
+51 −29
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.never;

import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Intent;
import android.content.res.Configuration;
@@ -50,6 +51,7 @@ import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
@@ -219,7 +221,8 @@ public class SplitControllerTest {
        spyOn(tf);
        doReturn(mActivity).when(tf).getTopNonFinishingActivity();
        doReturn(true).when(tf).isEmpty();
        doReturn(true).when(mSplitController).launchPlaceholderIfNecessary(mActivity);
        doReturn(true).when(mSplitController).launchPlaceholderIfNecessary(mActivity,
                false /* isOnCreated */);
        doNothing().when(mSplitPresenter).updateSplitContainer(any(), any(), any());

        mSplitController.updateContainer(mTransaction, tf);
@@ -287,8 +290,7 @@ public class SplitControllerTest {
        mSplitController.onActivityCreated(mActivity);

        // Disallow to split as primary because we want the new launch to be always on top.
        verify(mSplitController).resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
        verify(mSplitController).resolveActivityToContainer(mActivity, false /* isOnReparent */);
    }

    @Test
@@ -297,8 +299,7 @@ public class SplitControllerTest {
                mActivity.getActivityToken());

        // Treated as on activity created, but allow to split as primary.
        verify(mSplitController).resolveActivityToContainer(mActivity,
                true /* canSplitAsPrimary */);
        verify(mSplitController).resolveActivityToContainer(mActivity, true /* isOnReparent */);
        // Try to place the activity to the top TaskFragment when there is no matched rule.
        verify(mSplitController).placeActivityInTopContainer(mActivity);
    }
@@ -432,7 +433,7 @@ public class SplitControllerTest {
    @Test
    public void testResolveActivityToContainer_noRuleMatched() {
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertFalse(result);
        verify(mSplitController, never()).newContainer(any(), any(), anyInt());
@@ -444,7 +445,7 @@ public class SplitControllerTest {

        // When the activity is not in any TaskFragment, create a new expanded TaskFragment for it.
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);
        final TaskFragmentContainer container = mSplitController.getContainerWithActivity(
                mActivity);

@@ -461,7 +462,7 @@ public class SplitControllerTest {
        // When the activity is not in any TaskFragment, create a new expanded TaskFragment for it.
        final TaskFragmentContainer container = mSplitController.newContainer(mActivity, TASK_ID);
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertTrue(result);
        verify(mSplitPresenter).expandTaskFragment(container.getTaskFragmentToken());
@@ -475,7 +476,7 @@ public class SplitControllerTest {
        final Activity activity = createMockActivity();
        addSplitTaskFragments(activity, mActivity);
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);
        final TaskFragmentContainer container = mSplitController.getContainerWithActivity(
                mActivity);

@@ -492,11 +493,12 @@ public class SplitControllerTest {

        // Launch placeholder if the activity is not in any TaskFragment.
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertTrue(result);
        verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT,
                null /* activityOptions */, placeholderRule, true /* isPlaceholder */);
                mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */),
                placeholderRule, true /* isPlaceholder */);
    }

    @Test
@@ -508,7 +510,7 @@ public class SplitControllerTest {
        mSplitController.newContainer(mActivity, TASK_ID);
        mSplitController.newContainer(activity, TASK_ID);
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertFalse(result);
        verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(),
@@ -524,11 +526,12 @@ public class SplitControllerTest {
        // Launch placeholder if the activity is in the topmost expanded TaskFragment.
        mSplitController.newContainer(mActivity, TASK_ID);
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertTrue(result);
        verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT,
                null /* activityOptions */, placeholderRule, true /* isPlaceholder */);
                mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */),
                placeholderRule, true /* isPlaceholder */);
    }

    @Test
@@ -539,7 +542,7 @@ public class SplitControllerTest {
        final Activity secondaryActivity = createMockActivity();
        addSplitTaskFragments(mActivity, secondaryActivity);
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertFalse(result);
        verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(),
@@ -556,11 +559,12 @@ public class SplitControllerTest {
        final Activity primaryActivity = createMockActivity();
        addSplitTaskFragments(primaryActivity, mActivity);
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertTrue(result);
        verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT,
                null /* activityOptions */, placeholderRule, true /* isPlaceholder */);
                mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */),
                placeholderRule, true /* isPlaceholder */);
    }

    @Test
@@ -582,7 +586,7 @@ public class SplitControllerTest {
                splitRule);
        clearInvocations(mSplitController);
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertTrue(result);
        verify(mSplitController, never()).newContainer(any(), any(), anyInt());
@@ -598,7 +602,7 @@ public class SplitControllerTest {
        addSplitTaskFragments(primaryActivity, mActivity);
        clearInvocations(mSplitController);
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertTrue(result);
        verify(mSplitController, never()).newContainer(any(), any(), anyInt());
@@ -616,7 +620,7 @@ public class SplitControllerTest {
        mSplitController.getContainerWithActivity(secondaryActivity)
                .addPendingAppearedActivity(mActivity);
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertFalse(result);
    }
@@ -641,7 +645,7 @@ public class SplitControllerTest {
                secondaryContainer,
                placeholderRule);
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertTrue(result);
    }
@@ -655,7 +659,7 @@ public class SplitControllerTest {
                TASK_ID);
        container.addPendingAppearedActivity(mActivity);
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertTrue(result);
        assertSplitPair(activityBelow, mActivity);
@@ -671,14 +675,13 @@ public class SplitControllerTest {
                TASK_ID);
        container.addPendingAppearedActivity(mActivity);
        boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertFalse(result);
        assertEquals(container, mSplitController.getContainerWithActivity(mActivity));

        // Allow to split as primary.
        result = mSplitController.resolveActivityToContainer(mActivity,
                true /* canSplitAsPrimary */);
        result = mSplitController.resolveActivityToContainer(mActivity, true /* isOnReparent */);

        assertTrue(result);
        assertSplitPair(mActivity, activityBelow);
@@ -697,7 +700,7 @@ public class SplitControllerTest {
                activityBelow);
        secondaryContainer.addPendingAppearedActivity(mActivity);
        final boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);
        final TaskFragmentContainer container = mSplitController.getContainerWithActivity(
                mActivity);

@@ -718,19 +721,38 @@ public class SplitControllerTest {
                primaryActivity);
        primaryContainer.addPendingAppearedActivity(mActivity);
        boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* canSplitAsPrimary */);
                false /* isOnReparent */);

        assertFalse(result);
        assertEquals(primaryContainer, mSplitController.getContainerWithActivity(mActivity));


        result = mSplitController.resolveActivityToContainer(mActivity,
                true /* canSplitAsPrimary */);
        result = mSplitController.resolveActivityToContainer(mActivity, true /* isOnReparent */);

        assertTrue(result);
        assertSplitPair(mActivity, primaryActivity);
    }

    @Test
    public void testGetPlaceholderOptions() {
        doReturn(true).when(mActivity).isResumed();

        assertNull(mSplitController.getPlaceholderOptions(mActivity, false /* isOnCreated */));

        doReturn(false).when(mActivity).isResumed();

        assertNull(mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */));

        // Launch placeholder without moving the Task to front if the Task is now in background (not
        // resumed or onCreated).
        final Bundle options = mSplitController.getPlaceholderOptions(mActivity,
                false /* isOnCreated */);

        assertNotNull(options);
        final ActivityOptions activityOptions = new ActivityOptions(options);
        assertTrue(activityOptions.getAvoidMoveToFront());
    }

    /** Creates a mock activity in the organizer process. */
    private Activity createMockActivity() {
        final Activity activity = mock(Activity.class);