From aa0130145e08aa0153a7a0cf9ff334dbef076c35 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Thu, 12 Jan 2023 20:46:08 +0800 Subject: Avoid duplicated WindowContainerTransaction for TaskFragment Before, when there is Task bounds change without triggering TaskFragment bounds change, we may still trigger a WCT to update adjacentTF that isn't different from the previous request. Record the last request value to avoid duplicated request. Also make sure the requested changes are reset when TaskFragment enters PiP. Bug: 265271880 Test: atest WmTests:TaskFragmentTest Test: atest WMJetpackUnitTests:SplitPresenterTest Change-Id: Ifd9bba2de3e5f2caaa3efd1a6af66f3bcebff7d1 --- .../embedding/JetpackTaskFragmentOrganizer.java | 42 +++++++---- .../extensions/embedding/SplitController.java | 3 + .../extensions/embedding/SplitPresenter.java | 71 ++++++++++++++++-- .../embedding/TaskFragmentContainer.java | 87 ++++++++++++++++++++-- .../extensions/embedding/SplitPresenterTest.java | 58 +++++++++++++++ 5 files changed, 236 insertions(+), 25 deletions(-) (limited to 'libs/WindowManager/Jetpack') diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java index 67963a36713c..d94e8e426c4b 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java @@ -154,7 +154,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { activityOptions); // Set adjacent to each other so that the containers below will be invisible. - setAdjacentTaskFragments(wct, launchingFragmentToken, secondaryFragmentToken, rule); + setAdjacentTaskFragmentsWithRule(wct, launchingFragmentToken, secondaryFragmentToken, rule); setCompanionTaskFragment(wct, launchingFragmentToken, secondaryFragmentToken, rule, false /* isStacked */); } @@ -167,7 +167,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { void expandTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken) { resizeTaskFragment(wct, fragmentToken, new Rect()); - setAdjacentTaskFragments(wct, fragmentToken, null /* secondary */, null /* splitRule */); + clearAdjacentTaskFragments(wct, fragmentToken); updateWindowingMode(wct, fragmentToken, WINDOWING_MODE_UNDEFINED); updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT); } @@ -238,26 +238,37 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { wct.reparentActivityToTaskFragment(fragmentToken, reparentActivityToken); } - void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, - @NonNull IBinder primary, @Nullable IBinder secondary, @Nullable SplitRule splitRule) { - if (secondary == null) { - wct.clearAdjacentTaskFragments(primary); - return; - } - + /** + * Sets the two given TaskFragments as adjacent to each other with respecting the given + * {@link SplitRule} for {@link WindowContainerTransaction.TaskFragmentAdjacentParams}. + */ + void setAdjacentTaskFragmentsWithRule(@NonNull WindowContainerTransaction wct, + @NonNull IBinder primary, @NonNull IBinder secondary, @NonNull SplitRule splitRule) { WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams = null; final boolean finishSecondaryWithPrimary = - splitRule != null && SplitContainer.shouldFinishSecondaryWithPrimary(splitRule); + SplitContainer.shouldFinishSecondaryWithPrimary(splitRule); final boolean finishPrimaryWithSecondary = - splitRule != null && SplitContainer.shouldFinishPrimaryWithSecondary(splitRule); + SplitContainer.shouldFinishPrimaryWithSecondary(splitRule); if (finishSecondaryWithPrimary || finishPrimaryWithSecondary) { adjacentParams = new WindowContainerTransaction.TaskFragmentAdjacentParams(); adjacentParams.setShouldDelayPrimaryLastActivityRemoval(finishSecondaryWithPrimary); adjacentParams.setShouldDelaySecondaryLastActivityRemoval(finishPrimaryWithSecondary); } + setAdjacentTaskFragments(wct, primary, secondary, adjacentParams); + } + + void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, + @NonNull IBinder primary, @NonNull IBinder secondary, + @Nullable WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams) { wct.setAdjacentTaskFragments(primary, secondary, adjacentParams); } + void clearAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, + @NonNull IBinder fragmentToken) { + // Clear primary will also clear secondary. + wct.clearAdjacentTaskFragments(fragmentToken); + } + void setCompanionTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder primary, @NonNull IBinder secondary, @NonNull SplitRule splitRule, boolean isStacked) { @@ -268,7 +279,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { } else { finishPrimaryWithSecondary = shouldFinishPrimaryWithSecondary(splitRule); } - wct.setCompanionTaskFragment(primary, finishPrimaryWithSecondary ? secondary : null); + setCompanionTaskFragment(wct, primary, finishPrimaryWithSecondary ? secondary : null); final boolean finishSecondaryWithPrimary; if (isStacked) { @@ -277,7 +288,12 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { } else { finishSecondaryWithPrimary = shouldFinishSecondaryWithPrimary(splitRule); } - wct.setCompanionTaskFragment(secondary, finishSecondaryWithPrimary ? primary : null); + setCompanionTaskFragment(wct, secondary, finishSecondaryWithPrimary ? primary : null); + } + + void setCompanionTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder primary, + @Nullable IBinder secondary) { + wct.setCompanionTaskFragment(primary, secondary); } void resizeTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, 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 57ba6bbb04f0..2d7fd7e6a085 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -424,6 +424,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // All overrides will be cleanup. container.setLastRequestedBounds(null /* bounds */); container.setLastRequestedWindowingMode(WINDOWING_MODE_UNDEFINED); + container.clearLastAdjacentTaskFragment(); + container.setLastCompanionTaskFragment(null /* fragmentToken */); + container.setLastRequestAnimationParams(TaskFragmentAnimationParams.DEFAULT); cleanupForEnterPip(wct, container); } else if (wasInPip) { // Exit PIP. 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 2b93682d791f..8a21039ffe4e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -387,10 +387,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { // secondaryContainer could not be finished. boolean isStacked = !shouldShowSplit(splitAttributes); if (isStacked) { - setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), - null /* secondary */, null /* splitRule */); + clearAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken()); } else { - setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), + setAdjacentTaskFragmentsWithRule(wct, primaryContainer.getTaskFragmentToken(), secondaryContainer.getTaskFragmentToken(), splitRule); } setCompanionTaskFragment(wct, primaryContainer.getTaskFragmentToken(), @@ -427,7 +426,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { fragmentOptions.getFragmentToken()); if (container == null) { throw new IllegalStateException( - "Creating a task fragment that is not registered with controller."); + "Creating a TaskFragment that is not registered with controller."); } container.setLastRequestedBounds(fragmentOptions.getInitialRelativeBounds()); @@ -441,7 +440,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { TaskFragmentContainer container = mController.getContainer(fragmentToken); if (container == null) { throw new IllegalStateException( - "Resizing a task fragment that is not registered with controller."); + "Resizing a TaskFragment that is not registered with controller."); } if (container.areLastRequestedBoundsEqual(relBounds)) { @@ -458,7 +457,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { @NonNull IBinder fragmentToken, @WindowingMode int windowingMode) { final TaskFragmentContainer container = mController.getContainer(fragmentToken); if (container == null) { - throw new IllegalStateException("Setting windowing mode for a task fragment that is" + throw new IllegalStateException("Setting windowing mode for a TaskFragment that is" + " not registered with controller."); } @@ -476,7 +475,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { @NonNull IBinder fragmentToken, @NonNull TaskFragmentAnimationParams animationParams) { final TaskFragmentContainer container = mController.getContainer(fragmentToken); if (container == null) { - throw new IllegalStateException("Setting animation params for a task fragment that is" + throw new IllegalStateException("Setting animation params for a TaskFragment that is" + " not registered with controller."); } @@ -489,6 +488,64 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { super.updateAnimationParams(wct, fragmentToken, animationParams); } + @Override + void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, + @NonNull IBinder primary, @NonNull IBinder secondary, + @Nullable WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams) { + final TaskFragmentContainer primaryContainer = mController.getContainer(primary); + final TaskFragmentContainer secondaryContainer = mController.getContainer(secondary); + if (primaryContainer == null || secondaryContainer == null) { + throw new IllegalStateException("setAdjacentTaskFragments on TaskFragment that is" + + " not registered with controller."); + } + + if (primaryContainer.isLastAdjacentTaskFragmentEqual(secondary, adjacentParams) + && secondaryContainer.isLastAdjacentTaskFragmentEqual(primary, adjacentParams)) { + // Return early if the same adjacent TaskFragments were already requested + return; + } + + primaryContainer.setLastAdjacentTaskFragment(secondary, adjacentParams); + secondaryContainer.setLastAdjacentTaskFragment(primary, adjacentParams); + super.setAdjacentTaskFragments(wct, primary, secondary, adjacentParams); + } + + @Override + void clearAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, + @NonNull IBinder fragmentToken) { + final TaskFragmentContainer container = mController.getContainer(fragmentToken); + if (container == null) { + throw new IllegalStateException("clearAdjacentTaskFragments on TaskFragment that is" + + " not registered with controller."); + } + + if (container.isLastAdjacentTaskFragmentEqual(null /* fragmentToken*/, null /* params */)) { + // Return early if no adjacent TaskFragment was yet requested + return; + } + + container.clearLastAdjacentTaskFragment(); + super.clearAdjacentTaskFragments(wct, fragmentToken); + } + + @Override + void setCompanionTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder primary, + @Nullable IBinder secondary) { + final TaskFragmentContainer container = mController.getContainer(primary); + if (container == null) { + throw new IllegalStateException("setCompanionTaskFragment on TaskFragment that is" + + " not registered with controller."); + } + + if (container.isLastCompanionTaskFragmentEqual(secondary)) { + // Return early if the same companion TaskFragment was already requested + return; + } + + container.setLastCompanionTaskFragment(secondary); + super.setCompanionTaskFragment(wct, primary, secondary); + } + /** * Expands the split container if the current split bounds are smaller than the Activity or * Intent that is added to the container. 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 38ac719ebbe2..bc082b134acf 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -39,6 +39,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Objects; /** * Client-side container for a stack of activities. Corresponds to an instance of TaskFragment @@ -115,6 +116,27 @@ class TaskFragmentContainer { @NonNull private TaskFragmentAnimationParams mLastAnimationParams = TaskFragmentAnimationParams.DEFAULT; + /** + * TaskFragment token that was requested last via + * {@link android.window.TaskFragmentOperation#OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS}. + */ + @Nullable + private IBinder mLastAdjacentTaskFragment; + + /** + * {@link WindowContainerTransaction.TaskFragmentAdjacentParams} token that was requested last + * via {@link android.window.TaskFragmentOperation#OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS}. + */ + @Nullable + private WindowContainerTransaction.TaskFragmentAdjacentParams mLastAdjacentParams; + + /** + * TaskFragment token that was requested last via + * {@link android.window.TaskFragmentOperation#OP_TYPE_SET_COMPANION_TASK_FRAGMENT}. + */ + @Nullable + private IBinder mLastCompanionTaskFragment; + /** * When the TaskFragment has appeared in server, but is empty, we should remove the TaskFragment * if it is still empty after the timeout. @@ -571,6 +593,7 @@ class TaskFragmentContainer { /** * Checks if last requested bounds are equal to the provided value. * The requested bounds are relative bounds in parent coordinate. + * @see WindowContainerTransaction#setRelativeBounds */ boolean areLastRequestedBoundsEqual(@Nullable Rect relBounds) { return (relBounds == null && mLastRequestedBounds.isEmpty()) @@ -580,6 +603,7 @@ class TaskFragmentContainer { /** * Updates the last requested bounds. * The requested bounds are relative bounds in parent coordinate. + * @see WindowContainerTransaction#setRelativeBounds */ void setLastRequestedBounds(@Nullable Rect relBounds) { if (relBounds == null) { @@ -589,13 +613,9 @@ class TaskFragmentContainer { } } - @NonNull - Rect getLastRequestedBounds() { - return mLastRequestedBounds; - } - /** * Checks if last requested windowing mode is equal to the provided value. + * @see WindowContainerTransaction#setWindowingMode */ boolean isLastRequestedWindowingModeEqual(@WindowingMode int windowingMode) { return mLastRequestedWindowingMode == windowingMode; @@ -603,6 +623,7 @@ class TaskFragmentContainer { /** * Updates the last requested windowing mode. + * @see WindowContainerTransaction#setWindowingMode */ void setLastRequestedWindowingMode(@WindowingMode int windowingModes) { mLastRequestedWindowingMode = windowingModes; @@ -610,6 +631,7 @@ class TaskFragmentContainer { /** * Checks if last requested {@link TaskFragmentAnimationParams} are equal to the provided value. + * @see android.window.TaskFragmentOperation#OP_TYPE_SET_ANIMATION_PARAMS */ boolean areLastRequestedAnimationParamsEqual( @NonNull TaskFragmentAnimationParams animationParams) { @@ -618,11 +640,66 @@ class TaskFragmentContainer { /** * Updates the last requested {@link TaskFragmentAnimationParams}. + * @see android.window.TaskFragmentOperation#OP_TYPE_SET_ANIMATION_PARAMS */ void setLastRequestAnimationParams(@NonNull TaskFragmentAnimationParams animationParams) { mLastAnimationParams = animationParams; } + /** + * Checks if last requested adjacent TaskFragment token and params are equal to the provided + * values. + * @see android.window.TaskFragmentOperation#OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS + * @see android.window.TaskFragmentOperation#OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS + */ + boolean isLastAdjacentTaskFragmentEqual(@Nullable IBinder fragmentToken, + @Nullable WindowContainerTransaction.TaskFragmentAdjacentParams params) { + return Objects.equals(mLastAdjacentTaskFragment, fragmentToken) + && Objects.equals(mLastAdjacentParams, params); + } + + /** + * Updates the last requested adjacent TaskFragment token and params. + * @see android.window.TaskFragmentOperation#OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS + */ + void setLastAdjacentTaskFragment(@NonNull IBinder fragmentToken, + @NonNull WindowContainerTransaction.TaskFragmentAdjacentParams params) { + mLastAdjacentTaskFragment = fragmentToken; + mLastAdjacentParams = params; + } + + /** + * Clears the last requested adjacent TaskFragment token and params. + * @see android.window.TaskFragmentOperation#OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS + */ + void clearLastAdjacentTaskFragment() { + final TaskFragmentContainer lastAdjacentTaskFragment = mLastAdjacentTaskFragment != null + ? mController.getContainer(mLastAdjacentTaskFragment) + : null; + mLastAdjacentTaskFragment = null; + mLastAdjacentParams = null; + if (lastAdjacentTaskFragment != null) { + // Clear the previous adjacent TaskFragment as well. + lastAdjacentTaskFragment.clearLastAdjacentTaskFragment(); + } + } + + /** + * Checks if last requested companion TaskFragment token is equal to the provided value. + * @see android.window.TaskFragmentOperation#OP_TYPE_SET_COMPANION_TASK_FRAGMENT + */ + boolean isLastCompanionTaskFragmentEqual(@Nullable IBinder fragmentToken) { + return Objects.equals(mLastCompanionTaskFragment, fragmentToken); + } + + /** + * Updates the last requested companion TaskFragment token. + * @see android.window.TaskFragmentOperation#OP_TYPE_SET_COMPANION_TASK_FRAGMENT + */ + void setLastCompanionTaskFragment(@Nullable IBinder fragmentToken) { + mLastCompanionTaskFragment = fragmentToken; + } + /** Gets the parent leaf Task id. */ int getTaskId() { return mTaskContainer.getTaskId(); 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 a41e63f46f55..96dd86aff7c0 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 @@ -176,6 +176,64 @@ public class SplitPresenterTest { verify(mTransaction, never()).setWindowingMode(any(), anyInt()); } + @Test + public void testSetAdjacentTaskFragments() { + final TaskFragmentContainer container0 = mController.newContainer(mActivity, TASK_ID); + final TaskFragmentContainer container1 = mController.newContainer(mActivity, TASK_ID); + + mPresenter.setAdjacentTaskFragments(mTransaction, container0.getTaskFragmentToken(), + container1.getTaskFragmentToken(), null /* adjacentParams */); + verify(mTransaction).setAdjacentTaskFragments(container0.getTaskFragmentToken(), + container1.getTaskFragmentToken(), null /* adjacentParams */); + + // No request to set the same adjacent TaskFragments. + clearInvocations(mTransaction); + mPresenter.setAdjacentTaskFragments(mTransaction, container0.getTaskFragmentToken(), + container1.getTaskFragmentToken(), null /* adjacentParams */); + + verify(mTransaction, never()).setAdjacentTaskFragments(any(), any(), any()); + } + + @Test + public void testClearAdjacentTaskFragments() { + final TaskFragmentContainer container0 = mController.newContainer(mActivity, TASK_ID); + final TaskFragmentContainer container1 = mController.newContainer(mActivity, TASK_ID); + + // No request to clear as it is not set by default. + mPresenter.clearAdjacentTaskFragments(mTransaction, container0.getTaskFragmentToken()); + verify(mTransaction, never()).clearAdjacentTaskFragments(any()); + + mPresenter.setAdjacentTaskFragments(mTransaction, container0.getTaskFragmentToken(), + container1.getTaskFragmentToken(), null /* adjacentParams */); + mPresenter.clearAdjacentTaskFragments(mTransaction, container0.getTaskFragmentToken()); + verify(mTransaction).clearAdjacentTaskFragments(container0.getTaskFragmentToken()); + + // No request to clear on either of the previous cleared TasKFragments. + clearInvocations(mTransaction); + mPresenter.clearAdjacentTaskFragments(mTransaction, container0.getTaskFragmentToken()); + mPresenter.clearAdjacentTaskFragments(mTransaction, container1.getTaskFragmentToken()); + + verify(mTransaction, never()).clearAdjacentTaskFragments(any()); + } + + @Test + public void testSetCompanionTaskFragment() { + final TaskFragmentContainer container0 = mController.newContainer(mActivity, TASK_ID); + final TaskFragmentContainer container1 = mController.newContainer(mActivity, TASK_ID); + + mPresenter.setCompanionTaskFragment(mTransaction, container0.getTaskFragmentToken(), + container1.getTaskFragmentToken()); + verify(mTransaction).setCompanionTaskFragment(container0.getTaskFragmentToken(), + container1.getTaskFragmentToken()); + + // No request to set the same adjacent TaskFragments. + clearInvocations(mTransaction); + mPresenter.setCompanionTaskFragment(mTransaction, container0.getTaskFragmentToken(), + container1.getTaskFragmentToken()); + + verify(mTransaction, never()).setCompanionTaskFragment(any(), any()); + } + @Test public void testUpdateAnimationParams() { final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID); -- cgit v1.2.3-59-g8ed1b