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

Commit 8bf77af1 authored by Charles Chen's avatar Charles Chen
Browse files

Fix exception in expandSplitContainerIfNeeded

The exception is because the client side hasn't received
TaskFragmentInfo yet, so we don't have WindowContainerToken
to change the bounds of TaskFragments.
This CL verifies if we have TaskFragmentInfo before
calling expandTaskFragment. If there's no TaskFragmentInfo,
fallback to create new SplitContainer that fills the task
bounds.

Test: atest WMJetpackUnitTests
Test: manual: reproduce steps mentioned in bug
Bug: 232871351

Merged-In: Id8d122f7d95d1c6a3574b02ddd2b6afbc548f853
Change-Id: I83ee6ea32df485bf78db3b50dec8c92d80be8912
parent 909495e9
Loading
Loading
Loading
Loading
+18 −17
Original line number Original line Diff line number Diff line
@@ -24,9 +24,9 @@ import static androidx.window.extensions.embedding.SplitContainer.getFinishSecon
import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenAdjacent;
import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenAdjacent;
import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked;
import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked;
import static androidx.window.extensions.embedding.SplitPresenter.boundsSmallerThanMinDimensions;
import static androidx.window.extensions.embedding.SplitPresenter.RESULT_EXPAND_FAILED_NO_TF_INFO;
import static androidx.window.extensions.embedding.SplitPresenter.getActivityIntentMinDimensionsPair;
import static androidx.window.extensions.embedding.SplitPresenter.getActivityIntentMinDimensionsPair;
import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions;
import static androidx.window.extensions.embedding.SplitPresenter.getNonEmbeddedActivityBounds;
import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSideBySide;
import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSideBySide;


