Apply timeout of one-shot pip animation type for shell transition

The setPipAnimationTypeToAlpha is always called from any gesture
to home. If the one-shot type doesn't have a timeout, the issue
can be: swipe to home from A, open B and use it several minutes,
click B's button and it calls enterPictureInPictureMode. Expect
it should use bounds animation but it uses alpha type from A.

Move the management of one-shot type to PipAnimationController.
So it can decouple from different transition systems, that also
eliminates duplicated code in PipTaskOrganizer and PipTransition.

Also reduce the timeout a bit because setPipAnimationTypeToAlpha
is called at the end of swipe animation. If the duration is long,
the wrong type animation may still be selected if the user
operates quickly.

Bug: 276438425
Test: Launch Calculator, swipe to home, open a sample app.
      Wait several seconds, calls enterPictureInPictureMode.
      There should be bounds animation.

Change-Id: Ia35e0232bccdece2335a87d3ad1bc6ce80f72fdf
This commit is contained in:
Riddle Hsu
2023-03-30 23:51:28 +08:00
parent 7dd18dd348
commit 9338d0cce3
6 changed files with 71 additions and 87 deletions

View File

@@ -30,14 +30,17 @@ import android.app.TaskInfo;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.os.SystemClock;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.Transitions;
import java.lang.annotation.Retention;
@@ -61,6 +64,14 @@ public class PipAnimationController {
@Retention(RetentionPolicy.SOURCE)
public @interface AnimationType {}
/**
* The alpha type is set for swiping to home. But the swiped task may not enter PiP. And if
* another task enters PiP by non-swipe ways, e.g. call API in foreground or switch to 3-button
* navigation, then the alpha type is unexpected. So use a timeout to avoid applying wrong
* animation style to an unrelated task.
*/
private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 800;
public static final int TRANSITION_DIRECTION_NONE = 0;
public static final int TRANSITION_DIRECTION_SAME = 1;
public static final int TRANSITION_DIRECTION_TO_PIP = 2;
@@ -109,6 +120,9 @@ public class PipAnimationController {
});
private PipTransitionAnimator mCurrentAnimator;
@AnimationType
private int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private long mLastOneShotAlphaAnimationTime;
public PipAnimationController(PipSurfaceTransactionHelper helper) {
mSurfaceTransactionHelper = helper;
@@ -221,6 +235,37 @@ public class PipAnimationController {
animator.cancel();
}
/**
* Sets the preferred enter animation type for one time. This is typically used to set the
* animation type to {@link PipAnimationController#ANIM_TYPE_ALPHA}.
* <p>
* For example, gesture navigation would first fade out the PiP activity, and the transition
* should be responsible to animate in (such as fade in) the PiP.
*/
public void setOneShotEnterAnimationType(@AnimationType int animationType) {
mOneShotAnimationType = animationType;
if (animationType == ANIM_TYPE_ALPHA) {
mLastOneShotAlphaAnimationTime = SystemClock.uptimeMillis();
}
}
/** Returns the preferred animation type and consumes the one-shot type if needed. */
@AnimationType
public int takeOneShotEnterAnimationType() {
final int type = mOneShotAnimationType;
if (type == ANIM_TYPE_ALPHA) {
// Restore to default type.
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
if (SystemClock.uptimeMillis() - mLastOneShotAlphaAnimationTime
> ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"Alpha animation is expired. Use bounds animation.");
return ANIM_TYPE_BOUNDS;
}
}
return type;
}
/**
* Additional callback interface for PiP animation
*/

View File

