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

Commit 0217352c authored by Chris Li's avatar Chris Li Committed by Android (Google) Code Review
Browse files

Merge "Implement the runtime APIs"

parents cfe33554 eca505ed
Loading
Loading
Loading
Loading
+56 −12
Original line number Diff line number Diff line
@@ -21,9 +21,11 @@ import android.os.Binder;
import android.os.IBinder;
import android.util.Pair;
import android.util.Size;
import android.window.TaskFragmentParentInfo;
import android.window.WindowContainerTransaction;

import androidx.annotation.NonNull;
import androidx.window.extensions.core.util.function.Function;

/**
 * Client-side descriptor of a split that holds two containers.
@@ -35,8 +37,12 @@ class SplitContainer {
    private final TaskFragmentContainer mSecondaryContainer;
    @NonNull
    private final SplitRule mSplitRule;
    /** @see SplitContainer#getCurrentSplitAttributes() */
    @NonNull
    private SplitAttributes mSplitAttributes;
    private SplitAttributes mCurrentSplitAttributes;
    /** @see SplitContainer#getDefaultSplitAttributes() */
    @NonNull
    private SplitAttributes mDefaultSplitAttributes;
    @NonNull
    private final IBinder mToken;

@@ -48,7 +54,8 @@ class SplitContainer {
        mPrimaryContainer = primaryContainer;
        mSecondaryContainer = secondaryContainer;
        mSplitRule = splitRule;
        mSplitAttributes = splitAttributes;
        mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes();
        mCurrentSplitAttributes = splitAttributes;
        mToken = new Binder("SplitContainer");

        if (shouldFinishPrimaryWithSecondary(splitRule)) {
@@ -82,9 +89,37 @@ class SplitContainer {
        return mSplitRule;
    }

    /**
     * Returns the current {@link SplitAttributes} this {@code SplitContainer} is showing.
     * <p>
     * If the {@code SplitAttributes} calculator function is not set by
     * {@link SplitController#setSplitAttributesCalculator(Function)}, the current
     * {@code SplitAttributes} is either to expand the containers if the size constraints of
     * {@link #getSplitRule()} are not satisfied,
     * or the {@link #getDefaultSplitAttributes()}, otherwise.
     * </p><p>
     * If the {@code SplitAttributes} calculator function is set, the current
     * {@code SplitAttributes} will be customized by the function, which can be any
     * {@code SplitAttributes}.
     * </p>
     *
     * @see SplitAttributes.SplitType.ExpandContainersSplitType
     */
    @NonNull
    SplitAttributes getSplitAttributes() {
        return mSplitAttributes;
    SplitAttributes getCurrentSplitAttributes() {
        return mCurrentSplitAttributes;
    }

    /**
     * Returns the default {@link SplitAttributes} when the parent task container bounds satisfy
     * {@link #getSplitRule()} constraints.
     * <p>
     * The value is usually from {@link SplitRule#getDefaultSplitAttributes} unless it is overridden
     * by {@link SplitController#updateSplitAttributes(IBinder, SplitAttributes)}.
     */
    @NonNull
    SplitAttributes getDefaultSplitAttributes() {
        return mDefaultSplitAttributes;
    }

    @NonNull
@@ -95,11 +130,19 @@ class SplitContainer {
    /**
     * Updates the {@link SplitAttributes} to this container.
     * It is usually used when there's a folding state change or
     * {@link SplitController#onTaskFragmentParentInfoChanged(WindowContainerTransaction, int,
     * Configuration)}.
     * {@link SplitController#onTaskFragmentParentInfoChanged(WindowContainerTransaction,
     * int, TaskFragmentParentInfo)}.
     */
    void updateCurrentSplitAttributes(@NonNull SplitAttributes splitAttributes) {
        mCurrentSplitAttributes = splitAttributes;
    }

    /**
     * Overrides the default {@link SplitAttributes} to this container, which may be different
     * from {@link SplitRule#getDefaultSplitAttributes}.
     */
    void setSplitAttributes(@NonNull SplitAttributes splitAttributes) {
        mSplitAttributes = splitAttributes;
    void updateDefaultSplitAttributes(@NonNull SplitAttributes splitAttributes) {
        mDefaultSplitAttributes = splitAttributes;
    }

    @NonNull
@@ -121,7 +164,7 @@ class SplitContainer {
    @NonNull
    SplitInfo toSplitInfo() {
        return new SplitInfo(mPrimaryContainer.toActivityStack(),
                mSecondaryContainer.toActivityStack(), mSplitAttributes, mToken);
                mSecondaryContainer.toActivityStack(), mCurrentSplitAttributes, mToken);
    }

    static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
@@ -180,9 +223,10 @@ class SplitContainer {
    public String toString() {
        return "SplitContainer{"
                + " primaryContainer=" + mPrimaryContainer
                + " secondaryContainer=" + mSecondaryContainer
                + " splitRule=" + mSplitRule
                + " splitAttributes" + mSplitAttributes
                + ", secondaryContainer=" + mSecondaryContainer
                + ", splitRule=" + mSplitRule
                + ", currentSplitAttributes" + mCurrentSplitAttributes
                + ", defaultSplitAttributes" + mDefaultSplitAttributes
                + "}";
    }
}
+165 −47
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -279,6 +280,98 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }
    }

    @Override
    public void finishActivityStacks(@NonNull Set<IBinder> activityStackTokens) {
        if (activityStackTokens.isEmpty()) {
            return;
        }
        synchronized (mLock) {
            // Translate ActivityStack to TaskFragmentContainer.
            final List<TaskFragmentContainer> pendingFinishingContainers =
                    activityStackTokens.stream()
                    .map(token -> {
                        synchronized (mLock) {
                            return getContainer(token);
                        }
                    }).filter(Objects::nonNull)
                    .toList();

            if (pendingFinishingContainers.isEmpty()) {
                return;
            }
            // Start transaction with close transit type.
            final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
            transactionRecord.setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
            final WindowContainerTransaction wct = transactionRecord.getTransaction();

            forAllTaskContainers(taskContainer -> {
                synchronized (mLock) {
                    final List<TaskFragmentContainer> containers = taskContainer.mContainers;
                    // Clean up the TaskFragmentContainers by the z-order from the lowest.
                    for (int i = 0; i < containers.size() - 1; i++) {
                        final TaskFragmentContainer container = containers.get(i);
                        if (pendingFinishingContainers.contains(container)) {
                            // Don't update records here to prevent double invocation.
                            container.finish(false /* shouldFinishDependant */, mPresenter,
                                    wct, this, false /* shouldRemoveRecord */);
                        }
                    }
                    // Remove container records.
                    removeContainers(taskContainer, pendingFinishingContainers);
                    // Update the change to the client side.
                    updateContainersInTaskIfVisible(wct, taskContainer.getTaskId());
                }
            });

            // Apply the transaction.
            transactionRecord.apply(false /* shouldApplyIndependently */);
        }
    }

    @Override
    public void invalidateTopVisibleSplitAttributes() {
        synchronized (mLock) {
            WindowContainerTransaction wct = mTransactionManager.startNewTransaction()
                    .getTransaction();
            forAllTaskContainers(taskContainer -> {
                synchronized (mLock) {
                    updateContainersInTaskIfVisible(wct, taskContainer.getTaskId());
                }
            });
            mTransactionManager.getCurrentTransactionRecord()
                    .apply(false /* shouldApplyIndependently */);
        }
    }

    @GuardedBy("mLock")
    private void forAllTaskContainers(@NonNull Consumer<TaskContainer> callback) {
        for (int i = mTaskContainers.size() - 1; i >= 0; --i) {
            callback.accept(mTaskContainers.valueAt(i));
        }
    }

    @Override
    public void updateSplitAttributes(@NonNull IBinder splitInfoToken,
            @NonNull SplitAttributes splitAttributes) {
        synchronized (mLock) {
            final SplitContainer splitContainer = getSplitContainer(splitInfoToken);
            if (splitContainer == null) {
                Log.w(TAG, "Cannot find SplitContainer for token:" + splitInfoToken);
                return;
            }
            WindowContainerTransaction wct = mTransactionManager.startNewTransaction()
                    .getTransaction();
            if (updateSplitContainerIfNeeded(splitContainer, wct, splitAttributes)) {
                splitContainer.updateDefaultSplitAttributes(splitAttributes);
                mTransactionManager.getCurrentTransactionRecord()
                        .apply(false /* shouldApplyIndependently */);
            } else {
                // Abort if the SplitContainer wasn't updated.
                mTransactionManager.getCurrentTransactionRecord().abort();
            }
        }
    }

    /**
     * Called when the transaction is ready so that the organizer can update the TaskFragments based
     * on the changes in transaction.
@@ -648,35 +741,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }
    }

    /** Returns whether the given {@link TaskContainer} may show in split. */
    // Suppress GuardedBy warning because lint asks to mark this method as
    // @GuardedBy(mPresenter.mController.mLock), which is mLock itself
    @SuppressWarnings("GuardedBy")
    @GuardedBy("mLock")
    private boolean mayShowSplit(@NonNull TaskContainer taskContainer) {
        // No split inside PIP.
        if (taskContainer.isInPictureInPicture()) {
            return false;
        }
        // Always assume the TaskContainer if SplitAttributesCalculator is set
        if (mSplitAttributesCalculator != null) {
            return true;
        }
        // Check if the parent container bounds can support any split rule.
        for (EmbeddingRule rule : mSplitRules) {
            if (!(rule instanceof SplitRule)) {
                continue;
            }
            final SplitRule splitRule = (SplitRule) rule;
            final SplitAttributes splitAttributes = mPresenter.computeSplitAttributes(
                    taskContainer.getTaskProperties(), splitRule, null /* minDimensionsPair */);
            if (shouldShowSplit(splitAttributes)) {
                return true;
            }
        }
        return false;
    }

    @VisibleForTesting
    @GuardedBy("mLock")
    void onActivityCreated(@NonNull WindowContainerTransaction wct,
@@ -1360,20 +1424,33 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
     * Removes the container from bookkeeping records.
     */
    void removeContainer(@NonNull TaskFragmentContainer container) {
        removeContainers(container.getTaskContainer(), Collections.singletonList(container));
    }

    /**
     * Removes containers from bookkeeping records.
     */
    void removeContainers(@NonNull TaskContainer taskContainer,
            @NonNull List<TaskFragmentContainer> containers) {
        // Remove all split containers that included this one
        final TaskContainer taskContainer = container.getTaskContainer();
        taskContainer.mContainers.remove(container);
        taskContainer.mContainers.removeAll(containers);
        // Marked as a pending removal which will be removed after it is actually removed on the
        // server side (#onTaskFragmentVanished).
        // In this way, we can keep track of the Task bounds until we no longer have any
        // TaskFragment there.
        taskContainer.mFinishedContainer.add(container.getTaskFragmentToken());
        taskContainer.mFinishedContainer.addAll(containers.stream().map(
                TaskFragmentContainer::getTaskFragmentToken).toList());

        // Cleanup any split references.
        final List<SplitContainer> containersToRemove = new ArrayList<>();
        for (SplitContainer splitContainer : taskContainer.mSplitContainers) {
            if (container.equals(splitContainer.getSecondaryContainer())
                    || container.equals(splitContainer.getPrimaryContainer())) {
            if (containersToRemove.contains(splitContainer)) {
                // Don't need to check because it has been in the remove list.
                continue;
            }
            if (containers.stream().anyMatch(container ->
                    splitContainer.getPrimaryContainer().equals(container)
                            || splitContainer.getSecondaryContainer().equals(container))) {
                containersToRemove.add(splitContainer);
            }
        }
@@ -1381,7 +1458,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen

        // Cleanup any dependent references.
        for (TaskFragmentContainer containerToUpdate : taskContainer.mContainers) {
            containerToUpdate.removeContainerToFinishOnExit(container);
            containerToUpdate.removeContainersToFinishOnExit(containers);
        }
    }

@@ -1461,26 +1538,53 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        if (splitContainer == null) {
            return;
        }

        updateSplitContainerIfNeeded(splitContainer, wct, null /* splitAttributes */);
    }

    /**
     * Updates {@link SplitContainer} with the given {@link SplitAttributes} if the
     * {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes}
     * are {@code null}, the {@link SplitAttributes} will be calculated with
     * {@link SplitPresenter#computeSplitAttributes(TaskContainer.TaskProperties, SplitRule, Pair)}.
     *
     * @param splitContainer The {@link SplitContainer} to update
     * @param splitAttributes Update with this {@code splitAttributes} if it is not {@code null}.
     *                        Otherwise, use the value calculated by
     *                        {@link SplitPresenter#computeSplitAttributes(
     *                        TaskContainer.TaskProperties, SplitRule, Pair)}
     *
     * @return {@code true} if the update succeed. Otherwise, returns {@code false}.
     */
    @GuardedBy("mLock")
    private boolean updateSplitContainerIfNeeded(@NonNull SplitContainer splitContainer,
            @NonNull WindowContainerTransaction wct, @Nullable SplitAttributes splitAttributes) {
        if (!isTopMostSplit(splitContainer)) {
            // Skip position update - it isn't the topmost split.
            return;
            return false;
        }
        if (splitContainer.getPrimaryContainer().isFinished()
                || splitContainer.getSecondaryContainer().isFinished()) {
            // Skip position update - one or both containers are finished.
            return;
            return false;
        }
        final TaskContainer taskContainer = splitContainer.getTaskContainer();
        if (splitAttributes == null) {
            final TaskContainer.TaskProperties taskProperties = splitContainer.getTaskContainer()
                    .getTaskProperties();
            final SplitRule splitRule = splitContainer.getSplitRule();
            final SplitAttributes defaultSplitAttributes = splitContainer
                    .getDefaultSplitAttributes();
            final Pair<Size, Size> minDimensionsPair = splitContainer.getMinDimensionsPair();
        final SplitAttributes splitAttributes = mPresenter.computeSplitAttributes(
                taskContainer.getTaskProperties(), splitRule, minDimensionsPair);
        splitContainer.setSplitAttributes(splitAttributes);
            splitAttributes = mPresenter.computeSplitAttributes(taskProperties, splitRule,
                    defaultSplitAttributes, minDimensionsPair);
        }
        splitContainer.updateCurrentSplitAttributes(splitAttributes);
        if (dismissPlaceholderIfNecessary(wct, splitContainer)) {
            // Placeholder was finished, the positions will be updated when its container is emptied
            return;
            return true;
        }
        mPresenter.updateSplitContainer(splitContainer, container, wct);
        mPresenter.updateSplitContainer(splitContainer, wct);
        return true;
    }

    /** Whether the given split is the topmost split in the Task. */
@@ -1576,7 +1680,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair(activity,
                placeholderRule.getPlaceholderIntent());
        final SplitAttributes splitAttributes = mPresenter.computeSplitAttributes(taskProperties,
                placeholderRule, minDimensionsPair);
                placeholderRule, placeholderRule.getDefaultSplitAttributes(), minDimensionsPair);
        if (!SplitPresenter.shouldShowSplit(splitAttributes)) {
            return false;
        }
@@ -1655,7 +1759,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            // The placeholder should remain after it was first shown.
            return false;
        }
        final SplitAttributes splitAttributes = splitContainer.getSplitAttributes();
        final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes();
        if (SplitPresenter.shouldShowSplit(splitAttributes)) {
            return false;
        }
@@ -1797,6 +1901,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        return null;
    }

    @Nullable
    @GuardedBy("mLock")
    SplitContainer getSplitContainer(@NonNull IBinder token) {
        for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
            final List<SplitContainer> containers = mTaskContainers.valueAt(i).mSplitContainers;
            for (SplitContainer container : containers) {
                if (container.getToken().equals(token)) {
                    return container;
                }
            }
        }
        return null;
    }

    @Nullable
    @GuardedBy("mLock")
    TaskContainer getTaskContainer(int taskId) {
+10 −17
Original line number Diff line number Diff line
@@ -179,7 +179,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair(
                primaryActivity, secondaryIntent);
        final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule,
                minDimensionsPair);
                rule.getDefaultSplitAttributes(), minDimensionsPair);
        final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
                splitAttributes);
        final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
