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

Commit 15db4793 authored by Ikram Gabiyev's avatar Ikram Gabiyev
Browse files

Refactor 3-btn-nav with flipv2

Refactor the 3-button-nav enter PiP
bounds animation and transition flow
incorporating the new config-at-end flipv2 approach.

This should reduce the delay to reenable PiP interactions
after enterin PiP, as well as remove need for
setBoundsChangeTransactions.

Also separate enter from expand animator since the animation
logic for these two is going to be inherently different -
as one uses config-at-end flipv2 and the other uses a generic
transition.

Bug: 350801661
Flag: com.android.wm.shell.enable_pip2
Test: atest AutoEnterPipWithSourceRectHintTest
Change-Id: I8a963487db49148db83c6065a983668bf6d3374d
parent 849be897
Loading
Loading
Loading
Loading
+159 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.wm.shell.pip2.animation;

import android.animation.Animator;
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;

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

import com.android.wm.shell.R;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.shared.animation.Interpolators;

/**
 * Animator that handles bounds animations for entering PIP.
 */
public class PipEnterAnimator extends ValueAnimator
        implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
    @NonNull private final SurfaceControl mLeash;
    private final SurfaceControl.Transaction mStartTransaction;
    private final SurfaceControl.Transaction mFinishTransaction;

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

    private final RectEvaluator mRectEvaluator;
    private final Rect mEndBounds = new Rect();
    @Nullable private final Rect mSourceRectHint;
    private final @Surface.Rotation int mRotation;
    @Nullable private Runnable mAnimationStartCallback;
    @Nullable private Runnable mAnimationEndCallback;

    private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
            mSurfaceControlTransactionFactory;

    // Internal state representing initial transform - cached to avoid recalculation.
    private final PointF mInitScale = new PointF();
    private final PointF mInitPos = new PointF();
    private final Rect mInitCrop = new Rect();

    public PipEnterAnimator(Context context,
            @NonNull SurfaceControl leash,
            SurfaceControl.Transaction startTransaction,
            SurfaceControl.Transaction finishTransaction,
            @NonNull Rect endBounds,
            @Nullable Rect sourceRectHint,
            @Surface.Rotation int rotation) {
        mLeash = leash;
        mStartTransaction = startTransaction;
        mFinishTransaction = finishTransaction;
        mRectEvaluator = new RectEvaluator(mAnimatedRect);
        mEndBounds.set(endBounds);
        mSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint) : null;
        mRotation = rotation;
        mSurfaceControlTransactionFactory =
                new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();

        final int enterAnimationDuration = context.getResources()
                .getInteger(R.integer.config_pipEnterAnimationDuration);
        setDuration(enterAnimationDuration);
        setFloatValues(0f, 1f);
        setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
        addListener(this);
        addUpdateListener(this);
    }

    public void setAnimationStartCallback(@NonNull Runnable runnable) {
        mAnimationStartCallback = runnable;
    }

    public void setAnimationEndCallback(@NonNull Runnable runnable) {
        mAnimationEndCallback = runnable;
    }

    @Override
    public void onAnimationStart(@NonNull Animator animation) {
        if (mAnimationStartCallback != null) {
            mAnimationStartCallback.run();
        }
        if (mStartTransaction != null) {
            onEnterAnimationUpdate(mInitScale, mInitPos, mInitCrop,
                    0f /* fraction */, mStartTransaction);
            mStartTransaction.apply();
        }
    }

    @Override
    public void onAnimationEnd(@NonNull Animator animation) {
        if (mAnimationEndCallback != null) {
            mAnimationEndCallback.run();
        }
    }

    @Override
    public void onAnimationUpdate(@NonNull ValueAnimator animation) {
        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
        final float fraction = getAnimatedFraction();
        onEnterAnimationUpdate(mInitScale, mInitPos, mInitCrop, fraction, tx);
        tx.apply();
    }

    private void onEnterAnimationUpdate(PointF initScale, PointF initPos, Rect initCrop,
            float fraction, SurfaceControl.Transaction tx) {
        float scaleX = 1 + (initScale.x - 1) * (1 - fraction);
        float scaleY = 1 + (initScale.y - 1) * (1 - fraction);
        tx.setScale(mLeash, scaleX, scaleY);

        float posX = initPos.x + (mEndBounds.left - initPos.x) * fraction;
        float posY = initPos.y + (mEndBounds.top - initPos.y) * fraction;
        tx.setPosition(mLeash, posX, posY);

        Rect endCrop = new Rect(mEndBounds);
        endCrop.offsetTo(0, 0);
        mRectEvaluator.evaluate(fraction, initCrop, endCrop);
        tx.setCrop(mLeash, mAnimatedRect);
    }

    // no-ops

    @Override
    public void onAnimationCancel(@NonNull Animator animation) {}

    @Override
    public void onAnimationRepeat(@NonNull Animator animation) {}

    /**
     * Caches the initial transform relevant values for the bounds enter animation.
     *
     * Since enter PiP makes use of a config-at-end transition, initial transform needs to be
     * calculated differently from generic transitions.
     * @param pipChange PiP change received as a transition target.
     */
    public void setEnterStartState(@NonNull TransitionInfo.Change pipChange) {
        PipUtils.calcStartTransform(pipChange, mInitScale, mInitPos, mInitCrop);
    }
}
+22 −45
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.wm.shell.pip2.animation;
import android.animation.Animator;
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.content.Context;
import android.graphics.Rect;
import android.view.Surface;
@@ -30,35 +29,22 @@ import androidx.annotation.Nullable;

