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

Commit c8db426a authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Fix AE launch of the same SingleTask Activity twice" into main

parents 009f9d5f 1a941dbb
Loading
Loading
Loading
Loading
+41 −10
Original line number Diff line number Diff line
@@ -2734,6 +2734,23 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        return overlayContainers;
    }

    @GuardedBy("mLock")
    @NonNull
    private List<IBinder> getAllNonFinishingContainerTokens() {
        final List<IBinder> tokens = new ArrayList<>();
        for (int i = 0; i < mTaskContainers.size(); i++) {
            final TaskContainer taskContainer = mTaskContainers.valueAt(i);
            final List<IBinder> tokensPerTask = taskContainer
                    .getTaskFragmentContainers()
                    .stream()
                    .filter(c -> !c.isFinished())
                    .map(TaskFragmentContainer::getTaskFragmentToken)
                    .toList();
            tokens.addAll(tokensPerTask);
        }
        return tokens;
    }

    @GuardedBy("mLock")
    private boolean isAssociatedWithOverlay(@NonNull Activity activity) {
        final TaskContainer taskContainer = getTaskContainer(getTaskId(activity));
@@ -3086,9 +3103,15 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
     */
    @VisibleForTesting
    class ActivityStartMonitor extends Instrumentation.ActivityMonitor {

        /**
         * Keeps track of the current starting activity Intent if it is also used to create a new
         * TaskFragment as {@link TaskFragmentContainer#getPendingAppearedIntent()}.
         */
        @VisibleForTesting
        @Nullable
        @GuardedBy("mLock")
        Intent mCurrentIntent;
        Intent mCurrentPendingAppearedIntent;

        @Override
        public Instrumentation.ActivityResult onStartActivity(@NonNull Context who,
@@ -3109,9 +3132,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            // TODO(b/295993745): Use KEY_LAUNCH_TASK_FRAGMENT_TOKEN after WM Jetpack migrates to
            // bundle. This is still needed to support #setLaunchingActivityStack.
            if (options.getBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN) != null) {
                synchronized (mLock) {
                    mCurrentIntent = intent;
                }
                return super.onStartActivity(who, intent, options);
            }

@@ -3142,6 +3162,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                        .startNewTransaction();
                transactionRecord.setOriginType(TASK_FRAGMENT_TRANSIT_OPEN);
                final WindowContainerTransaction wct = transactionRecord.getTransaction();

                // Track the existing TFs before the operation.
                final List<IBinder> allExistingTFTokens = getAllNonFinishingContainerTokens();

                final TaskFragmentContainer launchedInTaskFragment;
                if (launchingActivity != null) {
                    final String overlayTag = options.getString(KEY_OVERLAY_TAG);
@@ -3171,12 +3195,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                    // Amend the request to let the WM know that the activity should be placed in
                    // the dedicated container.
                    // TODO(b/229680885): skip override launching TaskFragment token by split-rule
                    options.putBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
                            launchedInTaskFragment.getTaskFragmentToken());
                    final IBinder tfToken = launchedInTaskFragment.getTaskFragmentToken();
                    options.putBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN, tfToken);
                    if (activityEmbeddingDelayTaskFragmentFinishForActivityLaunch()) {
                        launchedInTaskFragment.setActivityLaunchHint();
                    }
                    mCurrentIntent = intent;
                    if (!allExistingTFTokens.contains(tfToken)) {
                        // When we are creating a new TaskFragment for this Intent, keep track of it
                        // in case the startActivity fails, so that we can cleanup early.
                        // If the TF is an existing one, this Intent will not be kept as a pending
                        // appeared Intent.
                        mCurrentPendingAppearedIntent = intent;
                    }
                } else {
                    transactionRecord.abort();
                }
@@ -3189,18 +3219,19 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        public void onStartActivityResult(int result, @NonNull Bundle bOptions) {
            super.onStartActivityResult(result, bOptions);
            synchronized (mLock) {
                if (mCurrentIntent != null && result != START_SUCCESS) {
                if (mCurrentPendingAppearedIntent != null && result != START_SUCCESS) {
                    // Clear the pending appeared intent if the activity was not started
                    // successfully.
                    final IBinder token = bOptions.getBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN);
                    if (token != null) {
                        final TaskFragmentContainer container = getContainer(token);
                        if (container != null) {
                            container.clearPendingAppearedIntentIfNeeded(mCurrentIntent);
                            container.clearPendingAppearedIntentIfNeeded(
                                    mCurrentPendingAppearedIntent);
                        }
                    }
                }
                mCurrentIntent = null;
                mCurrentPendingAppearedIntent = null;
            }
        }
    }
+6 −0
Original line number Diff line number Diff line
@@ -864,6 +864,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
            // bounds. Return failure to create a new SplitContainer which fills task bounds.
            final TaskFragmentContainer primaryContainer = splitContainer.getPrimaryContainer();
            final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
            if (primaryContainer.areLastRequestedBoundsEqual(null)
                    && secondaryContainer.areLastRequestedBoundsEqual(null)
                    && secondaryContainer.isLastAdjacentTaskFragmentEqual(null, null)) {
                // No need to update since it is already expanded.
                return RESULT_EXPANDED;
            }
            if (primaryContainer.getInfo() == null || secondaryContainer.getInfo() == null) {
                return RESULT_EXPAND_FAILED_NO_TF_INFO;
            }
