Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +56 −12 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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; Loading @@ -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)) { Loading Loading @@ -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 Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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 + "}"; } } libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +165 −47 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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, Loading Loading @@ -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); } } Loading @@ -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); } } Loading Loading @@ -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. */ Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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) { Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +10 −17 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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, Loading Loading @@ -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. Loading @@ -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, Loading @@ -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); Loading Loading @@ -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. Loading @@ -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) { Loading @@ -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) { Loading Loading @@ -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( Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +2 −11 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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. */ Loading Loading @@ -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 Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +23 −3 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +56 −12 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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; Loading @@ -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)) { Loading Loading @@ -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 Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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 + "}"; } }
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +165 −47 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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, Loading Loading @@ -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); } } Loading @@ -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); } } Loading Loading @@ -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. */ Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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) { Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +10 −17 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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, Loading Loading @@ -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. Loading @@ -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, Loading @@ -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); Loading Loading @@ -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. Loading @@ -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) { Loading @@ -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) { Loading Loading @@ -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( Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +2 −11 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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. */ Loading Loading @@ -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 Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +23 −3 File changed.Preview size limit exceeded, changes collapsed. Show changes