import com.android.wm.shell.R;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import com.android.wm.shell.shared.animation.Interpolators;

/**
 * Animator that handles bounds animations for entering / exiting PIP.
 * Animator that handles bounds animations for exit-via-expanding PIP.
 */
public class PipEnterExitAnimator extends ValueAnimator
public class PipExpandAnimator extends ValueAnimator
        implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
    @IntDef(prefix = {"BOUNDS_"}, value = {
            BOUNDS_ENTER,
            BOUNDS_EXIT
    })

    @Retention(RetentionPolicy.SOURCE)
    public @interface BOUNDS {}

    public static final int BOUNDS_ENTER = 0;
    public static final int BOUNDS_EXIT = 1;

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

    // optional callbacks for tracking animation start and end
    @Nullable private Runnable mAnimationStartCallback;
    @Nullable
    private Runnable mAnimationStartCallback;
    @Nullable private Runnable mAnimationEndCallback;

    private final Rect mBaseBounds = new Rect();
@@ -78,7 +64,7 @@ public class PipEnterExitAnimator extends ValueAnimator
    private final RectEvaluator mInsetEvaluator;
    private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;

    public PipEnterExitAnimator(Context context,
    public PipExpandAnimator(Context context,
            @NonNull SurfaceControl leash,
            SurfaceControl.Transaction startTransaction,
            SurfaceControl.Transaction finishTransaction,
@@ -86,7 +72,6 @@ public class PipEnterExitAnimator extends ValueAnimator
            @NonNull Rect startBounds,
            @NonNull Rect endBounds,
            @Nullable Rect sourceRectHint,
            @BOUNDS int direction,
            @Surface.Rotation int rotation) {
        mLeash = leash;
        mStartTransaction = startTransaction;
@@ -98,7 +83,6 @@ public class PipEnterExitAnimator extends ValueAnimator
        mRectEvaluator = new RectEvaluator(mAnimatedRect);
        mInsetEvaluator = new RectEvaluator(new Rect());
        mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
        mDirection = direction;
        mRotation = rotation;

        mSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint) : null;
@@ -113,12 +97,14 @@ public class PipEnterExitAnimator extends ValueAnimator

        mSurfaceControlTransactionFactory =
                new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
        mEnterExitAnimationDuration = context.getResources()

        final int enterAnimationDuration = context.getResources()
                .getInteger(R.integer.config_pipEnterAnimationDuration);
        setDuration(enterAnimationDuration);

        setObjectValues(startBounds, endBounds);
        setDuration(mEnterExitAnimationDuration);
        setEvaluator(mRectEvaluator);
        setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
        addListener(this);
        addUpdateListener(this);
    }
@@ -147,9 +133,10 @@ public class PipEnterExitAnimator extends ValueAnimator
            // 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());
                            mBaseBounds, mAnimatedRect, getInsets(1f),
                            false /* isInPipDirection */, 1f)
                    .round(mFinishTransaction, mLeash, false /* applyCornerRadius */)
                    .shadow(mFinishTransaction, mLeash, false /* applyCornerRadius */);
        }
        if (mAnimationEndCallback != null) {
            mAnimationEndCallback.run();
@@ -160,32 +147,22 @@ public class PipEnterExitAnimator extends ValueAnimator
    public void onAnimationUpdate(@NonNull ValueAnimator animation) {
        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
        final float fraction = getAnimatedFraction();
        Rect insets = getInsets(fraction);

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

        Rect insets = getInsets(fraction);
        mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint,
                mBaseBounds, mAnimatedRect, insets, isInPipDirection(), fraction)
                .round(tx, mLeash, isInPipDirection())
                .shadow(tx, mLeash, isInPipDirection());
                        mBaseBounds, mAnimatedRect, insets, false /* isInPipDirection */, fraction)
                .round(tx, mLeash, false /* applyCornerRadius */)
                .shadow(tx, mLeash, false /* applyCornerRadius */);
        tx.apply();
    }

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

        final Rect startInsets = mSourceRectHintInsets;
        final Rect endInsets = mZeroInsets;
        return mInsetEvaluator.evaluate(fraction, startInsets, endInsets);
    }

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

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

    // no-ops

    @Override
