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

Commit 7c6f77f6 authored by Johannes Gallmann's avatar Johannes Gallmann Committed by Android (Google) Code Review
Browse files

Merge "Add fling velocity handling to cross task predictive back" into main

parents 9182d252 73aec093
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);