@@ -62,7 +62,6 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
import android.view.Choreographer;
@@ -111,12 +110,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
DisplayController.OnDisplaysChangedListener, ShellTaskOrganizer.FocusListener {
private static final String TAG = PipTaskOrganizer.class.getSimpleName();
private static final boolean DEBUG = false;
/**
* The alpha type is set for swiping to home. But the swiped task may not enter PiP. And if
* another task enters PiP by non-swipe ways, e.g. call API in foreground or switch to 3-button
* navigation, then the alpha type is unexpected.
*/
private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000;
/**
* The fixed start delay in ms when fading out the content overlay from bounds animation.
@@ -301,8 +294,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private WindowContainerToken mToken;
private SurfaceControl mLeash;
protected PipTransitionState mPipTransitionState;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private long mLastOneShotAlphaAnimationTime;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
protected PictureInPictureParams mPictureInPictureParams;
@@ -421,18 +412,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mOnDisplayIdChangeCallback = onDisplayIdChangeCallback;
}
/**
* Sets the preferred animation type for one time.
* This is typically used to set the animation type to
* {@link PipAnimationController#ANIM_TYPE_ALPHA}.
*/
public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
mOneShotAnimationType = animationType;
if (animationType == ANIM_TYPE_ALPHA) {
mLastOneShotAlphaAnimationTime = SystemClock.uptimeMillis();
}
}
/**
* Override if the PiP should always use a fade-in animation during PiP entry.
*
@@ -733,26 +712,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
if (mOneShotAnimationType == ANIM_TYPE_ALPHA
&& SystemClock.uptimeMillis() - mLastOneShotAlphaAnimationTime
> ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Alpha animation is expired. Use bounds animation.", TAG);
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 (shouldAlwaysFadeIn()) {
mOneShotAnimationType = ANIM_TYPE_ALPHA;
}
final int animationType = shouldAlwaysFadeIn()
? ANIM_TYPE_ALPHA
: mPipAnimationController.takeOneShotEnterAnimationType();
if (mWaitForFixedRotation) {
onTaskAppearedWithFixedRotation();
onTaskAppearedWithFixedRotation(animationType);
return;
}
@@ -763,7 +733,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
if (animationType == ANIM_TYPE_BOUNDS) {
if (!shouldAttachMenuEarly()) {
mPipMenuController.attach(mLeash);
}
@@ -773,16 +743,15 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration,
null /* updateBoundsCallback */);
mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
} else if (animationType == ANIM_TYPE_ALPHA) {
enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration);
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
} else {
throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType);
throw new RuntimeException("Unrecognized animation type: " + animationType);
}
}
private void onTaskAppearedWithFixedRotation() {
if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
private void onTaskAppearedWithFixedRotation(int animationType) {
if (animationType == ANIM_TYPE_ALPHA) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Defer entering PiP alpha animation, fixed rotation is ongoing", TAG);
// If deferred, hside the surface till fixed rotation is completed.
@@ -791,7 +760,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
tx.setAlpha(mLeash, 0f);
tx.show(mLeash);
tx.apply();
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
return;
}
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
@@ -1895,7 +1863,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
+ " binder=" + (mToken != null ? mToken.asBinder() : null));
pw.println(innerPrefix + "mLeash=" + mLeash);
pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState());
pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
}

View File