@@ -225,7 +225,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        final Pair<Size, Size> minDimensionsPair = getActivitiesMinDimensionsPair(primaryActivity,
                secondaryActivity);
        final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule,
                minDimensionsPair);
                rule.getDefaultSplitAttributes(), minDimensionsPair);
        final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
                splitAttributes);
        final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
@@ -334,11 +334,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
    /**
     * Updates the positions of containers in an existing split.
     * @param splitContainer The split container to be updated.
     * @param updatedContainer The task fragment that was updated and caused this split update.
     * @param wct WindowContainerTransaction that this update should be performed with.
     */
    void updateSplitContainer(@NonNull SplitContainer splitContainer,
            @NonNull TaskFragmentContainer updatedContainer,
            @NonNull WindowContainerTransaction wct) {
        // Getting the parent configuration using the updated container - it will have the recent
        // value.
@@ -348,8 +346,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        if (activity == null) {
            return;
        }
        final TaskProperties taskProperties = getTaskProperties(updatedContainer);
        final SplitAttributes splitAttributes = splitContainer.getSplitAttributes();
        final TaskContainer taskContainer = splitContainer.getTaskContainer();
        final TaskProperties taskProperties = taskContainer.getTaskProperties();
        final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes();
        final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
                splitAttributes);
        final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties,
