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

Commit 95ba9d4c authored by Johannes Gallmann's avatar Johannes Gallmann
Browse files

Play predictive animation when quickly restarting back gesture after cancel

Bug: 327579977
Flag: ACONFIG com.android.systemui.predictive_back_system_anims TRUNKFOOD50
Test: atest BackProgressAnimatorTest
Test: atest BackAnimationControllerTest
Test: Manual, i.e. extensively testing all three system animations with quick swipes in succession
Change-Id: I1a959205eb68dc05aa5627d4833537046409ec8c
parent 7d796f4f
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -155,6 +155,14 @@ public class BackProgressAnimator {
        mSpring.animateToFinalPosition(0);
    }

    /**
     * Removes the finishCallback passed into {@link #onBackCancelled}
     */
    public void removeOnBackCancelledFinishCallback() {
        mSpring.removeEndListener(mOnAnimationEndListener);
        mBackCancelledFinishRunnable = null;
    }

    /** Returns true if the back animation is in progress. */
    boolean isBackAnimationInProgress() {
        return mBackAnimationInProgress;
+72 −44
Original line number Diff line number Diff line
@@ -147,7 +147,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
    private final Runnable mAnimationTimeoutRunnable = () -> {
        ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation didn't finish in %d ms. Resetting...",
                MAX_ANIMATION_DURATION);
        onBackAnimationFinished();
        finishBackAnimation();
    };

    private IBackAnimationFinishedCallback mBackAnimationFinishedCallback;
