diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 1d7e64988359483ee522a9dc74cf8095965a19c8..bbfeb90704db197b4e59e4c95122750de5da4dfc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -52,7 +52,7 @@ import java.util.Objects; */ public class PipAnimationController { static final float FRACTION_START = 0f; - private static final float FRACTION_END = 1f; + static final float FRACTION_END = 1f; public static final int ANIM_TYPE_BOUNDS = 0; public static final int ANIM_TYPE_ALPHA = 1; @@ -718,7 +718,9 @@ public class PipAnimationController { .round(tx, leash, sourceBounds, bounds) .shadow(tx, leash, shouldApplyShadowRadius()); } - tx.apply(); + if (!handlePipTransaction(leash, tx, bounds, 1f /* alpha */)) { + tx.apply(); + } } private Rect computeInsets(float fraction) { 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 363d6759f8d06080cc32fa011aff718b698f206b..8709eaba027290c233c40ef0c0aaca604b6b9e28 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 @@ -828,6 +828,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void onEndOfSwipePipToHomeTransition() { if (Transitions.ENABLE_SHELL_TRANSITIONS) { + mPipTransitionController.setEnterAnimationType(ANIM_TYPE_BOUNDS); return; } 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 046d6fce443ba1b6a6c206382f3fcc7148032049..db516c0e74f93b8b59984a202abbc1210de22333 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 @@ -45,7 +45,6 @@ import android.animation.Animator; import android.app.ActivityManager; import android.app.TaskInfo; import android.content.Context; -import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; @@ -109,6 +108,17 @@ public class PipTransition extends PipTransitionController { /** Whether the PIP window has fade out for fixed rotation. */ private boolean mHasFadeOut; + /** Used for setting transform to a transaction from animator. */ + private final PipAnimationController.PipTransactionHandler mTransactionConsumer = + new PipAnimationController.PipTransactionHandler() { + @Override + public boolean handlePipTransaction(SurfaceControl leash, + SurfaceControl.Transaction tx, Rect destinationBounds, float alpha) { + // Only set the operation to transaction but do not apply. + return true; + } + }; + public PipTransition(Context context, @NonNull ShellInit shellInit, @NonNull ShellTaskOrganizer shellTaskOrganizer, @@ -338,7 +348,7 @@ public class PipTransition extends PipTransitionController { @Override public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, - @Nullable SurfaceControl.Transaction tx) { + @NonNull SurfaceControl.Transaction tx) { final boolean enteringPip = isInPipDirection(direction); if (enteringPip) { mPipTransitionState.setTransitionState(ENTERED_PIP); @@ -348,13 +358,15 @@ public class PipTransition extends PipTransitionController { // (likely a remote like launcher), so don't fire the finish-callback here -- wait until // the exit transition is merged. if ((mExitTransition == null || isAnimatingLocally()) && mFinishCallback != null) { + final SurfaceControl leash = mPipOrganizer.getSurfaceControl(); + final boolean hasValidLeash = leash != null && leash.isValid(); WindowContainerTransaction wct = null; if (isOutPipDirection(direction)) { // Only need to reset surface properties. The server-side operations were already // done at the start. But if it is running fixed rotation, there will be a seamless // display transition later. So the last rotation transform needs to be kept to // avoid flickering, and then the display transition will reset the transform. - if (tx != null && !mInFixedRotation) { + if (!mInFixedRotation && mFinishTransaction != null) { mFinishTransaction.merge(tx); } } else { @@ -363,27 +375,36 @@ public class PipTransition extends PipTransitionController { // If we are animating from fullscreen using a bounds animation, then reset the // activity windowing mode, and set the task bounds to the final bounds wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED); - wct.scheduleFinishEnterPip(taskInfo.token, destinationBounds); wct.setBounds(taskInfo.token, destinationBounds); } else { wct.setBounds(taskInfo.token, null /* bounds */); } - if (tx != null) { - wct.setBoundsChangeTransaction(taskInfo.token, tx); + // Reset the scale with bounds change synchronously. + if (hasValidLeash) { + mSurfaceTransactionHelper.crop(tx, leash, destinationBounds) + .resetScale(tx, leash, destinationBounds) + .round(tx, leash, true /* applyCornerRadius */); } + wct.setBoundsChangeTransaction(taskInfo.token, tx); } - final SurfaceControl leash = mPipOrganizer.getSurfaceControl(); final int displayRotation = taskInfo.getConfiguration().windowConfiguration .getDisplayRotation(); if (enteringPip && mInFixedRotation && mEndFixedRotation != displayRotation - && leash != null && leash.isValid()) { + && hasValidLeash) { // Launcher may update the Shelf height during the animation, which will update the // destination bounds. Because this is in fixed rotation, We need to make sure the // finishTransaction is using the updated bounds in the display rotation. + final PipAnimationController.PipTransitionAnimator<?> animator = + mPipAnimationController.getCurrentAnimator(); final Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds(); final Rect finishBounds = new Rect(destinationBounds); rotateBounds(finishBounds, displayBounds, mEndFixedRotation, displayRotation); - mSurfaceTransactionHelper.crop(mFinishTransaction, leash, finishBounds); + if (!finishBounds.equals(animator.getEndValue())) { + ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: Destination bounds were changed during animation", TAG); + rotateBounds(finishBounds, displayBounds, mEndFixedRotation, displayRotation); + mSurfaceTransactionHelper.crop(mFinishTransaction, leash, finishBounds); + } } mFinishTransaction = null; callFinishCallback(wct); @@ -665,9 +686,11 @@ public class PipTransition extends PipTransitionController { private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash, final Rect baseBounds, final Rect startBounds, final Rect endBounds, final int rotationDelta) { + final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( + taskInfo.pictureInPictureParams, endBounds); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(taskInfo, leash, baseBounds, startBounds, - endBounds, null /* sourceHintRect */, TRANSITION_DIRECTION_LEAVE_PIP, + endBounds, sourceHintRect, TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, rotationDelta); animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP) .setPipAnimationCallback(mPipAnimationCallback) @@ -800,10 +823,6 @@ public class PipTransition extends PipTransitionController { computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo, destinationBounds, sourceHintRect); } - // Set corner radius for entering pip. - mSurfaceTransactionHelper - .crop(finishTransaction, leash, destinationBounds) - .round(finishTransaction, leash, true /* applyCornerRadius */); if (!mPipOrganizer.shouldAttachMenuEarly()) { mTransitions.getMainExecutor().executeDelayed( () -> mPipMenuController.attach(leash), 0); @@ -812,41 +831,11 @@ public class PipTransition extends PipTransitionController { if (taskInfo.pictureInPictureParams != null && taskInfo.pictureInPictureParams.isAutoEnterEnabled() && mPipTransitionState.getInSwipePipToHomeTransition()) { - 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()); - 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 - // at the end. - destinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); - } - 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); + handleSwipePipToHomeTransition(startTransaction, finishTransaction, leash, + sourceHintRect, destinationBounds, rotationDelta, taskInfo); return; } - if (rotationDelta != Surface.ROTATION_0) { - Matrix tmpTransform = new Matrix(); - tmpTransform.postRotate(rotationDelta); - startTransaction.setMatrix(leash, tmpTransform, new float[9]); - } - final int enterAnimationType = mEnterAnimationType; if (enterAnimationType == ANIM_TYPE_ALPHA) { startTransaction.setAlpha(leash, 0f); @@ -880,11 +869,13 @@ public class PipTransition extends PipTransitionController { } else if (enterAnimationType == ANIM_TYPE_ALPHA) { animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds, 0f, 1f); + mSurfaceTransactionHelper + .crop(finishTransaction, leash, destinationBounds) + .round(finishTransaction, leash, true /* applyCornerRadius */); } else { throw new RuntimeException("Unrecognized animation type: " + enterAnimationType); } animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) - .setPipTransactionHandler(mPipOrganizer.getPipTransactionHandler()) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration); if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) { @@ -893,7 +884,15 @@ public class PipTransition extends PipTransitionController { // ComputeRotatedBounds has changed the DisplayLayout without affecting the animation. animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds()); } - animator.start(); + // Keep the last appearance when finishing the transition. The transform will be reset when + // setting bounds. + animator.setPipTransactionHandler(mTransactionConsumer).applySurfaceControlTransaction( + leash, finishTransaction, PipAnimationController.FRACTION_END); + // Remove the workaround after fixing ClosePipBySwipingDownTest that detects the shadow + // as unexpected visible. + finishTransaction.setShadowRadius(leash, 0); + // Start to animate enter PiP. + animator.setPipTransactionHandler(mPipOrganizer.getPipTransactionHandler()).start(); } /** Computes destination bounds in old rotation and updates source hint rect if available. */ @@ -915,6 +914,51 @@ public class PipTransition extends PipTransitionController { } } + private void handleSwipePipToHomeTransition( + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull SurfaceControl leash, @Nullable Rect sourceHintRect, + @NonNull Rect destinationBounds, int rotationDelta, + @NonNull ActivityManager.RunningTaskInfo pipTaskInfo) { + final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay; + 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; + } + + Rect sourceBounds = pipTaskInfo.configuration.windowConfiguration.getBounds(); + if (!Transitions.SHELL_TRANSITIONS_ROTATION && rotationDelta % 2 == 1) { + // PipController#startSwipePipToHome has updated the display layout to new rotation, + // so flip the source bounds to match the same orientation. + sourceBounds = new Rect(0, 0, sourceBounds.height(), sourceBounds.width()); + } + final PipAnimationController.PipTransitionAnimator animator = + mPipAnimationController.getAnimator(pipTaskInfo, leash, sourceBounds, sourceBounds, + destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, + 0 /* startingAngle */, 0 /* rotationDelta */) + .setPipTransactionHandler(mTransactionConsumer) + .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP); + // The start state is the end state for swipe-auto-pip. + startTransaction.merge(finishTransaction); + animator.applySurfaceControlTransaction(leash, startTransaction, + PipAnimationController.FRACTION_END); + startTransaction.apply(); + + mPipBoundsState.setBounds(destinationBounds); + final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); + onFinishResize(pipTaskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, tx); + sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); + if (swipePipToHomeOverlay != null) { + mPipOrganizer.fadeOutAndRemoveOverlay(swipePipToHomeOverlay, + null /* callback */, false /* withStartDelay */); + } + mPipTransitionState.setInSwipePipToHomeTransition(false); + } + private void startExitToSplitAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction,