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

Commit 73aec093 authored by Johannes Gallmann's avatar Johannes Gallmann
Browse files

Add fling velocity handling to cross task predictive back

Bug: 373811048
Test: Manual, i.e. analysing and verifying smoothness of screen recordings of cross task predictive back animation
Flag: com.android.window.flags.predictive_back_timestamp_api
Change-Id: I246e3d6317d90356cbcf20083c2e9a4bd937a61e
parent ff3cee61
Loading
Loading
Loading
Loading
+72 −1
Original line number Diff line number Diff line
@@ -16,12 +16,15 @@

package com.android.wm.shell.back;

import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.window.BackEvent.EDGE_RIGHT;

import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_TASK;
import static com.android.window.flags.Flags.predictiveBackTimestampApi;
import static com.android.wm.shell.back.BackAnimationConstants.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.wm.shell.back.CrossActivityBackAnimationKt.scaleCentered;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;

import android.animation.Animator;
@@ -36,11 +39,14 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
import android.os.RemoteException;
import android.util.TimeUtils;
import android.view.Choreographer;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.VelocityTracker;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.window.BackEvent;
@@ -48,6 +54,9 @@ import android.window.BackMotionEvent;
import android.window.BackProgressAnimator;
import android.window.IOnBackInvokedCallback;

import com.android.internal.dynamicanimation.animation.FloatValueHolder;
import com.android.internal.dynamicanimation.animation.SpringAnimation;
import com.android.internal.dynamicanimation.animation.SpringForce;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.policy.SystemBarUtils;
import com.android.internal.protolog.ProtoLog;
@@ -81,6 +90,11 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
    /** Duration of post animation after gesture committed. */
    private static final int POST_ANIMATION_DURATION_MS = 500;

    private static final float SPRING_SCALE = 100f;
    private static final float DEFAULT_FLING_VELOCITY = 320f;
    private static final float MAX_FLING_VELOCITY = 1000f;
    private static final float FLING_SPRING_STIFFNESS = 320f;

    private final Rect mStartTaskRect = new Rect();
    private float mCornerRadius;
    private int mStatusbarHeight;
@@ -114,6 +128,14 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
    private float mInterWindowMargin;
    private float mVerticalMargin;

    private final FloatValueHolder mPostCommitFlingScale = new FloatValueHolder(SPRING_SCALE);
    private final SpringForce mPostCommitFlingSpring = new SpringForce(SPRING_SCALE)
            .setStiffness(FLING_SPRING_STIFFNESS)
            .setDampingRatio(1f);
    private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
    private float mGestureProgress = 0f;
    private long mDownTime = 0L;

    @Inject
    public CrossTaskBackAnimation(Context context, BackAnimationBackground background,
            @ShellMainThread Handler handler) {
@@ -168,6 +190,7 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
        if (mEnteringTarget == null || mClosingTarget == null) {
            return;
        }
        mGestureProgress = progress;

        float touchY = event.getTouchY();

@@ -229,6 +252,8 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
        }

        mClosingCurrentRect.set(left, top, left + width, top + height);

        applyFlingScale(mClosingCurrentRect);
        applyTransform(mClosingTarget.leash, mClosingCurrentRect, mCornerRadius);
    }

@@ -239,9 +264,19 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
        float height = mapRange(progress, mEnteringStartRect.height(), mStartTaskRect.height());

        mEnteringCurrentRect.set(left, top, left + width, top + height);

        applyFlingScale(mEnteringCurrentRect);
        applyTransform(mEnteringTarget.leash, mEnteringCurrentRect, mCornerRadius);
    }

    private void applyFlingScale(RectF rect) {
        // apply a scale to the rect to account for fling velocity
        final float flingScale = Math.min(mPostCommitFlingScale.getValue() / SPRING_SCALE, 1f);
        if (flingScale >= 1f) return;
        scaleCentered(rect, flingScale, /* pivotX */ rect.right,
                /* pivotY */ rect.top + rect.height() / 2);
    }

    /** Transform the target window to match the target rect. */
    private void applyTransform(SurfaceControl leash, RectF targetRect, float cornerRadius) {
        if (leash == null || !leash.isValid()) {
@@ -280,6 +315,9 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
        mTransformMatrix.reset();
        mClosingCurrentRect.setEmpty();
        mInitialTouchPos.set(0, 0);
        mGestureProgress = 0;
        mDownTime = 0;
        mVelocityTracker.clear();

        if (mFinishCallback != null) {
            try {
@@ -295,10 +333,24 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
    private void onGestureProgress(@NonNull BackEvent backEvent) {
        if (!mBackInProgress) {
            mBackInProgress = true;
            mDownTime = backEvent.getFrameTime();
        }
        float progress = backEvent.getProgress();
        mTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
        updateGestureBackProgress(getInterpolatedProgress(progress), backEvent);
        float interpolatedProgress = getInterpolatedProgress(progress);
        if (predictiveBackTimestampApi()) {
            mVelocityTracker.addMovement(
                    MotionEvent.obtain(
                            /* downTime */ mDownTime,
                            /* eventTime */ backEvent.getFrameTime(),
                            /* action */ ACTION_MOVE,
                            /* x */ interpolatedProgress * SPRING_SCALE,
                            /* y */ 0f,
                            /* metaState */ 0
                    )
            );
        }
        updateGestureBackProgress(interpolatedProgress, backEvent);
    }

    private void onGestureCommitted() {
@@ -307,6 +359,25 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
            return;
        }

        if (predictiveBackTimestampApi()) {
            // kick off spring animation with the current velocity from the pre-commit phase, this
            // affects the scaling of the closing and/or opening task during post-commit
            mVelocityTracker.computeCurrentVelocity(1000);
            float startVelocity = mGestureProgress < 0.1f
                    ? -DEFAULT_FLING_VELOCITY : -mVelocityTracker.getXVelocity();
            SpringAnimation flingAnimation =
                    new SpringAnimation(mPostCommitFlingScale, SPRING_SCALE)
                    .setStartVelocity(Math.max(-MAX_FLING_VELOCITY, Math.min(0f, startVelocity)))
                    .setStartValue(SPRING_SCALE)
                    .setMinimumVisibleChange(0.1f)
                    .setSpring(mPostCommitFlingSpring);
            flingAnimation.start();
            // do an animation-frame immediately to prevent idle frame
            flingAnimation.doAnimationFrame(
                    Choreographer.getInstance().getLastFrameTimeNanos() / TimeUtils.NANOS_PER_MS
            );
        }

        // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current
        // coordinate of the gesture driven phase.
        mEnteringCurrentRect.round(mEnteringStartRect);