Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9338d0cc authored by Riddle Hsu's avatar Riddle Hsu
Browse files

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
parent 7dd18dd3
Loading
Loading
Loading
Loading
+45 −0
Original line number Diff line number Diff line
@@ -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
     */
+9 −42
Original line number Diff line number Diff line
@@ -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);
    }

+13 −28
Original line number Diff line number Diff line
@@ -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)
+0 −9
Original line number Diff line number Diff line
@@ -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.
     */
+2 −7
Original line number Diff line number Diff line
@@ -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));
        }
    }
}
Loading