From eb8ca0d725e0840723cabab0192dcbfa126a1cb5 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Fri, 11 Feb 2022 22:25:35 +0800 Subject: Fixed rotation for entering PIP with Shell transition Entering PIP rotation with orientation is changed to use fixed rotation, so we need to update the animation as well. Will update exiting PIP and swipe to enter in next cl Bug: 219411429 Test: manual test enter PIP from different orientation Change-Id: Id30f15c72b7d59ae63012d9f62e829d73ec4f335 --- .../com/android/wm/shell/pip/PipTaskOrganizer.java | 30 ++-- .../com/android/wm/shell/pip/PipTransition.java | 175 ++++++++++++++++++--- .../wm/shell/pip/PipTransitionController.java | 4 + 3 files changed, 175 insertions(+), 34 deletions(-) (limited to 'libs/WindowManager/Shell') 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 67b39839826c..84469daf570c 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 @@ -563,6 +563,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Log.d(TAG, "Alpha animation is expired. Use bounds animation."); mOneShotAnimationType = ANIM_TYPE_BOUNDS; } + + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + // For Shell transition, we will animate the window in PipTransition#startAnimation + // instead of #onTaskAppeared. + return; + } + if (mWaitForFixedRotation) { onTaskAppearedWithFixedRotation(); return; @@ -572,15 +579,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Objects.requireNonNull(destinationBounds, "Missing destination bounds"); final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { - mPipMenuController.attach(mLeash); - } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { - mOneShotAnimationType = ANIM_TYPE_BOUNDS; - } - return; - } - if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { mPipMenuController.attach(mLeash); final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( @@ -813,6 +811,16 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mNextRotation = newRotation; mWaitForFixedRotation = true; + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + // The fixed rotation will also be included in the transition info. However, if it is + // not a PIP transition (such as open another app to different orientation), + // PIP transition handler may not be aware of the fixed rotation start. + // Notify the PIP transition handler so that it can fade out the PIP window early for + // fixed transition of other windows. + mPipTransitionController.onFixedRotationStarted(); + return; + } + if (mPipTransitionState.isInPip()) { // Fade out the existing PiP to avoid jump cut during seamless rotation. fadeExistingPip(false /* show */); @@ -824,6 +832,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (!mWaitForFixedRotation) { return; } + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + clearWaitForFixedRotation(); + return; + } if (mPipTransitionState.getTransitionState() == PipTransitionState.TASK_APPEARED) { if (mPipTransitionState.getInSwipePipToHomeTransition()) { onEndOfSwipePipToHomeTransition(); 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 3e5d5f645725..60aac6806623 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 @@ -16,6 +16,7 @@ package com.android.wm.shell.pip; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.util.RotationUtils.deltaRotation; @@ -32,9 +33,11 @@ import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP; +import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SAME; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; +import static com.android.wm.shell.pip.PipTransitionState.ENTERED_PIP; import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP; import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT; import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP; @@ -47,6 +50,7 @@ import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; +import android.util.Log; import android.view.Surface; import android.view.SurfaceControl; import android.window.TransitionInfo; @@ -86,6 +90,16 @@ public class PipTransition extends PipTransitionController { /** The Task window that is currently in PIP windowing mode. */ @Nullable private WindowContainerToken mCurrentPipTaskToken; + /** Whether display is in fixed rotation. */ + private boolean mInFixedRotation; + /** + * The rotation that the display will apply after expanding PiP to fullscreen. This is only + * meaningful if {@link #mInFixedRotation} is true. + */ + @Surface.Rotation + private int mFixedRotation; + /** Whether the PIP window has fade out for fixed rotation. */ + private boolean mHasFadeOut; public PipTransition(Context context, PipBoundsState pipBoundsState, @@ -136,35 +150,41 @@ public class PipTransition extends PipTransitionController { @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + final TransitionInfo.Change currentPipChange = findCurrentPipChange(info); + final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info); + mInFixedRotation = fixedRotationChange != null; + mFixedRotation = mInFixedRotation + ? fixedRotationChange.getEndFixedRotation() + : ROTATION_UNDEFINED; + // Exiting PIP. final int type = info.getType(); if (transition.equals(mExitTransition)) { mExitDestinationBounds.setEmpty(); mExitTransition = null; - + mHasFadeOut = false; if (mFinishCallback != null) { mFinishCallback.onTransitionFinished(null, null); mFinishCallback = null; throw new RuntimeException("Previous callback not called, aborting exit PIP."); } - final TransitionInfo.Change exitPipChange = findCurrentPipChange(info); - if (exitPipChange == null) { + if (currentPipChange == null) { throw new RuntimeException("Cannot find the pip window for exit-pip transition."); } switch (type) { case TRANSIT_EXIT_PIP: startExitAnimation(info, startTransaction, finishTransaction, finishCallback, - exitPipChange); + currentPipChange); break; case TRANSIT_EXIT_PIP_TO_SPLIT: startExitToSplitAnimation(info, startTransaction, finishTransaction, - finishCallback, exitPipChange); + finishCallback, currentPipChange); break; case TRANSIT_REMOVE_PIP: removePipImmediately(info, startTransaction, finishTransaction, finishCallback, - exitPipChange); + currentPipChange); break; default: throw new IllegalStateException("mExitTransition with unexpected transit type=" @@ -177,7 +197,6 @@ public class PipTransition extends PipTransitionController { // The previous PIP Task is no longer in PIP, but this is not an exit transition (This can // happen when a new activity requests enter PIP). In this case, we just show this Task in // its end state, and play other animation as normal. - final TransitionInfo.Change currentPipChange = findCurrentPipChange(info); if (currentPipChange != null && currentPipChange.getTaskInfo().getWindowingMode() != WINDOWING_MODE_PINNED) { resetPrevPip(currentPipChange, startTransaction); @@ -193,6 +212,12 @@ public class PipTransition extends PipTransitionController { if (currentPipChange != null) { updatePipForUnhandledTransition(currentPipChange, startTransaction, finishTransaction); } + + // Fade in the fadeout PIP when the fixed rotation is finished. + if (mPipTransitionState.isInPip() && !mInFixedRotation && mHasFadeOut) { + fadeExistingPip(true /* show */); + } + return false; } @@ -242,9 +267,8 @@ public class PipTransition extends PipTransitionController { public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, @Nullable SurfaceControl.Transaction tx) { - if (isInPipDirection(direction)) { - mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP); + mPipTransitionState.setTransitionState(ENTERED_PIP); } // If there is an expected exit transition, then the exit will be "merged" into this // transition so don't fire the finish-callback in that case. @@ -268,6 +292,16 @@ public class PipTransition extends PipTransitionController { mFinishCallback = null; } + @Override + public void onFixedRotationStarted() { + // The transition with this fixed rotation may be handled by other handler before reaching + // PipTransition, so we cannot do this in #startAnimation. + if (mPipTransitionState.getTransitionState() == ENTERED_PIP && !mHasFadeOut) { + // Fade out the existing PiP to avoid jump cut during seamless rotation. + fadeExistingPip(false /* show */); + } + } + @Nullable private TransitionInfo.Change findCurrentPipChange(@NonNull TransitionInfo info) { if (mCurrentPipTaskToken == null) { @@ -282,6 +316,17 @@ public class PipTransition extends PipTransitionController { return null; } + @Nullable + private TransitionInfo.Change findFixedRotationChange(@NonNull TransitionInfo info) { + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getEndFixedRotation() != ROTATION_UNDEFINED) { + return change; + } + } + return null; + } + private void startExitAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @@ -453,6 +498,7 @@ public class PipTransition extends PipTransitionController { } // Keep track of the PIP task. mCurrentPipTaskToken = enterPip.getContainer(); + mHasFadeOut = false; if (mFinishCallback != null) { mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */); @@ -465,12 +511,25 @@ public class PipTransition extends PipTransitionController { startTransaction.show(wallpaper.getLeash()); startTransaction.setAlpha(wallpaper.getLeash(), 1.f); } + // Make sure other open changes are visible as entering PIP. Some may be hidden in + // Transitions#setupStartState because the transition type is OPEN (such as auto-enter). + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change == enterPip || change == wallpaper) { + continue; + } + if (isOpeningType(change.getMode())) { + final SurfaceControl leash = change.getLeash(); + startTransaction.show(leash).setAlpha(leash, 1.f); + } + } mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); mFinishCallback = finishCallback; + final int endRotation = mInFixedRotation ? mFixedRotation : enterPip.getEndRotation(); return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(), startTransaction, finishTransaction, enterPip.getStartRotation(), - enterPip.getEndRotation()); + endRotation); } private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash, @@ -481,25 +540,36 @@ public class PipTransition extends PipTransitionController { taskInfo.topActivityInfo); final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds(); + int rotationDelta = deltaRotation(startRotation, endRotation); + Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( + taskInfo.pictureInPictureParams, currentBounds); + if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) { + // Need to get the bounds of new rotation in old rotation for fixed rotation, + sourceHintRect = computeRotatedBounds(rotationDelta, startRotation, endRotation, + taskInfo, TRANSITION_DIRECTION_TO_PIP, destinationBounds, sourceHintRect); + } PipAnimationController.PipTransitionAnimator animator; // Set corner radius for entering pip. mSurfaceTransactionHelper .crop(finishTransaction, leash, destinationBounds) .round(finishTransaction, leash, true /* applyCornerRadius */); + mPipMenuController.attach(leash); + if (taskInfo.pictureInPictureParams != null && taskInfo.pictureInPictureParams.isAutoEnterEnabled() && mPipTransitionState.getInSwipePipToHomeTransition()) { mOneShotAnimationType = ANIM_TYPE_BOUNDS; - - // PiP menu is attached late in the process here to avoid any artifacts on the leash - // caused by addShellRoot when in gesture navigation mode. - mPipMenuController.attach(leash); SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9]) .setPosition(leash, destinationBounds.left, destinationBounds.top) .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height()); startTransaction.merge(tx); 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); @@ -507,17 +577,14 @@ public class PipTransition extends PipTransitionController { return true; } - int rotationDelta = deltaRotation(endRotation, startRotation); if (rotationDelta != Surface.ROTATION_0) { Matrix tmpTransform = new Matrix(); - tmpTransform.postRotate(rotationDelta == Surface.ROTATION_90 - ? Surface.ROTATION_270 : Surface.ROTATION_90); + tmpTransform.postRotate(rotationDelta); startTransaction.setMatrix(leash, tmpTransform, new float[9]); } if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { - final Rect sourceHintRect = - PipBoundsAlgorithm.getValidSourceHintRect( - taskInfo.pictureInPictureParams, currentBounds); + // Reverse the rotation for Shell transition animation. + rotationDelta = deltaRotation(rotationDelta, 0); animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, rotationDelta); @@ -528,9 +595,6 @@ public class PipTransition extends PipTransitionController { } } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { startTransaction.setAlpha(leash, 0f); - // PiP menu is attached late in the process here to avoid any artifacts on the leash - // caused by addShellRoot when in gesture navigation mode. - mPipMenuController.attach(leash); animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds, 0f, 1f); mOneShotAnimationType = ANIM_TYPE_BOUNDS; @@ -541,12 +605,47 @@ public class PipTransition extends PipTransitionController { startTransaction.apply(); animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) - .setDuration(mEnterExitAnimationDuration) - .start(); + .setDuration(mEnterExitAnimationDuration); + if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) { + // For fixed rotation, the animation destination bounds is in old rotation coordinates. + // Set the destination bounds to new coordinates after the animation is finished. + // ComputeRotatedBounds has changed the DisplayLayout without affecting the animation. + animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds()); + } + animator.start(); return true; } + /** Computes destination bounds in old rotation and returns source hint rect if available. */ + @Nullable + private Rect computeRotatedBounds(int rotationDelta, int startRotation, int endRotation, + TaskInfo taskInfo, int direction, Rect outDestinationBounds, + @Nullable Rect sourceHintRect) { + if (direction == TRANSITION_DIRECTION_TO_PIP) { + mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation); + final Rect displayBounds = mPipBoundsState.getDisplayBounds(); + outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); + // Transform the destination bounds to current display coordinates. + rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation); + // When entering PiP (from button navigation mode), adjust the source rect hint by + // display cutout if applicable. + if (sourceHintRect != null && taskInfo.displayCutoutInsets != null) { + if (rotationDelta == Surface.ROTATION_270) { + sourceHintRect.offset(taskInfo.displayCutoutInsets.left, + taskInfo.displayCutoutInsets.top); + } + } + } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) { + final Rect rotatedDestinationBounds = new Rect(outDestinationBounds); + rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(), + rotationDelta); + return PipBoundsAlgorithm.getValidSourceHintRect(taskInfo.pictureInPictureParams, + rotatedDestinationBounds); + } + return sourceHintRect; + } + private void startExitToSplitAnimation(TransitionInfo info, SurfaceControl.Transaction startTransaction, SurfaceControl.Transaction finishTransaction, @@ -595,6 +694,13 @@ public class PipTransition extends PipTransitionController { startTransaction.setCornerRadius(leash, 0); startTransaction.setPosition(leash, bounds.left, bounds.top); + if (mHasFadeOut && prevPipChange.getTaskInfo().isVisible()) { + if (mPipAnimationController.getCurrentAnimator() != null) { + mPipAnimationController.getCurrentAnimator().cancel(); + } + startTransaction.setAlpha(leash, 1); + } + mHasFadeOut = false; mCurrentPipTaskToken = null; mPipOrganizer.onExitPipFinished(prevPipChange.getTaskInfo()); } @@ -615,6 +721,25 @@ public class PipTransition extends PipTransitionController { .round(finishTransaction, leash, isInPip); } + /** Hides and shows the existing PIP during fixed rotation transition of other activities. */ + private void fadeExistingPip(boolean show) { + final SurfaceControl leash = mPipOrganizer.getSurfaceControl(); + final TaskInfo taskInfo = mPipOrganizer.getTaskInfo(); + if (leash == null || !leash.isValid() || taskInfo == null) { + Log.w(TAG, "Invalid leash on fadeExistingPip: " + leash); + return; + } + final float alphaStart = show ? 0 : 1; + final float alphaEnd = show ? 1 : 0; + mPipAnimationController + .getAnimator(taskInfo, leash, mPipBoundsState.getBounds(), alphaStart, alphaEnd) + .setTransitionDirection(TRANSITION_DIRECTION_SAME) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(mEnterExitAnimationDuration) + .start(); + mHasFadeOut = !show; + } + private void finishResizeForMenu(Rect destinationBounds) { mPipMenuController.movePipMenu(null, null, destinationBounds); mPipMenuController.updateMenuBounds(destinationBounds); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index 3403fb5aa940..02e713d2eaa3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -123,6 +123,10 @@ public abstract class PipTransitionController implements Transitions.TransitionH public void forceFinishTransition() { } + /** Called when the fixed rotation started. */ + public void onFixedRotationStarted() { + } + public PipTransitionController(PipBoundsState pipBoundsState, PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm, PipAnimationController pipAnimationController, Transitions transitions, -- cgit v1.2.3-59-g8ed1b