@@ -370,7 +369,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
            // When placeholder is shown in split, we should keep the focus on the primary.
            wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
        }
        final TaskContainer taskContainer = updatedContainer.getTaskContainer();
        final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
                primaryRelBounds);
        updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode);
@@ -515,9 +513,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        // Expand the splitContainer if minimum dimensions are not satisfied.
        final TaskContainer taskContainer = splitContainer.getTaskContainer();
        final SplitAttributes splitAttributes = sanitizeSplitAttributes(
                taskContainer.getTaskProperties(), splitContainer.getSplitAttributes(),
                taskContainer.getTaskProperties(), splitContainer.getCurrentSplitAttributes(),
                minDimensionsPair);
        splitContainer.setSplitAttributes(splitAttributes);
        splitContainer.updateCurrentSplitAttributes(splitAttributes);
        if (!shouldShowSplit(splitAttributes)) {
            // If the client side hasn't received TaskFragmentInfo yet, we can't change TaskFragment
            // bounds. Return failure to create a new SplitContainer which fills task bounds.
@@ -540,7 +538,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
    }

    static boolean shouldShowSplit(@NonNull SplitContainer splitContainer) {
        return shouldShowSplit(splitContainer.getSplitAttributes());
        return shouldShowSplit(splitContainer.getCurrentSplitAttributes());
    }

    static boolean shouldShowSplit(@NonNull SplitAttributes splitAttributes) {
@@ -549,12 +547,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {

    @NonNull
    SplitAttributes computeSplitAttributes(@NonNull TaskProperties taskProperties,
            @NonNull SplitRule rule, @Nullable Pair<Size, Size> minDimensionsPair) {
            @NonNull SplitRule rule, @NonNull SplitAttributes defaultSplitAttributes,
            @Nullable Pair<Size, Size> minDimensionsPair) {
        final Configuration taskConfiguration = taskProperties.getConfiguration();
        final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(taskConfiguration);
        final Function<SplitAttributesCalculatorParams, SplitAttributes> calculator =
                mController.getSplitAttributesCalculator();
        final SplitAttributes defaultSplitAttributes = rule.getDefaultSplitAttributes();
        final boolean areDefaultConstraintsSatisfied = rule.checkParentMetrics(taskWindowMetrics);
        if (calculator == null) {
            if (!areDefaultConstraintsSatisfied) {
@@ -956,11 +954,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        return bounds.width() > bounds.height();
    }

    @NonNull
    static TaskProperties getTaskProperties(@NonNull TaskFragmentContainer container) {
        return container.getTaskContainer().getTaskProperties();
    }

    @NonNull
    TaskProperties getTaskProperties(@NonNull Activity activity) {
        final TaskContainer taskContainer = mController.getTaskContainer(
+2 −11
Original line number Diff line number Diff line
@@ -107,12 +107,6 @@ class TaskContainer {
        return mIsVisible;
    }

    @NonNull
    Configuration getConfiguration() {
        // Make a copy in case the config is updated unexpectedly.
        return new Configuration(mConfiguration);
    }

    @NonNull
    TaskProperties getTaskProperties() {
        return new TaskProperties(mDisplayId, mConfiguration);
@@ -157,7 +151,7 @@ class TaskContainer {

    @WindowingMode
    private int getWindowingMode() {
        return getConfiguration().windowConfiguration.getWindowingMode();
        return mConfiguration.windowConfiguration.getWindowingMode();
    }

    /** Whether there is any {@link TaskFragmentContainer} below this Task. */
@@ -220,10 +214,7 @@ class TaskContainer {
        }
    }

    /**
     * A wrapper class which contains the display ID and {@link Configuration} of a
     * {@link TaskContainer}
     */
    /** A wrapper class which contains the information of {@link TaskContainer} */
    static final class TaskProperties {
        private final int mDisplayId;
        @NonNull
+23 −3

File changed.

Preview size limit exceeded, changes collapsed.

Loading