From b6a5bf31ce90496bec330e97a782b674ff3e2b33 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Wed, 2 Mar 2022 22:45:55 +0800 Subject: [PATCH] Transfer PipContentOverlay when swipe to home with Shell transition Swipe to home animation is played by launcher, but the entering PIP transition will finished by PipTransition. As a result, we need to reparent the overlay and fade out the overlay in PipTransition when enter PIP is finalized, otherwise the overlay will be removed with the remote transition leash and cause flicker. Bug: 222030101 Test: verify with swipe to home with Shell transition Change-Id: Idde3414e3cc5e8193cf7d4da9818e59dc6b4b1c5 --- .../android/wm/shell/ShellTaskOrganizer.java | 27 ++++++++++- .../src/com/android/wm/shell/TaskView.java | 14 +++++- .../android/wm/shell/apppairs/AppPair.java | 16 +++++-- .../shell/freeform/FreeformTaskListener.java | 18 +++++++ .../fullscreen/FullscreenTaskListener.java | 12 ++++- .../LegacySplitScreenTaskListener.java | 12 ++++- .../src/com/android/wm/shell/pip/IPip.aidl | 5 +- .../wm/shell/pip/PipTaskOrganizer.java | 48 ++++++++++++++++--- .../android/wm/shell/pip/PipTransition.java | 17 +++++-- .../wm/shell/pip/phone/PipController.java | 11 +++-- .../shell/splitscreen/StageTaskListener.java | 14 +++++- .../shell/stagesplit/StageTaskListener.java | 14 +++++- 12 files changed, 178 insertions(+), 30 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index 91ea436f81b6a..3f8343a7abd39 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -98,16 +98,22 @@ public class ShellTaskOrganizer extends TaskOrganizer implements default void onTaskInfoChanged(RunningTaskInfo taskInfo) {} default void onTaskVanished(RunningTaskInfo taskInfo) {} default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {} - /** Whether this task listener supports compat UI. */ + /** Whether this task listener supports compat UI. */ default boolean supportCompatUI() { // All TaskListeners should support compat UI except PIP. return true; } - /** Attaches the a child window surface to the task surface. */ + /** Attaches a child window surface to the task surface. */ default void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { throw new IllegalStateException( "This task listener doesn't support child surface attachment."); } + /** Reparents a child window surface to the task surface. */ + default void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, + SurfaceControl.Transaction t) { + throw new IllegalStateException( + "This task listener doesn't support child surface reparent."); + } default void dump(@NonNull PrintWriter pw, String prefix) {}; } @@ -620,6 +626,23 @@ public class ShellTaskOrganizer extends TaskOrganizer implements updateCameraCompatControlState(info.getTaskInfo().token, state); } + /** Reparents a child window surface to the task surface. */ + public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, + SurfaceControl.Transaction t) { + final TaskListener taskListener; + synchronized (mLock) { + taskListener = mTasks.contains(taskId) + ? getTaskListener(mTasks.get(taskId).getTaskInfo()) + : null; + } + if (taskListener == null) { + ProtoLog.w(WM_SHELL_TASK_ORG, "Failed to find Task to reparent surface taskId=%d", + taskId); + return; + } + taskListener.reparentChildSurfaceToTask(taskId, sc, t); + } + private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info, int event) { ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java index 54e743f72cc6b..ca4ef07eb1db5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java @@ -367,10 +367,20 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, @Override public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { - if (mTaskInfo.taskId != taskId) { + b.setParent(findTaskSurface(taskId)); + } + + @Override + public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, + SurfaceControl.Transaction t) { + t.reparent(sc, findTaskSurface(taskId)); + } + + private SurfaceControl findTaskSurface(int taskId) { + if (mTaskInfo == null || mTaskLeash == null || mTaskInfo.taskId != taskId) { throw new IllegalArgumentException("There is no surface for taskId=" + taskId); } - b.setParent(mTaskLeash); + return mTaskLeash; } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index e344c3bea42b1..33eec335bca33 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -275,12 +275,22 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou @Override public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { + b.setParent(findTaskSurface(taskId)); + } + + @Override + public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, + SurfaceControl.Transaction t) { + t.reparent(sc, findTaskSurface(taskId)); + } + + private SurfaceControl findTaskSurface(int taskId) { if (getRootTaskId() == taskId) { - b.setParent(mRootTaskLeash); + return mRootTaskLeash; } else if (getTaskId1() == taskId) { - b.setParent(mTaskLeash1); + return mTaskLeash1; } else if (getTaskId2() == taskId) { - b.setParent(mTaskLeash2); + return mTaskLeash2; } else { throw new IllegalArgumentException("There is no surface for taskId=" + taskId); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java index 52ff21bc3172f..fef9be36a35f7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java @@ -109,6 +109,24 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener { }); } + @Override + public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { + b.setParent(findTaskSurface(taskId)); + } + + @Override + public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, + SurfaceControl.Transaction t) { + t.reparent(sc, findTaskSurface(taskId)); + } + + private SurfaceControl findTaskSurface(int taskId) { + if (!mTasks.contains(taskId)) { + throw new IllegalArgumentException("There is no surface for taskId=" + taskId); + } + return mTasks.get(taskId).mLeash; + } + @Override public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java index 6e38e421d4b61..73e6cba43ec02 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java @@ -133,10 +133,20 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { @Override public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { + b.setParent(findTaskSurface(taskId)); + } + + @Override + public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, + SurfaceControl.Transaction t) { + t.reparent(sc, findTaskSurface(taskId)); + } + + private SurfaceControl findTaskSurface(int taskId) { if (!mDataByTaskId.contains(taskId)) { throw new IllegalArgumentException("There is no surface for taskId=" + taskId); } - b.setParent(mDataByTaskId.get(taskId).surface); + return mDataByTaskId.get(taskId).surface; } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java index 86bf3ff1cea98..d2f42c39acd5f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java @@ -343,10 +343,20 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { @Override public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { + b.setParent(findTaskSurface(taskId)); + } + + @Override + public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, + SurfaceControl.Transaction t) { + t.reparent(sc, findTaskSurface(taskId)); + } + + private SurfaceControl findTaskSurface(int taskId) { if (!mLeashByTaskId.contains(taskId)) { throw new IllegalArgumentException("There is no surface for taskId=" + taskId); } - b.setParent(mLeashByTaskId.get(taskId)); + return mLeashByTaskId.get(taskId); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl index ddc85f758916a..e03421dd58ac3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl @@ -47,12 +47,13 @@ interface IPip { /** * Notifies the swiping Activity to PiP onto home transition is finished * + * @param taskId the Task id that the Activity and overlay are currently in. * @param componentName ComponentName represents the Activity * @param destinationBounds the destination bounds the PiP window lands into * @param overlay an optional overlay to fade out after entering PiP */ - oneway void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds, - in SurfaceControl overlay) = 2; + oneway void stopSwipePipToHome(int taskId, in ComponentName componentName, + in Rect destinationBounds, in SurfaceControl overlay) = 2; /** * Sets listener to get pinned stack animation callbacks. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 1eb9501ac076c..5becdfd2b07c6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -249,7 +249,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * An optional overlay used to mask content changing between an app in/out of PiP, only set if * {@link PipTransitionState#getInSwipePipToHomeTransition()} is true. */ - private SurfaceControl mSwipePipToHomeOverlay; + @Nullable + SurfaceControl mSwipePipToHomeOverlay; public PipTaskOrganizer(Context context, @NonNull SyncTransactionQueue syncTransactionQueue, @@ -356,12 +357,24 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * Callback when launcher finishes swipe-pip-to-home operation. * Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards. */ - public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds, + public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds, SurfaceControl overlay) { // do nothing if there is no startSwipePipToHome being called before - if (mPipTransitionState.getInSwipePipToHomeTransition()) { - mPipBoundsState.setBounds(destinationBounds); - mSwipePipToHomeOverlay = overlay; + if (!mPipTransitionState.getInSwipePipToHomeTransition()) { + return; + } + mPipBoundsState.setBounds(destinationBounds); + mSwipePipToHomeOverlay = overlay; + if (ENABLE_SHELL_TRANSITIONS) { + // With Shell transition, the overlay was attached to the remote transition leash, which + // will be removed when the current transition is finished, so we need to reparent it + // to the actual Task surface now. + // PipTransition is responsible to fade it out and cleanup when finishing the enter PIP + // transition. + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mTaskOrganizer.reparentChildSurfaceToTask(taskId, overlay, t); + t.setLayer(overlay, Integer.MAX_VALUE); + t.apply(); } } @@ -671,7 +684,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void onEndOfSwipePipToHomeTransition() { if (Transitions.ENABLE_SHELL_TRANSITIONS) { - mSwipePipToHomeOverlay = null; return; } @@ -822,6 +834,24 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return false; } + @Override + public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { + b.setParent(findTaskSurface(taskId)); + } + + @Override + public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, + SurfaceControl.Transaction t) { + t.reparent(sc, findTaskSurface(taskId)); + } + + private SurfaceControl findTaskSurface(int taskId) { + if (mTaskInfo == null || mLeash == null || mTaskInfo.taskId != taskId) { + throw new IllegalArgumentException("There is no surface for taskId=" + taskId); + } + return mLeash; + } + @Override public void onFixedRotationStarted(int displayId, int newRotation) { mNextRotation = newRotation; @@ -877,9 +907,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, clearWaitForFixedRotation(); } - /** Called when exiting PIP tranisiton is finished to do the state cleanup. */ + /** Called when exiting PIP transition is finished to do the state cleanup. */ void onExitPipFinished(TaskInfo info) { clearWaitForFixedRotation(); + if (mSwipePipToHomeOverlay != null) { + removeContentOverlay(mSwipePipToHomeOverlay, null /* callback */); + mSwipePipToHomeOverlay = null; + } mPipTransitionState.setInSwipePipToHomeTransition(false); mPictureInPictureParams = null; mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index f01457bc4c950..a366be2d9c6ef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -603,11 +603,18 @@ public class PipTransition extends PipTransitionController { && taskInfo.pictureInPictureParams.isAutoEnterEnabled() && mPipTransitionState.getInSwipePipToHomeTransition()) { mOneShotAnimationType = ANIM_TYPE_BOUNDS; - SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); - tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9]) + final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay; + startTransaction.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9]) .setPosition(leash, destinationBounds.left, destinationBounds.top) .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height()); - startTransaction.merge(tx); + if (swipePipToHomeOverlay != null) { + // Launcher fade in the overlay on top of the fullscreen Task. It is possible we + // reparent the PIP activity to a new PIP task (in case there are other activities + // in the original Task), so we should also reparent the overlay to the PIP task. + startTransaction.reparent(swipePipToHomeOverlay, leash) + .setLayer(swipePipToHomeOverlay, Integer.MAX_VALUE); + mPipOrganizer.mSwipePipToHomeOverlay = null; + } startTransaction.apply(); if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) { // For fixed rotation, set the destination bounds to the new rotation coordinates @@ -617,6 +624,10 @@ public class PipTransition extends PipTransitionController { mPipBoundsState.setBounds(destinationBounds); onFinishResize(taskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, null /* tx */); sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); + if (swipePipToHomeOverlay != null) { + mPipOrganizer.fadeOutAndRemoveOverlay(swipePipToHomeOverlay, + null /* callback */, false /* withStartDelay */); + } mPipTransitionState.setInSwipePipToHomeTransition(false); return true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index d3dc915159216..636f0dde44fdb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -592,9 +592,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb return entryBounds; } - private void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds, + private void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds, SurfaceControl overlay) { - mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds, overlay); + mPipTaskOrganizer.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay); } private String getTransitionTag(int direction) { @@ -923,11 +923,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override - public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds, - SurfaceControl overlay) { + public void stopSwipePipToHome(int taskId, ComponentName componentName, + Rect destinationBounds, SurfaceControl overlay) { executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome", (controller) -> { - controller.stopSwipePipToHome(componentName, destinationBounds, overlay); + controller.stopSwipePipToHome(taskId, componentName, destinationBounds, + overlay); }); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index c5aab45f56faf..109f96e166d77 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -280,10 +280,20 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { @Override public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { + b.setParent(findTaskSurface(taskId)); + } + + @Override + public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, + SurfaceControl.Transaction t) { + t.reparent(sc, findTaskSurface(taskId)); + } + + private SurfaceControl findTaskSurface(int taskId) { if (mRootTaskInfo.taskId == taskId) { - b.setParent(mRootLeash); + return mRootLeash; } else if (mChildrenLeashes.contains(taskId)) { - b.setParent(mChildrenLeashes.get(taskId)); + return mChildrenLeashes.get(taskId); } else { throw new IllegalArgumentException("There is no surface for taskId=" + taskId); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java index 8b36c9406b15d..7b679580fa874 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java @@ -227,10 +227,20 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { @Override public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { + b.setParent(findTaskSurface(taskId)); + } + + @Override + public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, + SurfaceControl.Transaction t) { + t.reparent(sc, findTaskSurface(taskId)); + } + + private SurfaceControl findTaskSurface(int taskId) { if (mRootTaskInfo.taskId == taskId) { - b.setParent(mRootLeash); + return mRootLeash; } else if (mChildrenLeashes.contains(taskId)) { - b.setParent(mChildrenLeashes.get(taskId)); + return mChildrenLeashes.get(taskId); } else { throw new IllegalArgumentException("There is no surface for taskId=" + taskId); }