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

Commit ca1e981d authored by Perry Wu's avatar Perry Wu
Browse files

[PIP2] Fix enter/exit animator

Fix issue with enter/exit animation. This contains a bug
with the home screen not drawing properly, will be investigated
separately. Also contains some skeleton code for fixed rotation,
which will be the follow up CL.

Test: enter exit in button nav, see video
Video: http://recall/-/cidfAmGe9UaIuFMHR3GEC8/gnO69ueSDM2VLcxnM4pY2p
Flag: com.android.wm.shell.enable_pip2_implementation
Bug: b/350801661
Change-Id: I9a431324ba59301c85c00f08047e5f654322316b
parent fc8016bf
Loading
Loading
Loading
Loading
+52 −6
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.IntDef;
import android.content.Context;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.Rect;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl;


import androidx.annotation.NonNull;
import androidx.annotation.NonNull;
@@ -51,8 +52,10 @@ public class PipEnterExitAnimator extends ValueAnimator


    @NonNull private final SurfaceControl mLeash;
    @NonNull private final SurfaceControl mLeash;
    private final SurfaceControl.Transaction mStartTransaction;
    private final SurfaceControl.Transaction mStartTransaction;
    private final int mEnterAnimationDuration;
    private final SurfaceControl.Transaction mFinishTransaction;
    private final int mEnterExitAnimationDuration;
    private final @BOUNDS int mDirection;
    private final @BOUNDS int mDirection;
    private final @Surface.Rotation int mRotation;


    // optional callbacks for tracking animation start and end
    // optional callbacks for tracking animation start and end
    @Nullable private Runnable mAnimationStartCallback;
    @Nullable private Runnable mAnimationStartCallback;