import android.app.Activity;
import android.app.Activity;
@@ -581,8 +581,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    }
    }


    /** Finds the activity below the given activity. */
    /** Finds the activity below the given activity. */
    @VisibleForTesting
    @Nullable
    @Nullable
    private Activity findActivityBelow(@NonNull Activity activity) {
    Activity findActivityBelow(@NonNull Activity activity) {
        Activity activityBelow = null;
        Activity activityBelow = null;
        final TaskFragmentContainer container = getContainerWithActivity(activity);
        final TaskFragmentContainer container = getContainerWithActivity(activity);
        if (container != null) {
        if (container != null) {
@@ -620,22 +621,22 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            // Can launch in the existing secondary container if the rules share the same
            // Can launch in the existing secondary container if the rules share the same
            // presentation.
            // presentation.
            final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
            final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
            if (secondaryContainer == getContainerWithActivity(secondaryActivity)
            if (secondaryContainer == getContainerWithActivity(secondaryActivity)) {
                    && !boundsSmallerThanMinDimensions(secondaryContainer.getLastRequestedBounds(),
                            getMinDimensions(secondaryActivity))) {
                // The activity is already in the target TaskFragment.
                // The activity is already in the target TaskFragment.
                return true;
                return true;
            }
            }
            secondaryContainer.addPendingAppearedActivity(secondaryActivity);
            secondaryContainer.addPendingAppearedActivity(secondaryActivity);
            final WindowContainerTransaction wct = new WindowContainerTransaction();
            final WindowContainerTransaction wct = new WindowContainerTransaction();
            mPresenter.expandSplitContainerIfNeeded(wct, splitContainer, primaryActivity,
            if (mPresenter.expandSplitContainerIfNeeded(wct, splitContainer, primaryActivity,
                    secondaryActivity, null /* secondaryIntent */);
                    secondaryActivity, null /* secondaryIntent */)
                    != RESULT_EXPAND_FAILED_NO_TF_INFO) {
                wct.reparentActivityToTaskFragment(
                wct.reparentActivityToTaskFragment(
                        secondaryContainer.getTaskFragmentToken(),
                        secondaryContainer.getTaskFragmentToken(),
                        secondaryActivity.getActivityToken());
                        secondaryActivity.getActivityToken());
                mPresenter.applyTransaction(wct);
                mPresenter.applyTransaction(wct);
                return true;
                return true;
            }
            }
        }
        // Create new split pair.
        // Create new split pair.
        mPresenter.createNewSplitContainer(primaryActivity, secondaryActivity, splitRule);
        mPresenter.createNewSplitContainer(primaryActivity, secondaryActivity, splitRule);
        return true;
        return true;
@@ -800,9 +801,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        if (splitContainer != null && existingContainer == splitContainer.getPrimaryContainer()
        if (splitContainer != null && existingContainer == splitContainer.getPrimaryContainer()
                && (canReuseContainer(splitRule, splitContainer.getSplitRule())
                && (canReuseContainer(splitRule, splitContainer.getSplitRule())
                // TODO(b/231845476) we should always respect clearTop.
                // TODO(b/231845476) we should always respect clearTop.
                || !respectClearTop)) {
                || !respectClearTop)
            mPresenter.expandSplitContainerIfNeeded(wct, splitContainer, primaryActivity,
                && mPresenter.expandSplitContainerIfNeeded(wct, splitContainer, primaryActivity,
                    null /* secondaryActivity */, intent);
                        null /* secondaryActivity */, intent) != RESULT_EXPAND_FAILED_NO_TF_INFO) {
            // Can launch in the existing secondary container if the rules share the same
            // Can launch in the existing secondary container if the rules share the same
            // presentation.
            // presentation.
            return splitContainer.getSecondaryContainer();
            return splitContainer.getSecondaryContainer();
@@ -872,7 +873,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                pendingAppearedIntent, taskContainer, this);
                pendingAppearedIntent, taskContainer, this);
        if (!taskContainer.isTaskBoundsInitialized()) {
        if (!taskContainer.isTaskBoundsInitialized()) {
            // Get the initial bounds before the TaskFragment has appeared.
            // Get the initial bounds before the TaskFragment has appeared.
            final Rect taskBounds = SplitPresenter.getTaskBoundsFromActivity(activityInTask);
            final Rect taskBounds = getNonEmbeddedActivityBounds(activityInTask);
            if (!taskContainer.setTaskBounds(taskBounds)) {
            if (!taskContainer.setTaskBounds(taskBounds)) {
                Log.w(TAG, "Can't find bounds from activity=" + activityInTask);
                Log.w(TAG, "Can't find bounds from activity=" + activityInTask);
            }
            }
+61 −8
Original line number Original line Diff line number Diff line
@@ -65,6 +65,41 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
    })
    })
    private @interface Position {}
    private @interface Position {}


    /**
     * Result of {@link #expandSplitContainerIfNeeded(WindowContainerTransaction, SplitContainer,
     * Activity, Activity, Intent)}.
     * No need to expand the splitContainer because screen is big enough to
     * {@link #shouldShowSideBySide(Rect, SplitRule, Pair)} and minimum dimensions is satisfied.
     */
    static final int RESULT_NOT_EXPANDED = 0;
    /**
     * Result of {@link #expandSplitContainerIfNeeded(WindowContainerTransaction, SplitContainer,
     * Activity, Activity, Intent)}.
     * The splitContainer should be expanded. It is usually because minimum dimensions is not
     * satisfied.
     * @see #shouldShowSideBySide(Rect, SplitRule, Pair)
     */
    static final int RESULT_EXPANDED = 1;
    /**
     * Result of {@link #expandSplitContainerIfNeeded(WindowContainerTransaction, SplitContainer,
     * Activity, Activity, Intent)}.
     * The splitContainer should be expanded, but the client side hasn't received
     * {@link android.window.TaskFragmentInfo} yet. Fallback to create new expanded SplitContainer
     * instead.
     */
    static final int RESULT_EXPAND_FAILED_NO_TF_INFO = 2;

    /**
     * Result of {@link #expandSplitContainerIfNeeded(WindowContainerTransaction, SplitContainer,
     * Activity, Activity, Intent)}
     */
    @IntDef(value = {
            RESULT_NOT_EXPANDED,
            RESULT_EXPANDED,
            RESULT_EXPAND_FAILED_NO_TF_INFO,
    })
    private @interface ResultCode {}

    private final SplitController mController;
    private final SplitController mController;


    SplitPresenter(@NonNull Executor executor, SplitController controller) {
    SplitPresenter(@NonNull Executor executor, SplitController controller) {
@@ -399,15 +434,19 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
    /**
    /**
     * Expands the split container if the current split bounds are smaller than the Activity or
     * Expands the split container if the current split bounds are smaller than the Activity or
     * Intent that is added to the container.
     * Intent that is added to the container.
     *
     * @return the {@link ResultCode} based on {@link #shouldShowSideBySide(Rect, SplitRule, Pair)}
     * and if {@link android.window.TaskFragmentInfo} has reported to the client side.
     */
     */
    void expandSplitContainerIfNeeded(@NonNull WindowContainerTransaction wct,
    @ResultCode
    int expandSplitContainerIfNeeded(@NonNull WindowContainerTransaction wct,
            @NonNull SplitContainer splitContainer, @NonNull Activity primaryActivity,
            @NonNull SplitContainer splitContainer, @NonNull Activity primaryActivity,
            @Nullable Activity secondaryActivity, @Nullable Intent secondaryIntent) {
            @Nullable Activity secondaryActivity, @Nullable Intent secondaryIntent) {
        if (secondaryActivity == null && secondaryIntent == null) {
        if (secondaryActivity == null && secondaryIntent == null) {
            throw new IllegalArgumentException("Either secondaryActivity or secondaryIntent must be"
            throw new IllegalArgumentException("Either secondaryActivity or secondaryIntent must be"
                    + " non-null.");
                    + " non-null.");
        }
        }
        final Rect taskBounds = getTaskBoundsFromActivity(primaryActivity);
        final Rect taskBounds = getParentContainerBounds(primaryActivity);
        final Pair<Size, Size> minDimensionsPair;
        final Pair<Size, Size> minDimensionsPair;
        if (secondaryActivity != null) {
        if (secondaryActivity != null) {
            minDimensionsPair = getActivitiesMinDimensionsPair(primaryActivity, secondaryActivity);
            minDimensionsPair = getActivitiesMinDimensionsPair(primaryActivity, secondaryActivity);
@@ -417,11 +456,17 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        }
        }
        // Expand the splitContainer if minimum dimensions are not satisfied.
        // Expand the splitContainer if minimum dimensions are not satisfied.
        if (!shouldShowSideBySide(taskBounds, splitContainer.getSplitRule(), minDimensionsPair)) {
        if (!shouldShowSideBySide(taskBounds, splitContainer.getSplitRule(), minDimensionsPair)) {
            expandTaskFragment(wct, splitContainer.getPrimaryContainer()
            // If the client side hasn't received TaskFragmentInfo yet, we can't change TaskFragment
                    .getTaskFragmentToken());
            // bounds. Return failure to create a new SplitContainer which fills task bounds.
            expandTaskFragment(wct, splitContainer.getSecondaryContainer()
            if (splitContainer.getPrimaryContainer().getInfo() == null
                    .getTaskFragmentToken());
                    || splitContainer.getSecondaryContainer().getInfo() == null) {
                return RESULT_EXPAND_FAILED_NO_TF_INFO;
            }
            expandTaskFragment(wct, splitContainer.getPrimaryContainer().getTaskFragmentToken());
            expandTaskFragment(wct, splitContainer.getSecondaryContainer().getTaskFragmentToken());
            return RESULT_EXPANDED;
        }
        }
        return RESULT_NOT_EXPANDED;
    }
    }


    static boolean shouldShowSideBySide(@NonNull Rect parentBounds, @NonNull SplitRule rule) {
    static boolean shouldShowSideBySide(@NonNull Rect parentBounds, @NonNull SplitRule rule) {
@@ -593,11 +638,19 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        if (container != null) {
        if (container != null) {
            return getParentContainerBounds(container);
            return getParentContainerBounds(container);
        }
        }
        return getTaskBoundsFromActivity(activity);
        // Obtain bounds from Activity instead because the Activity hasn't been embedded yet.
        return getNonEmbeddedActivityBounds(activity);
    }
    }


    /**
     * Obtains the bounds from a non-embedded Activity.
     * <p>
     * Note that callers should use {@link #getParentContainerBounds(Activity)} instead for most
     * cases unless we want to obtain task bounds before
     * {@link TaskContainer#isTaskBoundsInitialized()}.
     */
    @NonNull
    @NonNull
    static Rect getTaskBoundsFromActivity(@NonNull Activity activity) {
    static Rect getNonEmbeddedActivityBounds(@NonNull Activity activity) {
        final WindowConfiguration windowConfiguration =
        final WindowConfiguration windowConfiguration =
                activity.getResources().getConfiguration().windowConfiguration;
                activity.getResources().getConfiguration().windowConfiguration;
        if (!activity.isInMultiWindowMode()) {
        if (!activity.isInMultiWindowMode()) {
+17 −1
Original line number Original line Diff line number Diff line
@@ -58,13 +58,21 @@ public class EmbeddingTestUtils {
    /** Creates a rule to always split the given activity and the given intent. */
    /** Creates a rule to always split the given activity and the given intent. */
    static SplitRule createSplitRule(@NonNull Activity primaryActivity,
    static SplitRule createSplitRule(@NonNull Activity primaryActivity,
            @NonNull Intent secondaryIntent) {
            @NonNull Intent secondaryIntent) {
        return createSplitRule(primaryActivity, secondaryIntent, true /* clearTop */);
    }

    /** Creates a rule to always split the given activity and the given intent. */
    static SplitRule createSplitRule(@NonNull Activity primaryActivity,
            @NonNull Intent secondaryIntent, boolean clearTop) {
        final Pair<Activity, Intent> targetPair = new Pair<>(primaryActivity, secondaryIntent);
        final Pair<Activity, Intent> targetPair = new Pair<>(primaryActivity, secondaryIntent);
        return new SplitPairRule.Builder(
        return new SplitPairRule.Builder(
                activityPair -> false,
                activityPair -> false,
                targetPair::equals,
                targetPair::equals,
                w -> true)
                w -> true)
                .setSplitRatio(SPLIT_RATIO)
                .setSplitRatio(SPLIT_RATIO)
                .setShouldClearTop(true)
                .setShouldClearTop(clearTop)
                .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY)
                .setFinishSecondaryWithPrimary(DEFAULT_FINISH_SECONDARY_WITH_PRIMARY)
                .build();
                .build();
    }
    }


@@ -76,6 +84,14 @@ public class EmbeddingTestUtils {
                true /* clearTop */);
                true /* clearTop */);
    }
    }


    /** Creates a rule to always split the given activities. */
    static SplitRule createSplitRule(@NonNull Activity primaryActivity,
            @NonNull Activity secondaryActivity, boolean clearTop) {
        return createSplitRule(primaryActivity, secondaryActivity,
                DEFAULT_FINISH_PRIMARY_WITH_SECONDARY, DEFAULT_FINISH_SECONDARY_WITH_PRIMARY,
                clearTop);
    }

    /** Creates a rule to always split the given activities with the given finish behaviors. */
    /** Creates a rule to always split the given activities with the given finish behaviors. */
    static SplitRule createSplitRule(@NonNull Activity primaryActivity,
    static SplitRule createSplitRule(@NonNull Activity primaryActivity,
            @NonNull Activity secondaryActivity, int finishPrimaryWithSecondary,
            @NonNull Activity secondaryActivity, int finishPrimaryWithSecondary,
+94 −13
Original line number Original line Diff line number Diff line
@@ -35,6 +35,7 @@ import static com.google.common.truth.Truth.assertWithMessage;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertThrows;
@@ -436,6 +437,50 @@ public class SplitControllerTest {
        assertTrue(container.areLastRequestedBoundsEqual(null));
        assertTrue(container.areLastRequestedBoundsEqual(null));
    }
    }


    @Test
    public void testResolveStartActivityIntent_shouldExpandSplitContainer() {
        final Intent intent = new Intent().setComponent(
                new ComponentName(ApplicationProvider.getApplicationContext(),
                        MinimumDimensionActivity.class));
        setupSplitRule(mActivity, intent, false /* clearTop */);
        final Activity secondaryActivity = createMockActivity();
        addSplitTaskFragments(mActivity, secondaryActivity, false /* clearTop */);

        final TaskFragmentContainer container = mSplitController.resolveStartActivityIntent(
                mTransaction, TASK_ID, intent, mActivity);
        final TaskFragmentContainer primaryContainer = mSplitController.getContainerWithActivity(
                mActivity);

        assertNotNull(mSplitController.getActiveSplitForContainers(primaryContainer, container));
        assertTrue(primaryContainer.areLastRequestedBoundsEqual(null));
        assertTrue(container.areLastRequestedBoundsEqual(null));
        assertEquals(container, mSplitController.getContainerWithActivity(secondaryActivity));
    }

    @Test
    public void testResolveStartActivityIntent_noInfo_shouldCreateSplitContainer() {
        final Intent intent = new Intent().setComponent(
                new ComponentName(ApplicationProvider.getApplicationContext(),
                        MinimumDimensionActivity.class));
        setupSplitRule(mActivity, intent, false /* clearTop */);
        final Activity secondaryActivity = createMockActivity();
        addSplitTaskFragments(mActivity, secondaryActivity, false /* clearTop */);

        final TaskFragmentContainer secondaryContainer = mSplitController
                .getContainerWithActivity(secondaryActivity);
        secondaryContainer.mInfo = null;

        final TaskFragmentContainer container = mSplitController.resolveStartActivityIntent(
                mTransaction, TASK_ID, intent, mActivity);
        final TaskFragmentContainer primaryContainer = mSplitController.getContainerWithActivity(
                mActivity);

        assertNotNull(mSplitController.getActiveSplitForContainers(primaryContainer, container));
        assertTrue(primaryContainer.areLastRequestedBoundsEqual(null));
        assertTrue(container.areLastRequestedBoundsEqual(null));
        assertNotEquals(container, secondaryContainer);
    }

    @Test
    @Test
    public void testPlaceActivityInTopContainer() {
    public void testPlaceActivityInTopContainer() {
        mSplitController.placeActivityInTopContainer(mActivity);
        mSplitController.placeActivityInTopContainer(mActivity);
@@ -807,17 +852,12 @@ public class SplitControllerTest {
        final Activity activityBelow = createMockActivity();
        final Activity activityBelow = createMockActivity();
        setupSplitRule(activityBelow, mActivity);
        setupSplitRule(activityBelow, mActivity);


        ActivityInfo aInfo = new ActivityInfo();
        doReturn(createActivityInfoWithMinDimensions()).when(mActivity).getActivityInfo();
        final Rect secondaryBounds = getSplitBounds(false /* isPrimary */);
        aInfo.windowLayout = new ActivityInfo.WindowLayout(0, 0, 0, 0, 0,
                secondaryBounds.width() + 1, secondaryBounds.height() + 1);
        doReturn(aInfo).when(mActivity).getActivityInfo();


        final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
        final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
                TASK_ID);
                TASK_ID);
        container.addPendingAppearedActivity(mActivity);
        container.addPendingAppearedActivity(mActivity);


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


@@ -825,6 +865,27 @@ public class SplitControllerTest {
        assertSplitPair(activityBelow, mActivity, true /* matchParentBounds */);
        assertSplitPair(activityBelow, mActivity, true /* matchParentBounds */);
    }
    }


    @Test
    public void testResolveActivityToContainer_minDimensions_shouldExpandSplitContainer() {
        final Activity primaryActivity = createMockActivity();
        final Activity secondaryActivity = createMockActivity();
        addSplitTaskFragments(primaryActivity, secondaryActivity, false /* clearTop */);

        setupSplitRule(primaryActivity, mActivity, false /* clearTop */);
        doReturn(createActivityInfoWithMinDimensions()).when(mActivity).getActivityInfo();
        doReturn(secondaryActivity).when(mSplitController).findActivityBelow(eq(mActivity));

        clearInvocations(mSplitPresenter);
        boolean result = mSplitController.resolveActivityToContainer(mActivity,
                false /* isOnReparent */);

        assertTrue(result);
        assertSplitPair(primaryActivity, mActivity, true /* matchParentBounds */);
        assertEquals(mSplitController.getContainerWithActivity(secondaryActivity),
                mSplitController.getContainerWithActivity(mActivity));
        verify(mSplitPresenter, never()).createNewSplitContainer(any(), any(), any());
    }

    @Test
    @Test
    public void testResolveActivityToContainer_inUnknownTaskFragment() {
    public void testResolveActivityToContainer_inUnknownTaskFragment() {
        doReturn(new Binder()).when(mSplitController).getInitialTaskFragmentToken(mActivity);
        doReturn(new Binder()).when(mSplitController).getInitialTaskFragmentToken(mActivity);
@@ -941,23 +1002,41 @@ public class SplitControllerTest {
    /** Setups a rule to always split the given activities. */
    /** Setups a rule to always split the given activities. */
    private void setupSplitRule(@NonNull Activity primaryActivity,
    private void setupSplitRule(@NonNull Activity primaryActivity,
            @NonNull Intent secondaryIntent) {
            @NonNull Intent secondaryIntent) {
        final SplitRule splitRule = createSplitRule(primaryActivity, secondaryIntent);
        setupSplitRule(primaryActivity, secondaryIntent, true /* clearTop */);
    }

    /** Setups a rule to always split the given activities. */
    private void setupSplitRule(@NonNull Activity primaryActivity,
            @NonNull Intent secondaryIntent, boolean clearTop) {
        final SplitRule splitRule = createSplitRule(primaryActivity, secondaryIntent, clearTop);
        mSplitController.setEmbeddingRules(Collections.singleton(splitRule));
        mSplitController.setEmbeddingRules(Collections.singleton(splitRule));
    }
    }


    /** Setups a rule to always split the given activities. */
    /** Setups a rule to always split the given activities. */
    private void setupSplitRule(@NonNull Activity primaryActivity,
    private void setupSplitRule(@NonNull Activity primaryActivity,
            @NonNull Activity secondaryActivity) {
            @NonNull Activity secondaryActivity) {
        final SplitRule splitRule = createSplitRule(primaryActivity, secondaryActivity);
        setupSplitRule(primaryActivity, secondaryActivity, true /* clearTop */);
    }

    /** Setups a rule to always split the given activities. */
    private void setupSplitRule(@NonNull Activity primaryActivity,
            @NonNull Activity secondaryActivity, boolean clearTop) {
        final SplitRule splitRule = createSplitRule(primaryActivity, secondaryActivity, clearTop);
        mSplitController.setEmbeddingRules(Collections.singleton(splitRule));
        mSplitController.setEmbeddingRules(Collections.singleton(splitRule));
    }
    }


    /** Adds a pair of TaskFragments as split for the given activities. */
    /** Adds a pair of TaskFragments as split for the given activities. */
    private void addSplitTaskFragments(@NonNull Activity primaryActivity,
    private void addSplitTaskFragments(@NonNull Activity primaryActivity,
            @NonNull Activity secondaryActivity) {
            @NonNull Activity secondaryActivity) {
        addSplitTaskFragments(primaryActivity, secondaryActivity, true /* clearTop */);
    }

    /** Adds a pair of TaskFragments as split for the given activities. */
    private void addSplitTaskFragments(@NonNull Activity primaryActivity,
            @NonNull Activity secondaryActivity, boolean clearTop) {
        registerSplitPair(createMockTaskFragmentContainer(primaryActivity),
        registerSplitPair(createMockTaskFragmentContainer(primaryActivity),
                createMockTaskFragmentContainer(secondaryActivity),
                createMockTaskFragmentContainer(secondaryActivity),
                createSplitRule(primaryActivity, secondaryActivity));
                createSplitRule(primaryActivity, secondaryActivity, clearTop));
    }
    }


    /** Registers the two given TaskFragments as split pair. */
    /** Registers the two given TaskFragments as split pair. */
@@ -1008,16 +1087,18 @@ public class SplitControllerTest {
        if (primaryContainer.mInfo != null) {
        if (primaryContainer.mInfo != null) {
            final Rect primaryBounds = matchParentBounds ? new Rect()
            final Rect primaryBounds = matchParentBounds ? new Rect()
                    : getSplitBounds(true /* isPrimary */);
                    : getSplitBounds(true /* isPrimary */);
            final int windowingMode = matchParentBounds ? WINDOWING_MODE_UNDEFINED
                    : WINDOWING_MODE_MULTI_WINDOW;
            assertTrue(primaryContainer.areLastRequestedBoundsEqual(primaryBounds));
            assertTrue(primaryContainer.areLastRequestedBoundsEqual(primaryBounds));
            assertTrue(primaryContainer.isLastRequestedWindowingModeEqual(
            assertTrue(primaryContainer.isLastRequestedWindowingModeEqual(windowingMode));
                    WINDOWING_MODE_MULTI_WINDOW));
        }
        }
        if (secondaryContainer.mInfo != null) {
        if (secondaryContainer.mInfo != null) {
            final Rect secondaryBounds = matchParentBounds ? new Rect()
            final Rect secondaryBounds = matchParentBounds ? new Rect()
                    : getSplitBounds(false /* isPrimary */);
                    : getSplitBounds(false /* isPrimary */);
            final int windowingMode = matchParentBounds ? WINDOWING_MODE_UNDEFINED
                    : WINDOWING_MODE_MULTI_WINDOW;
            assertTrue(secondaryContainer.areLastRequestedBoundsEqual(secondaryBounds));
            assertTrue(secondaryContainer.areLastRequestedBoundsEqual(secondaryBounds));
            assertTrue(secondaryContainer.isLastRequestedWindowingModeEqual(
            assertTrue(secondaryContainer.isLastRequestedWindowingModeEqual(windowingMode));
                    WINDOWING_MODE_MULTI_WINDOW));
        }
        }
    }
    }
}
}
+19 −8
Original line number Original line Diff line number Diff line
@@ -21,11 +21,15 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_END;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_END;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_FILL;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_FILL;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_START;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_START;
import static androidx.window.extensions.embedding.SplitPresenter.RESULT_EXPANDED;
import static androidx.window.extensions.embedding.SplitPresenter.RESULT_EXPAND_FAILED_NO_TF_INFO;
import static androidx.window.extensions.embedding.SplitPresenter.RESULT_NOT_EXPANDED;
import static androidx.window.extensions.embedding.SplitPresenter.getBoundsForPosition;
import static androidx.window.extensions.embedding.SplitPresenter.getBoundsForPosition;
import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions;
import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions;
import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSideBySide;
import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSideBySide;
@@ -51,6 +55,7 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
import android.util.Pair;
import android.util.Size;
import android.util.Size;
@@ -212,26 +217,31 @@ public class SplitPresenterTest {
                mPresenter.expandSplitContainerIfNeeded(mTransaction, splitContainer, mActivity,
                mPresenter.expandSplitContainerIfNeeded(mTransaction, splitContainer, mActivity,
                        null /* secondaryActivity */, null /* secondaryIntent */));
                        null /* secondaryActivity */, null /* secondaryIntent */));


        mPresenter.expandSplitContainerIfNeeded(mTransaction, splitContainer, mActivity,
        assertEquals(RESULT_NOT_EXPANDED, mPresenter.expandSplitContainerIfNeeded(mTransaction,
                secondaryActivity, null /* secondaryIntent */);
                splitContainer, mActivity, secondaryActivity, null /* secondaryIntent */));

        verify(mPresenter, never()).expandTaskFragment(any(), any());
        verify(mPresenter, never()).expandTaskFragment(any(), any());


        doReturn(createActivityInfoWithMinDimensions()).when(secondaryActivity).getActivityInfo();
        doReturn(createActivityInfoWithMinDimensions()).when(secondaryActivity).getActivityInfo();
        assertEquals(RESULT_EXPAND_FAILED_NO_TF_INFO, mPresenter.expandSplitContainerIfNeeded(
                mTransaction, splitContainer, mActivity, secondaryActivity,
                null /* secondaryIntent */));


        mPresenter.expandSplitContainerIfNeeded(mTransaction, splitContainer, mActivity,
        primaryTf.setInfo(createMockTaskFragmentInfo(primaryTf, mActivity));
                secondaryActivity, null /* secondaryIntent */);
        secondaryTf.setInfo(createMockTaskFragmentInfo(secondaryTf, secondaryActivity));


        assertEquals(RESULT_EXPANDED, mPresenter.expandSplitContainerIfNeeded(mTransaction,
                splitContainer, mActivity, secondaryActivity, null /* secondaryIntent */));
        verify(mPresenter).expandTaskFragment(eq(mTransaction),
        verify(mPresenter).expandTaskFragment(eq(mTransaction),
                eq(primaryTf.getTaskFragmentToken()));
                eq(primaryTf.getTaskFragmentToken()));
        verify(mPresenter).expandTaskFragment(eq(mTransaction),
        verify(mPresenter).expandTaskFragment(eq(mTransaction),
                eq(secondaryTf.getTaskFragmentToken()));
                eq(secondaryTf.getTaskFragmentToken()));


        clearInvocations(mPresenter);
        clearInvocations(mPresenter);
        mPresenter.expandSplitContainerIfNeeded(mTransaction, splitContainer, mActivity,
                null /* secondaryActivity */, new Intent(ApplicationProvider
                        .getApplicationContext(), MinimumDimensionActivity.class));


        assertEquals(RESULT_EXPANDED, mPresenter.expandSplitContainerIfNeeded(mTransaction,
                splitContainer, mActivity, null /* secondaryActivity */,
                new Intent(ApplicationProvider.getApplicationContext(),
                        MinimumDimensionActivity.class)));
        verify(mPresenter).expandTaskFragment(eq(mTransaction),
        verify(mPresenter).expandTaskFragment(eq(mTransaction),
                eq(primaryTf.getTaskFragmentToken()));
                eq(primaryTf.getTaskFragmentToken()));
        verify(mPresenter).expandTaskFragment(eq(mTransaction),
        verify(mPresenter).expandTaskFragment(eq(mTransaction),
@@ -246,6 +256,7 @@ public class SplitPresenterTest {
        doReturn(mActivityResources).when(activity).getResources();
        doReturn(mActivityResources).when(activity).getResources();
        doReturn(activityConfig).when(mActivityResources).getConfiguration();
        doReturn(activityConfig).when(mActivityResources).getConfiguration();
        doReturn(new ActivityInfo()).when(activity).getActivityInfo();
        doReturn(new ActivityInfo()).when(activity).getActivityInfo();
        doReturn(mock(IBinder.class)).when(activity).getActivityToken();
        return activity;
        return activity;
    }
    }
}
}