diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java index 825c67071a2dd..18497ad249eeb 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java @@ -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. + *

+ * 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. + *

+ * If the {@code SplitAttributes} calculator function is set, the current + * {@code SplitAttributes} will be customized by the function, which can be any + * {@code SplitAttributes}. + *

+ * + * @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. + *

+ * 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 setSplitAttributes(@NonNull SplitAttributes splitAttributes) { - mSplitAttributes = splitAttributes; + void updateCurrentSplitAttributes(@NonNull SplitAttributes splitAttributes) { + mCurrentSplitAttributes = splitAttributes; + } + + /** + * Overrides the default {@link SplitAttributes} to this container, which may be different + * from {@link SplitRule#getDefaultSplitAttributes}. + */ + 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 + "}"; } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index ff582015c4be5..4ed53a859037f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -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 activityStackTokens) { + if (activityStackTokens.isEmpty()) { + return; + } + synchronized (mLock) { + // Translate ActivityStack to TaskFragmentContainer. + final List 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 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 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 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 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(); - final SplitRule splitRule = splitContainer.getSplitRule(); - final Pair minDimensionsPair = splitContainer.getMinDimensionsPair(); - final SplitAttributes splitAttributes = mPresenter.computeSplitAttributes( - taskContainer.getTaskProperties(), splitRule, minDimensionsPair); - splitContainer.setSplitAttributes(splitAttributes); + if (splitAttributes == null) { + final TaskContainer.TaskProperties taskProperties = splitContainer.getTaskContainer() + .getTaskProperties(); + final SplitRule splitRule = splitContainer.getSplitRule(); + final SplitAttributes defaultSplitAttributes = splitContainer + .getDefaultSplitAttributes(); + final Pair minDimensionsPair = splitContainer.getMinDimensionsPair(); + 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 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 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) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 2b93682d791ff..5d51760268654 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -179,7 +179,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { final Pair 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 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 minDimensionsPair) { + @NonNull SplitRule rule, @NonNull SplitAttributes defaultSplitAttributes, + @Nullable Pair minDimensionsPair) { final Configuration taskConfiguration = taskProperties.getConfiguration(); final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(taskConfiguration); final Function 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( diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index f41295b77a0de..4b15bb187035e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -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 diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 38ac719ebbe2e..ccb274a22c7e4 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -37,6 +37,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -436,10 +437,17 @@ class TaskFragmentContainer { * Removes a container that should be finished when this container is finished. */ void removeContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToRemove) { + removeContainersToFinishOnExit(Collections.singletonList(containerToRemove)); + } + + /** + * Removes container list that should be finished when this container is finished. + */ + void removeContainersToFinishOnExit(@NonNull List containersToRemove) { if (mIsFinished) { return; } - mContainersToFinishOnExit.remove(containerToRemove); + mContainersToFinishOnExit.removeAll(containersToRemove); } /** @@ -478,6 +486,16 @@ class TaskFragmentContainer { @GuardedBy("mController.mLock") void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter, @NonNull WindowContainerTransaction wct, @NonNull SplitController controller) { + finish(shouldFinishDependent, presenter, wct, controller, true /* shouldRemoveRecord */); + } + + /** + * Removes all activities that belong to this process and finishes other containers/activities + * configured to finish together. + */ + void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter, + @NonNull WindowContainerTransaction wct, @NonNull SplitController controller, + boolean shouldRemoveRecord) { if (!mIsFinished) { mIsFinished = true; if (mAppearEmptyTimeout != null) { @@ -494,8 +512,10 @@ class TaskFragmentContainer { // Cleanup the visuals presenter.deleteTaskFragment(wct, getTaskFragmentToken()); - // Cleanup the records - controller.removeContainer(this); + if (shouldRemoveRecord) { + // Cleanup the records + controller.removeContainer(this); + } // Clean up task fragment information mInfo = null; } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index a26311efc23eb..17909d4a0763a 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -275,7 +275,8 @@ public class SplitControllerTest { assertNotNull(tf); assertNotNull(taskContainer); - assertEquals(TASK_BOUNDS, taskContainer.getConfiguration().windowConfiguration.getBounds()); + assertEquals(TASK_BOUNDS, taskContainer.getTaskProperties().getConfiguration() + .windowConfiguration.getBounds()); } @Test @@ -288,7 +289,7 @@ public class SplitControllerTest { doReturn(true).when(tf).isEmpty(); doReturn(true).when(mSplitController).launchPlaceholderIfNecessary(mTransaction, mActivity, false /* isOnCreated */); - doNothing().when(mSplitPresenter).updateSplitContainer(any(), any(), any()); + doNothing().when(mSplitPresenter).updateSplitContainer(any(), any()); mSplitController.updateContainer(mTransaction, tf); @@ -341,7 +342,7 @@ public class SplitControllerTest { mSplitController.updateContainer(mTransaction, tf); - verify(mSplitPresenter, never()).updateSplitContainer(any(), any(), any()); + verify(mSplitPresenter, never()).updateSplitContainer(any(), any()); // Verify if the top active split is updated if both of its containers are not finished. doReturn(false).when(mSplitController) @@ -349,7 +350,7 @@ public class SplitControllerTest { mSplitController.updateContainer(mTransaction, tf); - verify(mSplitPresenter).updateSplitContainer(splitContainer, tf, mTransaction); + verify(mSplitPresenter).updateSplitContainer(splitContainer, mTransaction); } @Test @@ -366,14 +367,14 @@ public class SplitControllerTest { doReturn(false).when(taskContainer).isVisible(); mSplitController.updateContainer(mTransaction, taskFragmentContainer); - verify(mSplitPresenter, never()).updateSplitContainer(any(), any(), any()); + verify(mSplitPresenter, never()).updateSplitContainer(any(), any()); // Update the split when the Task is visible. doReturn(true).when(taskContainer).isVisible(); mSplitController.updateContainer(mTransaction, taskFragmentContainer); verify(mSplitPresenter).updateSplitContainer(taskContainer.mSplitContainers.get(0), - taskFragmentContainer, mTransaction); + mTransaction); } @Test diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java index a41e63f46f552..be7c26e9e2fe6 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java @@ -228,7 +228,7 @@ public class SplitPresenterTest { @Test public void testGetRelBoundsForPosition_expandContainers() { - final TaskContainer.TaskProperties taskProperties = getTaskProperty(); + final TaskContainer.TaskProperties taskProperties = getTaskProperties(); final SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(new SplitAttributes.SplitType.ExpandContainersSplitType()) .build(); @@ -248,7 +248,7 @@ public class SplitPresenterTest { @Test public void testGetRelBoundsForPosition_expandContainers_isRelativeToParent() { - final TaskContainer.TaskProperties taskProperties = getTaskProperty( + final TaskContainer.TaskProperties taskProperties = getTaskProperties( new Rect(100, 100, 500, 1000)); final SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(new SplitAttributes.SplitType.ExpandContainersSplitType()) @@ -273,7 +273,7 @@ public class SplitPresenterTest { false /* splitHorizontally */); final Rect secondaryBounds = getSplitBounds(false /* isPrimary */, false /* splitHorizontally */); - final TaskContainer.TaskProperties taskProperties = getTaskProperty(); + final TaskContainer.TaskProperties taskProperties = getTaskProperties(); SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually()) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) @@ -339,7 +339,7 @@ public class SplitPresenterTest { // Offset TaskBounds to 100, 100. The returned rel bounds shouldn't be affected. final Rect taskBounds = new Rect(TASK_BOUNDS); taskBounds.offset(100, 100); - final TaskContainer.TaskProperties taskProperties = getTaskProperty(taskBounds); + final TaskContainer.TaskProperties taskProperties = getTaskProperties(taskBounds); SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually()) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) @@ -400,7 +400,7 @@ public class SplitPresenterTest { true /* splitHorizontally */); final Rect secondaryBounds = getSplitBounds(false /* isPrimary */, true /* splitHorizontally */); - final TaskContainer.TaskProperties taskProperties = getTaskProperty(); + final TaskContainer.TaskProperties taskProperties = getTaskProperties(); SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually()) .setLayoutDirection(SplitAttributes.LayoutDirection.TOP_TO_BOTTOM) @@ -442,7 +442,7 @@ public class SplitPresenterTest { false /* splitHorizontally */); final Rect secondaryBounds = getSplitBounds(false /* isPrimary */, false /* splitHorizontally */); - final TaskContainer.TaskProperties taskProperties = getTaskProperty(); + final TaskContainer.TaskProperties taskProperties = getTaskProperties(); final SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(new SplitAttributes.SplitType.HingeSplitType( SplitAttributes.SplitType.RatioSplitType.splitEqually() @@ -506,7 +506,7 @@ public class SplitPresenterTest { @Test public void testGetRelBoundsForPosition_fallbackToExpandContainers() { - final TaskContainer.TaskProperties taskProperties = getTaskProperty(); + final TaskContainer.TaskProperties taskProperties = getTaskProperties(); final SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(new SplitAttributes.SplitType.HingeSplitType( new SplitAttributes.SplitType.ExpandContainersSplitType() @@ -528,7 +528,7 @@ public class SplitPresenterTest { @Test public void testGetRelBoundsForPosition_useHingeSplitType() { - final TaskContainer.TaskProperties taskProperties = getTaskProperty(); + final TaskContainer.TaskProperties taskProperties = getTaskProperties(); final SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(new SplitAttributes.SplitType.HingeSplitType( new SplitAttributes.SplitType.ExpandContainersSplitType() @@ -581,13 +581,13 @@ public class SplitPresenterTest { splitContainer, mActivity, secondaryActivity, null /* secondaryIntent */)); verify(mPresenter, never()).expandTaskFragment(any(), any()); - splitContainer.setSplitAttributes(SPLIT_ATTRIBUTES); + splitContainer.updateCurrentSplitAttributes(SPLIT_ATTRIBUTES); doReturn(createActivityInfoWithMinDimensions()).when(secondaryActivity).getActivityInfo(); assertEquals(RESULT_EXPAND_FAILED_NO_TF_INFO, mPresenter.expandSplitContainerIfNeeded( mTransaction, splitContainer, mActivity, secondaryActivity, null /* secondaryIntent */)); - splitContainer.setSplitAttributes(SPLIT_ATTRIBUTES); + splitContainer.updateCurrentSplitAttributes(SPLIT_ATTRIBUTES); primaryTf.setInfo(mTransaction, createMockTaskFragmentInfo(primaryTf, mActivity)); secondaryTf.setInfo(mTransaction, createMockTaskFragmentInfo(secondaryTf, secondaryActivity)); @@ -597,7 +597,7 @@ public class SplitPresenterTest { verify(mPresenter).expandTaskFragment(mTransaction, primaryTf.getTaskFragmentToken()); verify(mPresenter).expandTaskFragment(mTransaction, secondaryTf.getTaskFragmentToken()); - splitContainer.setSplitAttributes(SPLIT_ATTRIBUTES); + splitContainer.updateCurrentSplitAttributes(SPLIT_ATTRIBUTES); clearInvocations(mPresenter); assertEquals(RESULT_EXPANDED, mPresenter.expandSplitContainerIfNeeded(mTransaction, @@ -639,37 +639,35 @@ public class SplitPresenterTest { .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY) .setDefaultSplitAttributes(SPLIT_ATTRIBUTES) .build(); - final TaskContainer.TaskProperties taskProperties = getTaskProperty(); + final TaskContainer.TaskProperties taskProperties = getTaskProperties(); assertEquals(SPLIT_ATTRIBUTES, mPresenter.computeSplitAttributes(taskProperties, - splitPairRule, null /* minDimensionsPair */)); + splitPairRule, SPLIT_ATTRIBUTES, null /* minDimensionsPair */)); final Pair minDimensionsPair = new Pair<>( new Size(TASK_BOUNDS.width(), TASK_BOUNDS.height()), null); assertEquals(EXPAND_CONTAINERS_ATTRIBUTES, mPresenter.computeSplitAttributes(taskProperties, - splitPairRule, minDimensionsPair)); + splitPairRule, SPLIT_ATTRIBUTES, minDimensionsPair)); taskProperties.getConfiguration().windowConfiguration.setBounds(new Rect( TASK_BOUNDS.left + 1, TASK_BOUNDS.top + 1, TASK_BOUNDS.right + 1, TASK_BOUNDS.bottom + 1)); assertEquals(EXPAND_CONTAINERS_ATTRIBUTES, mPresenter.computeSplitAttributes(taskProperties, - splitPairRule, null /* minDimensionsPair */)); + splitPairRule, SPLIT_ATTRIBUTES, null /* minDimensionsPair */)); final SplitAttributes splitAttributes = new SplitAttributes.Builder() - .setSplitType( - new SplitAttributes.SplitType.HingeSplitType( - SplitAttributes.SplitType.RatioSplitType.splitEqually() - ) - ).build(); + .setSplitType(new SplitAttributes.SplitType.HingeSplitType( + SplitAttributes.SplitType.RatioSplitType.splitEqually())) + .build(); final Function calculator = params -> splitAttributes; mController.setSplitAttributesCalculator(calculator); assertEquals(splitAttributes, mPresenter.computeSplitAttributes(taskProperties, - splitPairRule, null /* minDimensionsPair */)); + splitPairRule, SPLIT_ATTRIBUTES, null /* minDimensionsPair */)); } @Test @@ -696,14 +694,15 @@ public class SplitPresenterTest { doReturn(activityConfig).when(mActivityResources).getConfiguration(); doReturn(new ActivityInfo()).when(activity).getActivityInfo(); doReturn(mock(IBinder.class)).when(activity).getActivityToken(); + doReturn(TASK_ID).when(activity).getTaskId(); return activity; } - private static TaskContainer.TaskProperties getTaskProperty() { - return getTaskProperty(TASK_BOUNDS); + private static TaskContainer.TaskProperties getTaskProperties() { + return getTaskProperties(TASK_BOUNDS); } - private static TaskContainer.TaskProperties getTaskProperty(@NonNull Rect taskBounds) { + private static TaskContainer.TaskProperties getTaskProperties(@NonNull Rect taskBounds) { final Configuration configuration = new Configuration(); configuration.windowConfiguration.setBounds(taskBounds); return new TaskContainer.TaskProperties(DEFAULT_DISPLAY, configuration); diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar index 7a6f46cdd7d9d..378ad811bd22a 100644 Binary files a/libs/WindowManager/Jetpack/window-extensions-release.aar and b/libs/WindowManager/Jetpack/window-extensions-release.aar differ