@@ -87,7 +87,7 @@ public class PipTransition extends PipTransitionController {
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Optional<SplitScreenController> mSplitScreenOptional;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private @PipAnimationController.AnimationType int mEnterAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
private SurfaceControl.Transaction mFinishTransaction;
private final Rect mExitDestinationBounds = new Rect();
@@ -132,20 +132,6 @@ public class PipTransition extends PipTransitionController {
mSplitScreenOptional = splitScreenOptional;
}
@Override
public void setIsFullAnimation(boolean isFullAnimation) {
setOneShotAnimationType(isFullAnimation ? ANIM_TYPE_BOUNDS : ANIM_TYPE_ALPHA);
}
/**
* Sets the preferred animation type for one time.
* This is typically used to set the animation type to
* {@link PipAnimationController#ANIM_TYPE_ALPHA}.
*/
private void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
mOneShotAnimationType = animationType;
}
@Override
public void startExitTransition(int type, WindowContainerTransaction out,
@Nullable Rect destinationBounds) {
@@ -288,7 +274,10 @@ public class PipTransition extends PipTransitionController {
if (!requestHasPipEnter(request)) {
throw new IllegalStateException("Called PiP augmentRequest when request has no PiP");
}
if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
mEnterAnimationType = mPipOrganizer.shouldAlwaysFadeIn()
? ANIM_TYPE_ALPHA
: mPipAnimationController.takeOneShotEnterAnimationType();
if (mEnterAnimationType == ANIM_TYPE_ALPHA) {
mRequestedEnterTransition = transition;
mRequestedEnterTask = request.getTriggerTask().token;
outWCT.setActivityWindowingMode(request.getTriggerTask().token,
@@ -308,7 +297,7 @@ public class PipTransition extends PipTransitionController {
@Override
public boolean handleRotateDisplay(int startRotation, int endRotation,
WindowContainerTransaction wct) {
if (mRequestedEnterTransition != null && mOneShotAnimationType == ANIM_TYPE_ALPHA) {
if (mRequestedEnterTransition != null && mEnterAnimationType == ANIM_TYPE_ALPHA) {
// A fade-in was requested but not-yet started. In this case, just recalculate the
// initial state under the new rotation.
int rotationDelta = deltaRotation(startRotation, endRotation);
@@ -760,7 +749,6 @@ public class PipTransition extends PipTransitionController {
if (taskInfo.pictureInPictureParams != null
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()
&& mPipTransitionState.getInSwipePipToHomeTransition()) {
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay;
startTransaction.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9])
.setPosition(leash, destinationBounds.left, destinationBounds.top)
@@ -796,17 +784,16 @@ public class PipTransition extends PipTransitionController {
startTransaction.setMatrix(leash, tmpTransform, new float[9]);
}
if (mPipOrganizer.shouldAlwaysFadeIn()) {
mOneShotAnimationType = ANIM_TYPE_ALPHA;
}
if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
final int enterAnimationType = mEnterAnimationType;
if (enterAnimationType == ANIM_TYPE_ALPHA) {
// Restore to default type.
mEnterAnimationType = ANIM_TYPE_BOUNDS;
startTransaction.setAlpha(leash, 0f);
}
startTransaction.apply();
PipAnimationController.PipTransitionAnimator animator;
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
if (enterAnimationType == ANIM_TYPE_BOUNDS) {
animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
0 /* startingAngle */, rotationDelta);
@@ -829,13 +816,11 @@ public class PipTransition extends PipTransitionController {
animator.setColorContentOverlay(mContext);
}
}
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
} else if (enterAnimationType == ANIM_TYPE_ALPHA) {
animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
0f, 1f);
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
} else {
throw new RuntimeException("Unrecognized animation type: "
+ mOneShotAnimationType);
throw new RuntimeException("Unrecognized animation type: " + enterAnimationType);
}
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)

View File

@@ -104,15 +104,6 @@ public abstract class PipTransitionController implements Transitions.TransitionH
SurfaceControl.Transaction tx) {
}
/**
* Called to inform the transition that the animation should start with the assumption that
* PiP is not animating from its original bounds, but rather a continuation of another
* animation. For example, gesture navigation would first fade out the PiP activity, and the
* transition should be responsible to animate in (such as fade in) the PiP.
*/
public void setIsFullAnimation(boolean isFullAnimation) {
}
/**
* Called when the Shell wants to start an exit Pip transition/animation.
*/

View File

@@ -968,12 +968,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mPipBoundsState.setShelfVisibility(visible, shelfHeight);
}
private void setPinnedStackAnimationType(int animationType) {
mPipTaskOrganizer.setOneShotAnimationType(animationType);
mPipTransitionController.setIsFullAnimation(
animationType == PipAnimationController.ANIM_TYPE_BOUNDS);
}
@VisibleForTesting
void setPinnedStackAnimationListener(PipAnimationListener callback) {
mPinnedStackAnimationRecentsCallback = callback;
@@ -1337,7 +1331,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
@Override
public void setPipAnimationTypeToAlpha() {
executeRemoteCallWithTaskPermission(mController, "setPipAnimationTypeToAlpha",
(controller) -> controller.setPinnedStackAnimationType(ANIM_TYPE_ALPHA));
(controller) -> controller.mPipAnimationController.setOneShotEnterAnimationType(
ANIM_TYPE_ALPHA));
}
}
}

View File

@@ -262,7 +262,8 @@ public class PipTaskOrganizerTest extends ShellTestCase {
DisplayLayout layout = new DisplayLayout(info,
mContext.getResources(), true, true);
mPipDisplayLayoutState.setDisplayLayout(layout);
mPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
doReturn(PipAnimationController.ANIM_TYPE_ALPHA).when(mMockPipAnimationController)
.takeOneShotEnterAnimationType();
mPipTaskOrganizer.setSurfaceControlTransactionFactory(
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
}