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

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

Merge "Support multiple overlays in a task" into main

parents 35da1164 80125c39
Loading
Loading
Loading
Loading
+63 −27
Original line number Diff line number Diff line
@@ -861,9 +861,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        // container update.
        updateDivider(wct, taskContainer);

        // If the last direct activity of the host task is dismissed and the overlay container is
        // the only taskFragment, the overlay container should also be dismissed.
        dismissOverlayContainerIfNeeded(wct, taskContainer);
        // If the last direct activity of the host task is dismissed and there's an always-on-top
        // overlay container in the task, the overlay container should also be dismissed.
        dismissAlwaysOnTopOverlayIfNeeded(wct, taskContainer);

        if (!shouldUpdateContainer) {
            return;
@@ -1990,7 +1990,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            @NonNull TaskFragmentContainer container) {
        final TaskContainer taskContainer = container.getTaskContainer();

        if (dismissOverlayContainerIfNeeded(wct, taskContainer)) {
        if (dismissAlwaysOnTopOverlayIfNeeded(wct, taskContainer)) {
            return;
        }

@@ -2014,24 +2014,29 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }
    }

    /** Dismisses the overlay container in the {@code taskContainer} if needed. */
    /**
     * Dismisses {@link TaskFragmentContainer#isAlwaysOnTopOverlay()} in the {@code taskContainer}
     * if needed.
     */
    @GuardedBy("mLock")
    private boolean dismissOverlayContainerIfNeeded(@NonNull WindowContainerTransaction wct,
    private boolean dismissAlwaysOnTopOverlayIfNeeded(@NonNull WindowContainerTransaction wct,
                                                      @NonNull TaskContainer taskContainer) {
        final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer();
        if (overlayContainer == null) {
        // Dismiss always-on-top overlay container if it's the only container in the task and
        // there's no direct activity in the parent task.
        final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
        if (containers.size() != 1 || taskContainer.hasDirectActivity()) {
            return false;
        }
        // Dismiss the overlay container if it's the only container in the task and there's no
        // direct activity in the parent task.
        if (taskContainer.getTaskFragmentContainers().size() == 1
                && !taskContainer.hasDirectActivity()) {
            mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */);
            return true;
        }

        final TaskFragmentContainer container = containers.getLast();
        if (!container.isAlwaysOnTopOverlay()) {
            return false;
        }

        mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependant */);
        return true;
    }

    /**
     * Updates {@link SplitContainer} with the given {@link SplitAttributes} if the
     * {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes}
@@ -2620,25 +2625,42 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    /**
     * Gets all overlay containers from all tasks in this process, or an empty list if there's
     * no overlay container.
     * <p>
     * Note that we only support one overlay container for each task, but an app could have multiple
     * tasks.
     */
    @VisibleForTesting
    @GuardedBy("mLock")
    @NonNull
    List<TaskFragmentContainer> getAllOverlayTaskFragmentContainers() {
    List<TaskFragmentContainer> getAllNonFinishingOverlayContainers() {
        final List<TaskFragmentContainer> overlayContainers = new ArrayList<>();
        for (int i = 0; i < mTaskContainers.size(); i++) {
            final TaskContainer taskContainer = mTaskContainers.valueAt(i);
            final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer();
            if (overlayContainer != null) {
                overlayContainers.add(overlayContainer);
            }
            final List<TaskFragmentContainer> overlayContainersPerTask = taskContainer
                    .getTaskFragmentContainers()
                    .stream()
                    .filter(c -> c.isOverlay() && !c.isFinished())
                    .toList();
            overlayContainers.addAll(overlayContainersPerTask);
        }
        return overlayContainers;
    }

    /**
     * Creates an overlay container or updates a visible overlay container if its
     * {@link TaskFragmentContainer#getTaskId()}, {@link TaskFragmentContainer#getOverlayTag()}
     * and {@link TaskFragmentContainer#getAssociatedActivityToken()} matches.
     * <p>
     * This method will also dismiss any existing overlay container if:
     * <ul>
     *   <li>it's visible but not meet the criteria to update overlay</li>
     *   <li>{@link TaskFragmentContainer#getOverlayTag()} matches but not meet the criteria to
     *   update overlay</li>
     * </ul>
     *
     * @param wct the {@link WindowContainerTransaction}
     * @param options the {@link ActivityOptions} to launch the overlay
     * @param intent the intent of activity to launch
     * @param launchActivity the activity to launch the overlay container
     * @return the overlay container
     */
    @VisibleForTesting
    // Suppress GuardedBy warning because lint ask to mark this method as
    // @GuardedBy(container.mController.mLock), which is mLock itself
@@ -2649,7 +2671,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            @NonNull WindowContainerTransaction wct, @NonNull Bundle options,
            @NonNull Intent intent, @NonNull Activity launchActivity) {
        final List<TaskFragmentContainer> overlayContainers =
                getAllOverlayTaskFragmentContainers();
                getAllNonFinishingOverlayContainers();
        final String overlayTag = Objects.requireNonNull(options.getString(KEY_OVERLAY_TAG));
        final boolean associateLaunchingActivity = options
                .getBoolean(KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY, true);
@@ -2672,9 +2694,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen

        final int taskId = getTaskId(launchActivity);
        if (!overlayContainers.isEmpty()) {
            // TODO(b/243518738): refactor logic below for readability.
            for (final TaskFragmentContainer overlayContainer : overlayContainers) {
                final boolean isTopNonFinishingOverlay = overlayContainer.getTaskContainer()
                        .getTopNonFinishingTaskFragmentContainer(true /* includePin */,
                                true /* includeOverlay */).equals(overlayContainer);
                if (!overlayTag.equals(overlayContainer.getOverlayTag())
                        && taskId == overlayContainer.getTaskId()) {
                        && taskId == overlayContainer.getTaskId()
                        && isTopNonFinishingOverlay) {
                    // If there's an overlay container with different tag shown in the same
                    // task, dismiss the existing overlay container.
                    mPresenter.cleanupContainer(wct, overlayContainer,
@@ -2694,7 +2721,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                }
                if (overlayTag.equals(overlayContainer.getOverlayTag())
                        && taskId == overlayContainer.getTaskId()) {
                    if (associateLaunchingActivity && !launchActivity.getActivityToken()
                    if (!isTopNonFinishingOverlay) {
                        Log.w(TAG, "The invisible overlay container with tag:"
                                + overlayContainer.getOverlayTag() + " is dismissed because"
                                + " there's a launching overlay container with the same tag."
                                + " The new associated activity is " + launchActivity);
                        // Dismiss the invisible overlay container regardless of activity
                        // association if it collides the tag of new launched overlay container .
                        mPresenter.cleanupContainer(wct, overlayContainer,
                                false /* shouldFinishDependant */);
                    } else if (associateLaunchingActivity && !launchActivity.getActivityToken()
                            .equals(overlayContainer.getAssociatedActivityToken())) {
                        Log.w(TAG, "The overlay container with tag:"
                                + overlayContainer.getOverlayTag() + " is dismissed because"
+5 −0
Original line number Diff line number Diff line
@@ -467,6 +467,11 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
            reorderTaskFragmentToFront(wct,
                    pinnedContainer.getSecondaryContainer().getTaskFragmentToken());
        }
        final TaskFragmentContainer alwaysOnTopOverlayContainer = container.getTaskContainer()
                .getAlwaysOnTopOverlayContainer();
        if (alwaysOnTopOverlayContainer != null) {
            reorderTaskFragmentToFront(wct, alwaysOnTopOverlayContainer.getTaskFragmentToken());
        }
    }

    @Override
+24 −18
Original line number Diff line number Diff line
@@ -70,9 +70,11 @@ class TaskContainer {
    @Nullable
    private SplitPinContainer mSplitPinContainer;

    /** The overlay container in this Task. */
    /**
     * The {@link TaskFragmentContainer#isAlwaysOnTopOverlay()} in the Task.
     */
    @Nullable
    private TaskFragmentContainer mOverlayContainer;
    private TaskFragmentContainer mAlwaysOnTopOverlayContainer;

    @NonNull
    private final Configuration mConfiguration;
@@ -316,10 +318,12 @@ class TaskContainer {
        return null;
    }

    /** Returns the overlay container in the task, or {@code null} if it doesn't exist. */
    /**
     * Returns the always-on-top overlay container in the task, or {@code null} if it doesn't exist.
     */
    @Nullable
    TaskFragmentContainer getOverlayContainer() {
        return mOverlayContainer;
    TaskFragmentContainer getAlwaysOnTopOverlayContainer() {
        return mAlwaysOnTopOverlayContainer;
    }

    int indexOf(@NonNull TaskFragmentContainer child) {
@@ -531,7 +535,21 @@ class TaskContainer {
        updateSplitPinContainerIfNecessary();
        // Update overlay container after split pin container since the overlay should be on top of
        // pin container.
        updateOverlayContainerIfNecessary();
        updateAlwaysOnTopOverlayIfNecessary();
    }

    private void updateAlwaysOnTopOverlayIfNecessary() {
        final List<TaskFragmentContainer> alwaysOnTopOverlays = mContainers
                .stream().filter(TaskFragmentContainer::isAlwaysOnTopOverlay).toList();
        if (alwaysOnTopOverlays.size() > 1) {
            throw new IllegalStateException("There must be at most one always-on-top overlay "
                    + "container per Task");
        }
        mAlwaysOnTopOverlayContainer = alwaysOnTopOverlays.isEmpty()
                ? null : alwaysOnTopOverlays.getFirst();
        if (mAlwaysOnTopOverlayContainer != null) {
            moveContainerToLastIfNecessary(mAlwaysOnTopOverlayContainer);
        }
    }

    private void updateSplitPinContainerIfNecessary() {
@@ -559,18 +577,6 @@ class TaskContainer {
        }
    }

    private void updateOverlayContainerIfNecessary() {
        final List<TaskFragmentContainer> overlayContainers = mContainers.stream()
                .filter(TaskFragmentContainer::isOverlay).toList();
        if (overlayContainers.size() > 1) {
            throw new IllegalStateException("There must be at most one overlay container per Task");
        }
        mOverlayContainer = overlayContainers.isEmpty() ? null : overlayContainers.get(0);
        if (mOverlayContainer != null) {
            moveContainerToLastIfNecessary(mOverlayContainer);
        }
    }

    /** Moves the {@code container} to the last to align taskFragments' z-order. */
    private void moveContainerToLastIfNecessary(@NonNull TaskFragmentContainer container) {
        final int index = mContainers.indexOf(container);
+8 −0
Original line number Diff line number Diff line
@@ -1023,6 +1023,14 @@ class TaskFragmentContainer {
        return mAssociatedActivityToken != null;
    }

    /**
     * Returns {@code true} if the overlay container should be always on top, which should be
     * a non-fill-parent overlay without activity association.
     */
    boolean isAlwaysOnTopOverlay() {
        return isOverlay() && !isAssociatedWithActivity();
    }

    @Override
    public String toString() {
        return toString(true /* includeContainersToFinishOnExit */);
+98 −51

File changed.

Preview size limit exceeded, changes collapsed.