@@ -156,6 +156,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont

    @Nullable
    private IOnBackInvokedCallback mActiveCallback;
    @Nullable
    private RemoteAnimationTarget[] mApps;

    @VisibleForTesting
    final RemoteCallback mNavigationObserver = new RemoteCallback(
@@ -466,6 +468,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
    }

    private void onGestureStarted(float touchX, float touchY, @BackEvent.SwipeEdge int swipeEdge) {
        boolean interruptCancelPostCommitAnimation = mPostCommitAnimationInProgress
                && mCurrentTracker.isFinished() && !mCurrentTracker.getTriggerBack()
                && mQueuedTracker.isInInitialState();
        if (interruptCancelPostCommitAnimation) {
            // If a system animation is currently in the post-commit phase animating an
            // onBackCancelled event, let's interrupt it and start animating a new back gesture
            resetTouchTracker();
        }
        TouchTracker touchTracker;
        if (mCurrentTracker.isInInitialState()) {
            touchTracker = mCurrentTracker;
@@ -480,9 +490,15 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
        touchTracker.setState(TouchTracker.TouchTrackerState.ACTIVE);
        mBackGestureStarted = true;

        if (touchTracker == mCurrentTracker) {
        if (interruptCancelPostCommitAnimation) {
            // post-commit cancel is currently running. let's interrupt it and dispatch a new
            // onBackStarted event.
            mPostCommitAnimationInProgress = false;
            mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
            startSystemAnimation();
        } else if (touchTracker == mCurrentTracker) {
            // Only start the back navigation if no other gesture is being processed. Otherwise,
            // the back navigation will be started once the current gesture has finished.
            // the back navigation will fall back to legacy back event injection.
            startBackNavigation(mCurrentTracker);
        }
    }
@@ -818,6 +834,20 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
     */
    @VisibleForTesting
    void onBackAnimationFinished() {
        if (!mPostCommitAnimationInProgress) {
            // This can happen when a post-commit cancel animation was interrupted by a new back
            // gesture but the timing of interruption was bad such that the back-callback
            // implementation finished in between the time of the new gesture having started and
            // the time of the back-callback receiving the new onBackStarted event. Due to the
            // asynchronous APIs this isn't an unlikely case. To handle this, let's return early.
            // The back-callback implementation will call onBackAnimationFinished again when it is
            // done with animating the second gesture.
            return;
        }
        finishBackAnimation();
    }

    private void finishBackAnimation() {
        // Stop timeout runner.
        mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
        mPostCommitAnimationInProgress = false;
@@ -878,6 +908,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
    void finishBackNavigation(boolean triggerBack) {
        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()");
        mActiveCallback = null;
        mApps = null;
        mShouldStartOnNextMoveEvent = false;
        mOnBackStartDispatched = false;
        mPointerPilfered = false;
@@ -914,60 +945,57 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
        mTrackingLatency = false;
    }

    private void createAdapter() {
        IBackAnimationRunner runner =
                new IBackAnimationRunner.Stub() {
                    @Override
                    public void onAnimationStart(
                            RemoteAnimationTarget[] apps,
                            RemoteAnimationTarget[] wallpapers,
                            RemoteAnimationTarget[] nonApps,
                            IBackAnimationFinishedCallback finishedCallback) {
                        mShellExecutor.execute(
                                () -> {
                                    endLatencyTracking();
    private void startSystemAnimation() {
        if (mBackNavigationInfo == null) {
                                        ProtoLog.e(WM_SHELL_BACK_PREVIEW,
                                                "Lack of navigation info to start animation.");
            ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Lack of navigation info to start animation.");
            return;
        }
        if (mApps == null) {
            ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Not starting animation due to mApps being null.");
            return;
        }

        final BackAnimationRunner runner =
                                            mShellBackAnimationRegistry.getAnimationRunnerAndInit(
                                                    mBackNavigationInfo);
                mShellBackAnimationRegistry.getAnimationRunnerAndInit(mBackNavigationInfo);
        if (runner == null) {
                                        if (finishedCallback != null) {
            if (mBackAnimationFinishedCallback != null) {
                try {
                                                finishedCallback.onAnimationFinished(false);
                    mBackAnimationFinishedCallback.onAnimationFinished(false);
                } catch (RemoteException e) {
                                                Log.w(
                                                        TAG,
                                                        "Failed call IBackNaviAnimationController",
                                                        e);
                    Log.w(TAG, "Failed call IBackNaviAnimationController", e);
                }
            }
            return;
        }
        mActiveCallback = runner.getCallback();
                                    mBackAnimationFinishedCallback = finishedCallback;

                                    ProtoLog.d(
                                            WM_SHELL_BACK_PREVIEW,
                                            "BackAnimationController: startAnimation()");
                                    runner.startAnimation(
                                            apps,
                                            wallpapers,
                                            nonApps,
                                            () ->
                                                    mShellExecutor.execute(
                                                            BackAnimationController.this
                                                                    ::onBackAnimationFinished));
        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startAnimation()");

        runner.startAnimation(mApps, /*wallpapers*/ null, /*nonApps*/ null,
                () -> mShellExecutor.execute(this::onBackAnimationFinished));

                                    if (apps.length >= 1) {
        if (mApps.length >= 1) {
            mCurrentTracker.updateStartLocation();
                                        BackMotionEvent startEvent =
                                                mCurrentTracker.createStartEvent(apps[0]);
            BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]);
            dispatchOnBackStarted(mActiveCallback, startEvent);
        }
    }

    private void createAdapter() {
        IBackAnimationRunner runner =
                new IBackAnimationRunner.Stub() {
                    @Override
                    public void onAnimationStart(
                            RemoteAnimationTarget[] apps,
                            RemoteAnimationTarget[] wallpapers,
                            RemoteAnimationTarget[] nonApps,
                            IBackAnimationFinishedCallback finishedCallback) {
                        mShellExecutor.execute(
                                () -> {
                                    endLatencyTracking();
                                    mBackAnimationFinishedCallback = finishedCallback;
                                    mApps = apps;
                                    startSystemAnimation();

                                    // Dispatch the first progress after animation start for
                                    // smoothing the initial animation, instead of waiting for next
+5 −0
Original line number Diff line number Diff line
@@ -283,6 +283,11 @@ class CrossActivityBackAnimation @Inject constructor(

    private inner class Callback : IOnBackInvokedCallback.Default() {
        override fun onBackStarted(backMotionEvent: BackMotionEvent) {
            // in case we're still animating an onBackCancelled event, let's remove the finish-
            // callback from the progress animator to prevent calling finishAnimation() before
            // restarting a new animation
            progressAnimator.removeOnBackCancelledFinishCallback();

            startBackAnimation(backMotionEvent)
            progressAnimator.onBackStarted(backMotionEvent) { backEvent: BackEvent ->
                onGestureProgress(backEvent)
+7 −2
Original line number Diff line number Diff line
@@ -275,8 +275,6 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {

    private void onGestureProgress(@NonNull BackEvent backEvent) {
        if (!mBackInProgress) {
            mIsRightEdge = backEvent.getSwipeEdge() == EDGE_RIGHT;
            mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
            mBackInProgress = true;
        }
        float progress = backEvent.getProgress();
@@ -326,6 +324,13 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
    private final class Callback extends IOnBackInvokedCallback.Default {
        @Override
        public void onBackStarted(BackMotionEvent backEvent) {
            // in case we're still animating an onBackCancelled event, let's remove the finish-
            // callback from the progress animator to prevent calling finishAnimation() before
            // restarting a new animation
            mProgressAnimator.removeOnBackCancelledFinishCallback();

            mIsRightEdge = backEvent.getSwipeEdge() == EDGE_RIGHT;
            mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
            mProgressAnimator.onBackStarted(backEvent,
                    CrossTaskBackAnimation.this::onGestureProgress);
        }
+5 −0
Original line number Diff line number Diff line
@@ -285,6 +285,11 @@ public class CustomizeActivityAnimation extends ShellBackAnimation {
    private final class Callback extends IOnBackInvokedCallback.Default {
        @Override
        public void onBackStarted(BackMotionEvent backEvent) {
            // in case we're still animating an onBackCancelled event, let's remove the finish-
            // callback from the progress animator to prevent calling finishAnimation() before
            // restarting a new animation
            mProgressAnimator.removeOnBackCancelledFinishCallback();

            mProgressAnimator.onBackStarted(backEvent,
                    CustomizeActivityAnimation.this::onGestureProgress);
        }
Loading