+16 −31
Original line number Diff line number Diff line
@@ -56,7 +56,8 @@ import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
import com.android.wm.shell.pip2.animation.PipEnterExitAnimator;
import com.android.wm.shell.pip2.animation.PipEnterAnimator;
import com.android.wm.shell.pip2.animation.PipExpandAnimator;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.shared.pip.PipContentOverlay;
import com.android.wm.shell.sysui.ShellInit;
@@ -218,6 +219,7 @@ public class PipTransition extends PipTransitionController implements
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        mFinishCallback = finishCallback;
        if (transition == mEnterTransition || info.getType() == TRANSIT_PIP) {
            mEnterTransition = null;
            // If we are in swipe PiP to Home transition we are ENTERING_PIP as a jumpcut transition
@@ -258,6 +260,7 @@ public class PipTransition extends PipTransitionController implements
        if (isRemovePipTransition(info)) {
            return removePipImmediately(info, startTransaction, finishTransaction, finishCallback);
        }
        mFinishCallback = null;
        return false;
    }

@@ -297,7 +300,6 @@ public class PipTransition extends PipTransitionController implements
            mBoundsChangeDuration = BOUNDS_CHANGE_JUMPCUT_DURATION;
        }

        mFinishCallback = finishCallback;
        mPipTransitionState.setState(PipTransitionState.CHANGING_PIP_BOUNDS, extra);
        return true;
    }
@@ -349,7 +351,6 @@ public class PipTransition extends PipTransitionController implements
            startTransaction.setMatrix(pipLeash, transformTensor, matrixTmp);
        }
        startTransaction.apply();
        finishCallback.onTransitionFinished(null /* finishWct */);
        finishInner();
        return true;
    }
@@ -386,14 +387,6 @@ public class PipTransition extends PipTransitionController implements
            return false;
        }

        WindowContainerToken pipTaskToken = pipChange.getContainer();
        if (pipTaskToken == null) {
            return false;
        }

        WindowContainerTransaction finishWct = new WindowContainerTransaction();
        SurfaceControl.Transaction tx = new SurfaceControl.Transaction();

        Rect startBounds = pipChange.getStartAbsBounds();
        Rect endBounds = pipChange.getEndAbsBounds();
        SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
@@ -405,29 +398,22 @@ public class PipTransition extends PipTransitionController implements
            sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
        }

        // For opening type transitions, if there is a non-pip change of mode TO_FRONT/OPEN,
        // For opening type transitions, if there is a change of mode TO_FRONT/OPEN,
        // make sure that change has alpha of 1f, since it's init state might be set to alpha=0f
        // by the Transitions framework to simplify Task opening transitions.
        if (TransitionUtil.isOpeningType(info.getType())) {
            for (TransitionInfo.Change change : info.getChanges()) {
                if (change.getLeash() == null || change == pipChange) continue;
                if (change.getLeash() == null) continue;
                if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
                    startTransaction.setAlpha(change.getLeash(), 1f);
                }
            }
        }

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

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

        animator.setAnimationEndCallback(() ->
                finishCallback.onTransitionFinished(finishWct));

        PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
                startTransaction, finishTransaction, endBounds, sourceRectHint, Surface.ROTATION_0);
        animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange));
        animator.setAnimationEndCallback(this::finishInner);
        animator.start();
        return true;
    }
@@ -452,11 +438,8 @@ public class PipTransition extends PipTransitionController implements

        PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction,
                PipAlphaAnimator.FADE_IN);
        animator.setAnimationEndCallback(() -> {
            finishCallback.onTransitionFinished(null);
        // This should update the pip transition state accordingly after we stop playing.
            finishInner();
        });
        animator.setAnimationEndCallback(this::finishInner);

        animator.start();
        return true;
@@ -510,9 +493,9 @@ public class PipTransition extends PipTransitionController implements
            sourceRectHint = mPipTaskListener.getPictureInPictureParams().getSourceRectHint();
        }

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

        animator.setAnimationEndCallback(() -> {
            mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
@@ -631,6 +614,7 @@ public class PipTransition extends PipTransitionController implements
    //

    private void finishInner() {
        finishTransition(null /* tx */);
        if (mPipTransitionState.getSwipePipToHomeOverlay() != null) {
            startOverlayFadeoutAnimation();
        } else if (mPipTransitionState.getState() == PipTransitionState.ENTERING_PIP) {
@@ -652,6 +636,7 @@ public class PipTransition extends PipTransitionController implements
        }
        if (mFinishCallback != null) {
            mFinishCallback.onTransitionFinished(wct);
            mFinishCallback = null;
        }
    }