From 1ba311c20a421664f37f1b5f65fa1b887bf05884 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Wed, 10 Aug 2022 18:29:51 +0800 Subject: [PATCH] Include embedding activity change in transition When enter ActivityEmbedding split, we will reparent the fill parent activity to an embedded TaskFragment of different size. Before, because the embedded TaskFragment is also a change target, the activity will be excluded from the change targets. Now, we also include such activity so that we can animate the transition of it. Bug: 207070762 Test: atest WmTests:TransitionTests#testIncludeEmbeddedActivityReparent Change-Id: I2de3a6e9ba2f74e455c56abd24010fff2c51e53d --- .../com/android/server/wm/Transition.java | 28 ++++++++++----- .../android/server/wm/TransitionTests.java | 35 +++++++++++++++++++ 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index bbc95a1dd70f6..471319e956a1d 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1192,7 +1192,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe return false; } - final @TransitionInfo.TransitionMode int mode = changes.get(target).getTransitMode(target); + final ChangeInfo change = changes.get(target); + if (change.mStartParent != null && target.getParent() != change.mStartParent) { + // When a window is reparented, the state change won't fit into any of the parents. + // Don't promote such change so that we can animate the reparent if needed. + return false; + } + + final @TransitionInfo.TransitionMode int mode = change.getTransitMode(target); for (int i = parent.getChildCount() - 1; i >= 0; --i) { final WindowContainer sibling = parent.getChildAt(i); if (target == sibling) continue; @@ -1332,14 +1339,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // Intermediate parents must be those that has window to be managed by Shell. continue; } - if (parentChange.mParent != null && !skipIntermediateReports) { - changes.get(wc).mParent = p; + if (parentChange.mEndParent != null && !skipIntermediateReports) { + changes.get(wc).mEndParent = p; // The chain above the parent was processed. break; } if (targetList.contains(p)) { if (skipIntermediateReports) { - changes.get(wc).mParent = p; + changes.get(wc).mEndParent = p; } else { intermediates.add(p); } @@ -1351,10 +1358,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } if (!foundParentInTargets || intermediates.isEmpty()) continue; // Add any always-report parents along the way. - changes.get(wc).mParent = intermediates.get(0); + changes.get(wc).mEndParent = intermediates.get(0); for (int j = 0; j < intermediates.size() - 1; j++) { final WindowContainer intermediate = intermediates.get(j); - changes.get(intermediate).mParent = intermediates.get(j + 1); + changes.get(intermediate).mEndParent = intermediates.get(j + 1); targets.add(intermediate); } } @@ -1467,8 +1474,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe target.mRemoteToken != null ? target.mRemoteToken.toWindowContainerToken() : null, getLeashSurface(target, startT)); // TODO(shell-transitions): Use leash for non-organized windows. - if (info.mParent != null) { - change.setParent(info.mParent.mRemoteToken.toWindowContainerToken()); + if (info.mEndParent != null) { + change.setParent(info.mEndParent.mRemoteToken.toWindowContainerToken()); } change.setMode(info.getTransitMode(target)); change.setStartAbsBounds(info.mAbsoluteBounds); @@ -1651,7 +1658,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe @interface Flag {} // Usually "post" change state. - WindowContainer mParent; + WindowContainer mEndParent; + // Parent before change state. + WindowContainer mStartParent; // State tracking boolean mExistenceChanged = false; @@ -1672,6 +1681,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mAbsoluteBounds.set(origState.getBounds()); mShowWallpaper = origState.showWallpaper(); mRotation = origState.getWindowConfiguration().getRotation(); + mStartParent = origState.getParent(); } @VisibleForTesting diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index a1ad07adbafb9..0f13fb20a06e2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -36,8 +36,10 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static android.window.TransitionInfo.isIndependent; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -1079,6 +1081,39 @@ public class TransitionTests extends WindowTestsBase { assertTrue((info.getChanges().get(1).getFlags() & FLAG_IS_EMBEDDED) != 0); } + @Test + public void testIncludeEmbeddedActivityReparent() { + final Transition transition = createTestTransition(TRANSIT_OPEN); + final Task task = createTask(mDisplayContent); + task.setBounds(new Rect(0, 0, 2000, 1000)); + final ActivityRecord activity = createActivityRecord(task); + activity.mVisibleRequested = true; + // Skip manipulate the SurfaceControl. + doNothing().when(activity).setDropInputMode(anyInt()); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + mAtm.mTaskFragmentOrganizerController.registerOrganizer( + ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder())); + final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setOrganizer(organizer) + .build(); + // TaskFragment with different bounds from Task. + embeddedTf.setBounds(new Rect(0, 0, 1000, 1000)); + + // Start states. + transition.collect(activity); + transition.collectExistenceChange(embeddedTf); + + // End states. + activity.reparent(embeddedTf, POSITION_TOP); + + // Verify that both activity and TaskFragment are included. + final ArrayList targets = Transition.calculateTargets( + transition.mParticipants, transition.mChanges); + assertTrue(targets.contains(embeddedTf)); + assertTrue(targets.contains(activity)); + } + private static void makeTaskOrganized(Task... tasks) { final ITaskOrganizer organizer = mock(ITaskOrganizer.class); for (Task t : tasks) {