Merge "Polish entering pip from fullscreen with shell transition" into udc-dev

This commit is contained in:
Riddle Hsu
2023-05-09 16:49:43 +00:00
committed by Android (Google) Code Review
3 changed files with 97 additions and 50 deletions

View File

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

View File

@@ -828,6 +828,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private void onEndOfSwipePipToHomeTransition() {
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mPipTransitionController.setEnterAnimationType(ANIM_TYPE_BOUNDS);
return;
}

View File

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