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

Commit dd82dd11 authored by Charles Chen's avatar Charles Chen Committed by Android (Google) Code Review
Browse files

Merge "Implement ActivityStack operations" into main

parents 2f767ec4 f5bc4291
Loading
Loading
Loading
Loading
+314 −114

File changed.

Preview size limit exceeded, changes collapsed.

+23 −10
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package androidx.window.extensions.embedding;

import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.pm.PackageManager.MATCH_ALL;

import android.app.Activity;
@@ -187,7 +189,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties,
                splitAttributes);
        final int windowingMode = mController.getTaskContainer(taskId)
                .getWindowingModeForSplitTaskFragment(secondaryRelBounds);
                .getWindowingModeForTaskFragment(secondaryRelBounds);
        createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
                primaryActivity.getActivityToken(), secondaryRelBounds, windowingMode);
        updateAnimationParams(wct, secondaryContainer.getTaskFragmentToken(), splitAttributes);
@@ -259,7 +261,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        if (container == null || container == containerToAvoid) {
            container = mController.newContainer(activity, taskId);
            final int windowingMode = mController.getTaskContainer(taskId)
                    .getWindowingModeForSplitTaskFragment(relBounds);
                    .getWindowingModeForTaskFragment(relBounds);
            final IBinder reparentActivityToken = activity.getActivityToken();
            createTaskFragment(wct, container.getTaskFragmentToken(), reparentActivityToken,
                    relBounds, windowingMode, reparentActivityToken);
@@ -268,7 +270,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        } else {
            resizeTaskFragmentIfRegistered(wct, container, relBounds);
            final int windowingMode = mController.getTaskContainer(taskId)
                    .getWindowingModeForSplitTaskFragment(relBounds);
                    .getWindowingModeForTaskFragment(relBounds);
            updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
        }
        updateAnimationParams(wct, container.getTaskFragmentToken(), splitAttributes);
@@ -310,7 +312,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
                // Pass in the primary container to make sure it is added right above the primary.
                primaryContainer);
        final TaskContainer taskContainer = mController.getTaskContainer(taskId);
        final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
        final int windowingMode = taskContainer.getWindowingModeForTaskFragment(
                primaryRelBounds);
        mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
                rule, splitAttributes);
@@ -347,6 +349,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
                && secondaryContainer.areLastRequestedBoundsEqual(null /* bounds */)
                && !secondaryRelBounds.isEmpty();

        // TODO(b/243518738): remove usages of XXXIfRegistered.
        // If the task fragments are not registered yet, the positions will be updated after they
        // are created again.
        resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRelBounds);
@@ -357,7 +360,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
            // When placeholder is shown in split, we should keep the focus on the primary.
            wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
        }
        final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
        final int windowingMode = taskContainer.getWindowingModeForTaskFragment(
                primaryRelBounds);
        updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode);
        updateTaskFragmentWindowingModeIfRegistered(wct, secondaryContainer, windowingMode);
