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
This commit is contained in:
Chris Li
2022-03-02 22:45:55 +08:00
parent d985a1796c
commit b6a5bf31ce
12 changed files with 178 additions and 30 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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 + " ";

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
});
}

View File

@@ -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);
}

View File

@@ -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);
}