@@ -62,37 +65,59 @@ public class PipEnterExitAnimator extends ValueAnimator
    private final Rect mStartBounds = new Rect();
    private final Rect mStartBounds = new Rect();
    private final Rect mEndBounds = new Rect();
    private final Rect mEndBounds = new Rect();


    @Nullable private final Rect mSourceRectHint;
    private final Rect mSourceRectHintInsets = new Rect();
    private final Rect mZeroInsets = new Rect(0, 0, 0, 0);

    // Bounds updated by the evaluator as animator is running.
    // Bounds updated by the evaluator as animator is running.
    private final Rect mAnimatedRect = new Rect();
    private final Rect mAnimatedRect = new Rect();


    private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
    private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
            mSurfaceControlTransactionFactory;
            mSurfaceControlTransactionFactory;
    private final RectEvaluator mRectEvaluator;
    private final RectEvaluator mRectEvaluator;
    private final RectEvaluator mInsetEvaluator;
    private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
    private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;


    public PipEnterExitAnimator(Context context,
    public PipEnterExitAnimator(Context context,
            @NonNull SurfaceControl leash,
            @NonNull SurfaceControl leash,
            SurfaceControl.Transaction startTransaction,
            SurfaceControl.Transaction startTransaction,
            SurfaceControl.Transaction finishTransaction,
            @NonNull Rect baseBounds,
            @NonNull Rect baseBounds,
            @NonNull Rect startBounds,
            @NonNull Rect startBounds,
            @NonNull Rect endBounds,
            @NonNull Rect endBounds,
            @BOUNDS int direction) {
            @Nullable Rect sourceRectHint,
            @BOUNDS int direction,
            @Surface.Rotation int rotation) {
        mLeash = leash;
        mLeash = leash;
        mStartTransaction = startTransaction;
        mStartTransaction = startTransaction;
        mFinishTransaction = finishTransaction;
        mBaseBounds.set(baseBounds);
        mBaseBounds.set(baseBounds);
        mStartBounds.set(startBounds);
        mStartBounds.set(startBounds);
        mAnimatedRect.set(startBounds);
        mAnimatedRect.set(startBounds);
        mEndBounds.set(endBounds);
        mEndBounds.set(endBounds);
        mRectEvaluator = new RectEvaluator(mAnimatedRect);
        mRectEvaluator = new RectEvaluator(mAnimatedRect);
        mInsetEvaluator = new RectEvaluator(new Rect());
        mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
        mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
        mDirection = direction;
        mDirection = direction;
        mRotation = rotation;

        mSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint) : null;
        if (mSourceRectHint != null) {
            mSourceRectHintInsets.set(
                    mSourceRectHint.left - mBaseBounds.left,
                    mSourceRectHint.top - mBaseBounds.top,
                    mBaseBounds.right - mSourceRectHint.right,
                    mBaseBounds.bottom - mSourceRectHint.bottom
            );
        }


        mSurfaceControlTransactionFactory =
        mSurfaceControlTransactionFactory =
                new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
                new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
        mEnterAnimationDuration = context.getResources()
        mEnterExitAnimationDuration = context.getResources()
                .getInteger(R.integer.config_pipEnterAnimationDuration);
                .getInteger(R.integer.config_pipEnterAnimationDuration);


        setDuration(mEnterAnimationDuration);
        setObjectValues(startBounds, endBounds);
        setDuration(mEnterExitAnimationDuration);
        setEvaluator(mRectEvaluator);
        setEvaluator(mRectEvaluator);
        addListener(this);
        addListener(this);
        addUpdateListener(this);
        addUpdateListener(this);
@@ -118,6 +143,14 @@ public class PipEnterExitAnimator extends ValueAnimator


    @Override
    @Override
    public void onAnimationEnd(@NonNull Animator animation) {
    public void onAnimationEnd(@NonNull Animator animation) {
        if (mFinishTransaction != null) {
            // finishTransaction might override some state (eg. corner radii) so we want to
            // manually set the state to the end of the animation
            mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash, mSourceRectHint,
                            mBaseBounds, mAnimatedRect, getInsets(1f), isInPipDirection(), 1f)
                    .round(mFinishTransaction, mLeash, isInPipDirection())
                    .shadow(mFinishTransaction, mLeash, isInPipDirection());
        }
        if (mAnimationEndCallback != null) {
        if (mAnimationEndCallback != null) {
            mAnimationEndCallback.run();
            mAnimationEndCallback.run();
        }
        }
@@ -127,19 +160,32 @@ public class PipEnterExitAnimator extends ValueAnimator
    public void onAnimationUpdate(@NonNull ValueAnimator animation) {
    public void onAnimationUpdate(@NonNull ValueAnimator animation) {
        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
        final float fraction = getAnimatedFraction();
        final float fraction = getAnimatedFraction();
        Rect insets = getInsets(fraction);

        // TODO (b/350801661): implement fixed rotation
        // TODO (b/350801661): implement fixed rotation


        mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, null,
        mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint,
                mBaseBounds, mAnimatedRect, null, isInPipDirection(), fraction)
                mBaseBounds, mAnimatedRect, insets, isInPipDirection(), fraction)
                .round(tx, mLeash, isInPipDirection())
                .round(tx, mLeash, isInPipDirection())
                .shadow(tx, mLeash, isInPipDirection());
                .shadow(tx, mLeash, isInPipDirection());
        tx.apply();
        tx.apply();
    }
    }


    private Rect getInsets(float fraction) {
        Rect startInsets = isInPipDirection() ? mZeroInsets : mSourceRectHintInsets;
        Rect endInsets = isInPipDirection() ? mSourceRectHintInsets : mZeroInsets;

        return mInsetEvaluator.evaluate(fraction, startInsets, endInsets);
    }

    private boolean isInPipDirection() {
    private boolean isInPipDirection() {
        return mDirection == BOUNDS_ENTER;
        return mDirection == BOUNDS_ENTER;
    }
    }


    private boolean isOutPipDirection() {
        return mDirection == BOUNDS_EXIT;
    }

    // no-ops
    // no-ops


    @Override
    @Override
+6 −0
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.Bundle;
import android.view.SurfaceControl;
import android.view.SurfaceControl;


import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Nullable;


import com.android.internal.util.Preconditions;
import com.android.internal.util.Preconditions;
@@ -88,6 +89,11 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
                : new PictureInPictureParams.Builder().build());
                : new PictureInPictureParams.Builder().build());
    }
    }


    @NonNull
    public PictureInPictureParams getPictureInPictureParams() {
        return mPictureInPictureParams;
    }

    @Override
    @Override
    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
        PictureInPictureParams params = taskInfo.pictureInPictureParams;
        PictureInPictureParams params = taskInfo.pictureInPictureParams;
