diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 89cad9c534647..6c6fbee885f22 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2098,6 +2098,7 @@ class RootWindowContainer extends WindowContainer // from the client organizer, so the PIP activity can get the correct config // from the Task, and prevent conflict with the PipTaskOrganizer. tf.updateRequestedOverrideConfiguration(EMPTY); + tf.updateRelativeEmbeddedBounds(); } }); rootTask.setWindowingMode(WINDOWING_MODE_PINNED); diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 7eb9c16a25ff4..454f338045d11 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -299,6 +299,14 @@ class TaskFragment extends WindowContainer { @Nullable private final IBinder mFragmentToken; + /** + * The bounds of the embedded TaskFragment relative to the parent Task. + * {@code null} if it is not {@link #mIsEmbedded} + * TODO(b/261785978) cleanup with legacy app transition + */ + @Nullable + private final Rect mRelativeEmbeddedBounds; + /** * Whether to delay the call to {@link #updateOrganizedTaskFragmentSurface()} when there is a * configuration change. @@ -381,6 +389,7 @@ class TaskFragment extends WindowContainer { mRootWindowContainer = mAtmService.mRootWindowContainer; mCreatedByOrganizer = createdByOrganizer; mIsEmbedded = isEmbedded; + mRelativeEmbeddedBounds = isEmbedded ? new Rect() : null; mTaskFragmentOrganizerController = mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController; mFragmentToken = fragmentToken; @@ -2419,16 +2428,53 @@ class TaskFragment extends WindowContainer { } } - /** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */ - boolean shouldStartChangeTransition(Rect startBounds) { + /** + * Gets the relative bounds of this embedded TaskFragment. This should only be called on + * embedded TaskFragment. + */ + @NonNull + Rect getRelativeEmbeddedBounds() { + if (mRelativeEmbeddedBounds == null) { + throw new IllegalStateException("The TaskFragment is not embedded"); + } + return mRelativeEmbeddedBounds; + } + + /** + * Updates the record of the relative bounds of this embedded TaskFragment. This should only be + * called when the embedded TaskFragment's override bounds are changed. + * Returns {@code true} if the bounds is changed. + */ + void updateRelativeEmbeddedBounds() { + // We only record the override bounds, which means it will not be changed when it is filling + // Task, and resize with the parent. + getRequestedOverrideBounds(mTmpBounds); + getRelativePosition(mTmpPoint); + mTmpBounds.offsetTo(mTmpPoint.x, mTmpPoint.y); + mRelativeEmbeddedBounds.set(mTmpBounds); + } + + /** + * Updates the record of relative bounds of this embedded TaskFragment, and checks whether we + * should prepare a transition for the bounds change. + */ + boolean shouldStartChangeTransition(@NonNull Rect absStartBounds, + @NonNull Rect relStartBounds) { if (mTaskFragmentOrganizer == null || !canStartChangeTransition()) { return false; } - // Only take snapshot if the bounds are resized. - final Rect endBounds = getConfiguration().windowConfiguration.getBounds(); - return endBounds.width() != startBounds.width() - || endBounds.height() != startBounds.height(); + if (mTransitionController.isShellTransitionsEnabled()) { + // For Shell transition, the change will be collected anyway, so only take snapshot when + // the bounds are resized. + final Rect endBounds = getConfiguration().windowConfiguration.getBounds(); + return endBounds.width() != absStartBounds.width() + || endBounds.height() != absStartBounds.height(); + } else { + // For legacy transition, we need to trigger a change transition as long as the bounds + // is changed, even if it is not resized. + return !relStartBounds.equals(mRelativeEmbeddedBounds); + } } /** Records the starting bounds of the closing organized TaskFragment. */ diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 69c4297ce717c..dc1d50c435b91 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -149,7 +149,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub @VisibleForTesting final ArrayMap mLaunchTaskFragments = new ArrayMap<>(); - private final Rect mTmpBounds = new Rect(); + private final Rect mTmpBounds0 = new Rect(); + private final Rect mTmpBounds1 = new Rect(); WindowOrganizerController(ActivityTaskManagerService atm) { mService = atm; @@ -804,14 +805,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // When the TaskFragment is resized, we may want to create a change transition for it, for // which we want to defer the surface update until we determine whether or not to start // change transition. - mTmpBounds.set(taskFragment.getBounds()); + mTmpBounds0.set(taskFragment.getBounds()); + mTmpBounds1.set(taskFragment.getRelativeEmbeddedBounds()); taskFragment.deferOrganizedTaskFragmentSurfaceUpdate(); final int effects = applyChanges(taskFragment, c, errorCallbackToken); - if (taskFragment.shouldStartChangeTransition(mTmpBounds)) { - taskFragment.initializeChangeTransition(mTmpBounds); + taskFragment.updateRelativeEmbeddedBounds(); + if (taskFragment.shouldStartChangeTransition(mTmpBounds0, mTmpBounds1)) { + taskFragment.initializeChangeTransition(mTmpBounds0); } taskFragment.continueOrganizedTaskFragmentSurfaceUpdate(); - mTmpBounds.set(0, 0, 0, 0); return effects; } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index 38a712ed04355..11ac929327b9c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -106,19 +106,50 @@ public class TaskFragmentTest extends WindowTestsBase { verify(mTransaction).setWindowCrop(mLeash, 1000, 1000); } + @Test + public void testShouldStartChangeTransition_relativePositionChange() { + mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer); + final Rect startBounds = new Rect(0, 0, 500, 1000); + final Rect endBounds = new Rect(500, 0, 1000, 1000); + mTaskFragment.setBounds(startBounds); + mTaskFragment.updateRelativeEmbeddedBounds(); + doReturn(true).when(mTaskFragment).isVisible(); + doReturn(true).when(mTaskFragment).isVisibleRequested(); + + // Do not resize, just change the relative position. + final Rect relStartBounds = new Rect(mTaskFragment.getRelativeEmbeddedBounds()); + mTaskFragment.setBounds(endBounds); + mTaskFragment.updateRelativeEmbeddedBounds(); + spyOn(mDisplayContent.mTransitionController); + + // For Shell transition, we don't want to take snapshot when the bounds are not resized + doReturn(true).when(mDisplayContent.mTransitionController) + .isShellTransitionsEnabled(); + assertFalse(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds)); + + // For legacy transition, we want to request a change transition even if it is just relative + // bounds change. + doReturn(false).when(mDisplayContent.mTransitionController) + .isShellTransitionsEnabled(); + assertTrue(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds)); + } + @Test public void testStartChangeTransition_resetSurface() { mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer); final Rect startBounds = new Rect(0, 0, 1000, 1000); final Rect endBounds = new Rect(500, 500, 1000, 1000); mTaskFragment.setBounds(startBounds); + mTaskFragment.updateRelativeEmbeddedBounds(); doReturn(true).when(mTaskFragment).isVisible(); doReturn(true).when(mTaskFragment).isVisibleRequested(); clearInvocations(mTransaction); + final Rect relStartBounds = new Rect(mTaskFragment.getRelativeEmbeddedBounds()); mTaskFragment.deferOrganizedTaskFragmentSurfaceUpdate(); mTaskFragment.setBounds(endBounds); - assertTrue(mTaskFragment.shouldStartChangeTransition(startBounds)); + mTaskFragment.updateRelativeEmbeddedBounds(); + assertTrue(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds)); mTaskFragment.initializeChangeTransition(startBounds); mTaskFragment.continueOrganizedTaskFragmentSurfaceUpdate(); @@ -157,17 +188,20 @@ public class TaskFragmentTest extends WindowTestsBase { final Rect startBounds = new Rect(0, 0, 1000, 1000); final Rect endBounds = new Rect(500, 500, 1000, 1000); mTaskFragment.setBounds(startBounds); + mTaskFragment.updateRelativeEmbeddedBounds(); doReturn(true).when(mTaskFragment).isVisible(); doReturn(true).when(mTaskFragment).isVisibleRequested(); + final Rect relStartBounds = new Rect(mTaskFragment.getRelativeEmbeddedBounds()); final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); displayPolicy.screenTurnedOff(); assertFalse(mTaskFragment.okToAnimate()); mTaskFragment.setBounds(endBounds); + mTaskFragment.updateRelativeEmbeddedBounds(); - assertFalse(mTaskFragment.shouldStartChangeTransition(startBounds)); + assertFalse(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds)); } /**