@@ -398,13 +401,13 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
     * Sets whether to enable isolated navigation for this {@link TaskFragmentContainer}
     */
    void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct,
                                           @NonNull TaskFragmentContainer taskFragmentContainer,
                                           @NonNull TaskFragmentContainer container,
                                           boolean isolatedNavigationEnabled) {
        if (taskFragmentContainer.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) {
        if (container.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) {
            return;
        }
        taskFragmentContainer.setIsolatedNavigationEnabled(isolatedNavigationEnabled);
        setTaskFragmentIsolatedNavigation(wct, taskFragmentContainer.getTaskFragmentToken(),
        container.setIsolatedNavigationEnabled(isolatedNavigationEnabled);
        setTaskFragmentIsolatedNavigation(wct, container.getTaskFragmentToken(),
                isolatedNavigationEnabled);
    }

@@ -566,6 +569,15 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        super.setCompanionTaskFragment(wct, primary, secondary);
    }

    void applyActivityStackAttributes(@NonNull WindowContainerTransaction wct,
            @NonNull TaskFragmentContainer container, @NonNull ActivityStackAttributes attributes) {
        final Rect bounds = attributes.getRelativeBounds();

        resizeTaskFragment(wct, container.getTaskFragmentToken(), bounds);
        updateWindowingMode(wct, container.getTaskFragmentToken(),
                bounds.isEmpty() ? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_MULTI_WINDOW);
    }

    /**
     * Expands the split container if the current split bounds are smaller than the Activity or
     * Intent that is added to the container.
@@ -1086,7 +1098,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
    }

    @NonNull
    ParentContainerInfo toParentContainerInfo(@NonNull TaskProperties taskProperties) {
    ParentContainerInfo createParentContainerInfoFromTaskProperties(
            @NonNull TaskProperties taskProperties) {
        final Configuration configuration = taskProperties.getConfiguration();
        final WindowLayoutInfo windowLayoutInfo = mWindowLayoutComponent
                .getCurrentWindowLayoutInfo(taskProperties.getDisplayId(),
+21 −1
Original line number Diff line number Diff line
@@ -145,7 +145,7 @@ class TaskContainer {
     *                              the pair of TaskFragments are stacked due to the limited space.
     */
    @WindowingMode
    int getWindowingModeForSplitTaskFragment(@Nullable Rect taskFragmentBounds) {
    int getWindowingModeForTaskFragment(@Nullable Rect taskFragmentBounds) {
        // Only set to multi-windowing mode if the pair are showing side-by-side. Otherwise, it
        // will be set to UNDEFINED which will then inherit the Task windowing mode.
        if (taskFragmentBounds == null || taskFragmentBounds.isEmpty() || isInPictureInPicture()) {
@@ -443,6 +443,26 @@ class TaskContainer {
        return splitStates;
    }

    // TODO(b/317358445): Makes ActivityStack and SplitInfo callback more stable.
    /**
     * Returns a list of currently active {@link ActivityStack activityStacks}.
     *
     * @return a list of {@link ActivityStack activityStacks} if all the containers are in
     * a stable state, or {@code null} otherwise.
     */
    @Nullable
    List<ActivityStack> getActivityStacksIfStable() {
        final List<ActivityStack> activityStacks = new ArrayList<>();
        for (TaskFragmentContainer container : mContainers) {
            final ActivityStack activityStack = container.toActivityStackIfStable();
            if (activityStack == null) {
                return null;
            }
            activityStacks.add(activityStack);
        }
        return activityStacks;
    }

    /** A wrapper class which contains the information of {@link TaskContainer} */
    static final class TaskProperties {
        private final int mDisplayId;
+18 −5
Original line number Diff line number Diff line
@@ -107,11 +107,11 @@ class TaskFragmentContainer {
    private final String mOverlayTag;

    /**
     * The launch options that was used to create this container. Must not be {@code null} for
     * {@link #isOverlay()} container.
     * The launch options that was used to create this container. Must not {@link Bundle#isEmpty()}
     * for {@link #isOverlay()} container.
     */
    @Nullable
    private final Bundle mLaunchOptions;
    @NonNull
    private final Bundle mLaunchOptions = new Bundle();

    /** Indicates whether the container was cleaned up after the last activity was removed. */
    private boolean mIsFinished;
@@ -210,7 +210,9 @@ class TaskFragmentContainer {
        if (overlayTag != null) {
            Objects.requireNonNull(launchOptions);
        }
        mLaunchOptions = launchOptions;
        if (launchOptions != null) {
            mLaunchOptions.putAll(launchOptions);
        }

        if (pairedPrimaryContainer != null) {
            // The TaskFragment will be positioned right above the paired container.
@@ -925,6 +927,17 @@ class TaskFragmentContainer {
        return mOverlayTag;
    }

    /**
     * Returns the options that was used to launch this {@link TaskFragmentContainer}.
     * {@link Bundle#isEmpty()} means there's no launch option for this container.
     * <p>
     * Note that WM Jetpack owns the logic. The WM Extension library must not modify this object.
     */
    @NonNull
    Bundle getLaunchOptions() {
        return mLaunchOptions;
    }

    @Override
    public String toString() {
        return toString(true /* includeContainersToFinishOnExit */);
+52 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package androidx.window.extensions.embedding;

import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;

import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG;
@@ -287,10 +288,10 @@ public class OverlayPresentationTest {
        createOrUpdateOverlayTaskFragmentIfNeeded("test");

        verify(mSplitPresenter).resizeTaskFragment(mTransaction, overlayToken, new Rect());
        verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
        verify(mSplitPresenter).updateWindowingMode(mTransaction, overlayToken,
                WINDOWING_MODE_UNDEFINED);
        verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayContainer,
                false);
        assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
                .containsExactly(overlayContainer);
    }

    @Test
@@ -315,8 +316,10 @@ public class OverlayPresentationTest {
        createOrUpdateOverlayTaskFragmentIfNeeded("test");

        verify(mSplitPresenter).resizeTaskFragment(mTransaction, overlayToken, new Rect());
        verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
                false);
        verify(mSplitPresenter).updateWindowingMode(mTransaction,
                overlayToken, WINDOWING_MODE_UNDEFINED);
        verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction,
                overlayContainer, false);
        assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
                .containsExactly(overlayContainer);
    }
@@ -425,6 +428,50 @@ public class OverlayPresentationTest {
                .that(taskContainer.getTaskFragmentContainers()).isEmpty();
    }

    @Test
    public void testUpdateActivityStackAttributes_nullParams_throwException() {
        assertThrows(NullPointerException.class, () ->
                mSplitController.updateActivityStackAttributes(null,
                        new ActivityStackAttributes.Builder().build()));

        assertThrows(NullPointerException.class, () ->
                mSplitController.updateActivityStackAttributes(new Binder(), null));

        verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any());
    }

    @Test
    public void testUpdateActivityStackAttributes_nullContainer_earlyReturn() {
        final TaskFragmentContainer container = mSplitController.newContainer(mActivity,
                mActivity.getTaskId());
        mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
                new ActivityStackAttributes.Builder().build());

        verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any());
    }

    @Test
    public void testUpdateActivityStackAttributes_notOverlay_earlyReturn() {
        final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity);

        mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
                new ActivityStackAttributes.Builder().build());

        verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any());
    }

    @Test
    public void testUpdateActivityStackAttributes() {
        final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test");
        doNothing().when(mSplitPresenter).applyActivityStackAttributes(any(), any(), any());
        final ActivityStackAttributes attrs = new ActivityStackAttributes.Builder().build();
        final IBinder token = container.getTaskFragmentToken();

        mSplitController.updateActivityStackAttributes(token, attrs);

        verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs));
    }

    /**
     * A simplified version of {@link SplitController.ActivityStartMonitor
     * #createOrUpdateOverlayTaskFragmentIfNeeded}
Loading