+49 −9
Original line number Original line Diff line number Diff line
@@ -36,6 +36,7 @@ import android.content.Context;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IBinder;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.TransitionRequestInfo;
@@ -398,17 +399,22 @@ public class PipTransition extends PipTransitionController implements
        SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
        SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
        Preconditions.checkNotNull(pipLeash, "Leash is null for bounds transition.");
        Preconditions.checkNotNull(pipLeash, "Leash is null for bounds transition.");


        Rect sourceRectHint = null;
        if (pipChange.getTaskInfo() != null
                && pipChange.getTaskInfo().pictureInPictureParams != null) {
            sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
        }

        PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
        PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
                startTransaction, startBounds, startBounds, endBounds,
                startTransaction, finishTransaction, startBounds, startBounds, endBounds,
                PipEnterExitAnimator.BOUNDS_ENTER);
                sourceRectHint, PipEnterExitAnimator.BOUNDS_ENTER, Surface.ROTATION_0);


        tx.addTransactionCommittedListener(mPipScheduler.getMainExecutor(),
        tx.addTransactionCommittedListener(mPipScheduler.getMainExecutor(),
                this::onClientDrawAtTransitionEnd);
                this::onClientDrawAtTransitionEnd);
        finishWct.setBoundsChangeTransaction(pipTaskToken, tx);
        finishWct.setBoundsChangeTransaction(pipTaskToken, tx);


        animator.setAnimationEndCallback(() -> {
        animator.setAnimationEndCallback(() ->
            finishCallback.onTransitionFinished(finishWct.isEmpty() ? null : finishWct);
                finishCallback.onTransitionFinished(finishWct));
        });


        animator.start();
        animator.start();
        return true;
        return true;
@@ -451,20 +457,54 @@ public class PipTransition extends PipTransitionController implements
        WindowContainerToken pipToken = mPipTransitionState.mPipTaskToken;
        WindowContainerToken pipToken = mPipTransitionState.mPipTaskToken;


        TransitionInfo.Change pipChange = getChangeByToken(info, pipToken);
        TransitionInfo.Change pipChange = getChangeByToken(info, pipToken);
        if (pipChange == null) {
            // pipChange is null, check to see if we've reparented the PIP activity for
            // the multi activity case. If so we should use the activity leash instead
            for (TransitionInfo.Change change : info.getChanges()) {
                if (change.getTaskInfo() == null
                        && change.getLastParent() != null
                        && change.getLastParent().equals(pipToken)) {
                    pipChange = change;
                    break;
                }
            }

            // failsafe
            if (pipChange == null) {
            if (pipChange == null) {
                return false;
                return false;
            }
            }
        }

        // for multi activity, we need to manually set the leash layer
        if (pipChange.getTaskInfo() == null) {
            TransitionInfo.Change parent = getChangeByToken(info, pipChange.getParent());
            if (parent != null) {
                startTransaction.setLayer(parent.getLeash(), Integer.MAX_VALUE - 1);
            }
        }


        Rect startBounds = pipChange.getStartAbsBounds();
        Rect startBounds = pipChange.getStartAbsBounds();
        Rect endBounds = pipChange.getEndAbsBounds();
        Rect endBounds = pipChange.getEndAbsBounds();
        SurfaceControl pipLeash = pipChange.getLeash();
        SurfaceControl pipLeash = pipChange.getLeash();
        Preconditions.checkNotNull(pipLeash, "Leash is null for exit transition.");

        Rect sourceRectHint = null;
        if (pipChange.getTaskInfo() != null
                && pipChange.getTaskInfo().pictureInPictureParams != null) {
            // single activity
            sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
        } else if (mPipTaskListener.getPictureInPictureParams().hasSourceBoundsHint()) {
            // multi activity
            sourceRectHint = mPipTaskListener.getPictureInPictureParams().getSourceRectHint();
        }


        PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
        PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
                startTransaction, startBounds, startBounds, endBounds,
                startTransaction, finishTransaction, endBounds, startBounds, endBounds,
                PipEnterExitAnimator.BOUNDS_EXIT);
                sourceRectHint, PipEnterExitAnimator.BOUNDS_EXIT, Surface.ROTATION_0);

        animator.setAnimationEndCallback(() -> {
        animator.setAnimationEndCallback(() -> {
            finishCallback.onTransitionFinished(null);
            mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
            mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
            finishCallback.onTransitionFinished(null);
        });
        });


        animator.start();
        animator.start();