+40 −1
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
package androidx.window.extensions.embedding;

import static android.app.ActivityManager.START_CANCELED;
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -384,13 +387,49 @@ public class SplitControllerTest {
        final Bundle bundle = new Bundle();
        bundle.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
                container.getTaskFragmentToken());
        monitor.mCurrentIntent = intent;
        monitor.mCurrentPendingAppearedIntent = intent;
        doReturn(container).when(mSplitController).getContainer(any(IBinder.class));

        monitor.onStartActivityResult(START_CANCELED, bundle);
        assertNull(container.getPendingAppearedIntent());
    }

    @Test
    public void testOnStartActivityResultError_notClearPendingForExistingContainer() {
        final SplitController.ActivityStartMonitor monitor =
                mSplitController.getActivityStartMonitor();
        final Intent intent = new Intent();
        final Bundle options0 = new Bundle();
        setupSplitRule(mActivity, intent, false /* clearTop */);

        // On creating a new split pair, keep track of the pending Intent.
        monitor.onStartActivity(mActivity, intent, options0);
        final IBinder tfToken = options0.getBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN);
        final TaskFragmentContainer container = mSplitController.getContainer(tfToken);

        assertEquals(intent, monitor.mCurrentPendingAppearedIntent);
        assertNotNull(container);
        assertEquals(intent, container.getPendingAppearedIntent());

        // On success, do not clear the pending Intent.
        monitor.onStartActivityResult(START_SUCCESS, new Bundle());

        assertNull(monitor.mCurrentPendingAppearedIntent);
        assertEquals(intent, container.getPendingAppearedIntent());

        // On reusing an existing split pair, do not track of the pending Intent.
        final Bundle options1 = new Bundle();
        monitor.onStartActivity(mActivity, intent, options1);

        assertEquals(tfToken, options1.getBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN));
        assertNull(monitor.mCurrentPendingAppearedIntent);

        // On failure of a non-tracking Intent, do not clear the pending Intent.
        monitor.onStartActivityResult(START_DELIVERED_TO_TOP, new Bundle());

        assertEquals(intent, container.getPendingAppearedIntent());
    }

    @Test
    public void testOnActivityCreated() {
        mSplitController.onActivityCreated(mTransaction, mActivity);
+21 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -666,6 +667,7 @@ public class SplitPresenterTest {

    @Test
    public void testExpandSplitContainerIfNeeded() {
        doNothing().when(mPresenter).expandTaskFragment(any(), any());
        Activity secondaryActivity = createMockActivity();
        SplitRule splitRule = createSplitRule(mActivity, secondaryActivity);
        TaskFragmentContainer primaryTf = createTfContainer(mController, mActivity);
@@ -681,11 +683,30 @@ public class SplitPresenterTest {
                splitContainer, mActivity, secondaryActivity, null /* secondaryIntent */));
        verify(mPresenter, never()).expandTaskFragment(any(), any());

        // Not fail when the split pair is already expanded, even if it's info is not yet received.
        splitContainer.updateCurrentSplitAttributes(SPLIT_ATTRIBUTES);
        doReturn(createActivityInfoWithMinDimensions()).when(secondaryActivity).getActivityInfo();
        primaryTf.setLastRequestedBounds(null);
        secondaryTf.setLastRequestedBounds(null);

        assertEquals(RESULT_EXPANDED, mPresenter.expandSplitContainerIfNeeded(
                mTransaction, splitContainer, mActivity, secondaryActivity,
                null /* secondaryIntent */));
        verify(mPresenter, never()).expandTaskFragment(any(), any());

        // Failed when the split pair is not expanded, and it's info is not yet received.
        splitContainer.updateCurrentSplitAttributes(SPLIT_ATTRIBUTES);
        primaryTf.setLastRequestedBounds(new Rect(0, 0, 500, 1000));
        secondaryTf.setLastRequestedBounds(new Rect(500, 0, 1000, 1000));
        final WindowContainerTransaction.TaskFragmentAdjacentParams params =
                new WindowContainerTransaction.TaskFragmentAdjacentParams();
        secondaryTf.setLastAdjacentTaskFragment(primaryTf.getTaskFragmentToken(), params);
        primaryTf.setLastAdjacentTaskFragment(secondaryTf.getTaskFragmentToken(), params);

        assertEquals(RESULT_EXPAND_FAILED_NO_TF_INFO, mPresenter.expandSplitContainerIfNeeded(
                mTransaction, splitContainer, mActivity, secondaryActivity,
                null /* secondaryIntent */));
        verify(mPresenter, never()).expandTaskFragment(any(), any());

        splitContainer.updateCurrentSplitAttributes(SPLIT_ATTRIBUTES);
        primaryTf.setInfo(mTransaction, createMockTaskFragmentInfo(primaryTf, mActivity));