diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index f01e2e809c210..c6d9ebab297d9 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -3139,6 +3139,12 @@ "group": "WM_DEBUG_LOCKTASK", "at": "com\/android\/server\/wm\/LockTaskController.java" }, + "956467125": { + "message": "Reparenting Activity to embedded TaskFragment, but the Activity is not collected", + "level": "WARN", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/WindowOrganizerController.java" + }, "958338552": { "message": "grantEmbeddedWindowFocus win=%s dropped focus so setting focus to null since no candidate was found", "level": "VERBOSE", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index dcd6277966dd2..0bec54399dd84 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -483,11 +483,11 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { postStartTransactionCallbacks.add(t -> startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish, mTransactionPool, mMainExecutor, mAnimExecutor, - null /* position */, cornerRadius, clipRect)); + change.getEndRelOffset(), cornerRadius, clipRect)); } else { startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish, - mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */, - cornerRadius, clipRect); + mTransactionPool, mMainExecutor, mAnimExecutor, + change.getEndRelOffset(), cornerRadius, clipRect); } if (info.getAnimationOptions() != null) { @@ -934,7 +934,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { a.restrictDuration(MAX_ANIMATION_DURATION); a.scaleCurrentDuration(mTransitionAnimationScaleSetting); startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool, - mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top), + mMainExecutor, mAnimExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds()); } @@ -959,7 +959,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { a.restrictDuration(MAX_ANIMATION_DURATION); a.scaleCurrentDuration(mTransitionAnimationScaleSetting); startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool, - mMainExecutor, mAnimExecutor, null /* position */, + mMainExecutor, mAnimExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds()); } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 890b910251514..5b664168e0e2d 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2973,6 +2973,10 @@ class ActivityStarter { newParent = candidateTf; } } + if (newParent.canHaveEmbeddingActivityTransition(mStartActivity)) { + // Make sure the embedded TaskFragment is included in the start activity transition. + newParent.collectEmbeddedTaskFragmentIfNeeded(); + } if (mStartActivity.getTaskFragment() == null || mStartActivity.getTaskFragment() == newParent) { newParent.addChild(mStartActivity, POSITION_TOP); diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index f8a9d4665acca..68884247df97b 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -2316,6 +2316,26 @@ class TaskFragment extends WindowContainer { return !startBounds.equals(getBounds()); } + boolean canHaveEmbeddingActivityTransition(@NonNull ActivityRecord child) { + if (!isOrganizedTaskFragment() || !mTransitionController.isShellTransitionsEnabled()) { + return false; + } + // The activity should request open transition when it is becoming visible. + return child.isVisibleRequested(); + } + + void collectEmbeddedTaskFragmentIfNeeded() { + if (!isOrganizedTaskFragment() || mTransitionController.isCollecting(this)) { + return; + } + if (getChildCount() == 0) { + // The TaskFragment is new created, and just becoming non-empty. + mTransitionController.collectExistenceChange(this); + } else { + mTransitionController.collect(this); + } + } + @Override void setSurfaceControl(SurfaceControl sc) { super.setSurfaceControl(sc); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 91f69a55d4000..31d8eb8eea170 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1149,6 +1149,26 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe return false; } + private static boolean isTranslucent(@NonNull WindowContainer wc) { + final TaskFragment taskFragment = wc.asTaskFragment(); + if (taskFragment != null) { + if (taskFragment.isTranslucent(null /* starting */)) { + return true; + } + final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment(); + if (adjacentTaskFragment != null) { + // Treat the TaskFragment as translucent if its adjacent TF is, otherwise everything + // behind two adjacent TaskFragments are occluded. + return adjacentTaskFragment.isTranslucent(null /* starting */); + } + } + // TODO(b/172695805): hierarchical check. This is non-trivial because for containers + // it is effected by child visibility but needs to work even + // before visibility is committed. This means refactoring some + // checks to use requested visibility. + return !wc.fillsParent(); + } + /** * Under some conditions (eg. all visible targets within a parent container are transitioning * the same way) the transition can be "promoted" to the parent container. This means an @@ -1701,20 +1721,13 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe if (mShowWallpaper || wc.showWallpaper()) { flags |= FLAG_SHOW_WALLPAPER; } - if (!wc.fillsParent()) { - // TODO(b/172695805): hierarchical check. This is non-trivial because for containers - // it is effected by child visibility but needs to work even - // before visibility is committed. This means refactoring some - // checks to use requested visibility. + if (isTranslucent(wc)) { flags |= FLAG_TRANSLUCENT; } final Task task = wc.asTask(); if (task != null && task.voiceSession != null) { flags |= FLAG_IS_VOICE_INTERACTION; } - if (task != null && task.isTranslucent(null)) { - flags |= FLAG_TRANSLUCENT; - } final ActivityRecord record = wc.asActivityRecord(); if (record != null) { if (record.mUseTransferredAnimation) { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index ee6435493699c..2a4360d2a9e94 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -21,6 +21,7 @@ import static android.app.ActivityManager.isStartResultSuccessful; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.TRANSIT_CHANGE; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT; @@ -837,6 +838,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } + prepareActivityEmbeddingTransitionForReparentActivityToTaskFragment(parent, + activity); activity.reparent(parent, POSITION_TOP); effects |= TRANSACT_EFFECTS_LIFECYCLE; break; @@ -1061,6 +1064,41 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return effects; } + private void prepareActivityEmbeddingTransitionForReparentActivityToTaskFragment( + @NonNull TaskFragment taskFragment, @NonNull ActivityRecord activity) { + if (!taskFragment.canHaveEmbeddingActivityTransition(activity)) { + return; + } + + // The reparent can happen in the following cases: + // 1. Reparent an existing activity to split when app launches new intent. + // - This happens after app calls to start activity, but before the activity is actually + // started, so we don't expect any collecting transition, but if it does, we can't + // queue the WCT because the start activity won't wait. + // 2. Reparent an existing activity to split to launch placeholder when Task size changed. + // - We expect to have a collecting transition for the Task resize, so just collect. + // 3. Reparent a new launching activity to an always-expand container. + // 4. Reparent a new launching activity to split to launch placeholder together. + // 5. Reparent a new launching activity to an existing split. + // - The new launching activity should have start an OPEN transition, so just collect. + // 6. Reparent PiP activity back to the original Task. + // - This should be part of the exiting PiP transition, so just collect. + + if (!taskFragment.getBounds().equals(activity.getBounds()) && activity.isVisible() + && !mTransitionController.isCollecting()) { + // 1. Reparent an existing activity to split when app launches new intent. + mTransitionController.requestTransitionIfNeeded(TRANSIT_CHANGE, activity); + } + + // We expect the activity to be in the transition already, so just collect the TaskFragment. + if (mTransitionController.isCollecting(activity)) { + taskFragment.collectEmbeddedTaskFragmentIfNeeded(); + } else { + ProtoLog.w(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Reparenting Activity" + + " to embedded TaskFragment, but the Activity is not collected"); + } + } + /** A helper method to send minimum dimension violation error to the client. */ private void sendMinimumDimensionViolation(TaskFragment taskFragment, Point minDimensions, IBinder errorCallbackToken, String reason) {