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

Commit e88cf5e1 authored by Shan Huang's avatar Shan Huang Committed by Android (Google) Code Review
Browse files

Merge "Fix broken animation on two consecutive swipes."

parents ebde13c2 f590efd6
Loading
Loading
Loading
Loading
+41 −1
Original line number Diff line number Diff line
@@ -58,6 +58,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
    @VisibleForTesting
    boolean mEnableAnimations = SystemProperties.getInt(
            "persist.wm.debug.predictive_back_anim", 0) != 0;
    /**
     * Max duration to wait for a transition to finish before accepting another gesture start
     * request.
     */
    private static final long MAX_TRANSITION_DURATION = 2000;

    /**
     * Location of the initial touch event of the back gesture.
@@ -73,6 +78,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
    /** True when a back gesture is ongoing */
    private boolean mBackGestureStarted = false;

    /** Tracks if an uninterruptible transition is in progress */
    private boolean mTransitionInProgress = false;
    /** @see #setTriggerBack(boolean) */
    private boolean mTriggerBack;

@@ -85,6 +92,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
    private IOnBackInvokedCallback mBackToLauncherCallback;
    private float mTriggerThreshold;
    private float mProgressThreshold;
    private final Runnable mResetTransitionRunnable = () -> {
        finishAnimation();
        mTransitionInProgress = false;
        ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Transition didn't finish in %d ms. Resetting...",
                MAX_TRANSITION_DURATION);
    };

    public BackAnimationController(
            @ShellMainThread ShellExecutor shellExecutor,
@@ -189,7 +202,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
        mBackToLauncherCallback = null;
    }

    private void onBackToLauncherAnimationFinished() {
    @VisibleForTesting
    void onBackToLauncherAnimationFinished() {
        if (mBackNavigationInfo != null) {
            IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
            if (mTriggerBack) {
@@ -206,6 +220,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
     * {@link BackAnimationController}
     */
    public void onMotionEvent(MotionEvent event, int action, @BackEvent.SwipeEdge int swipeEdge) {
        if (mTransitionInProgress) {
            return;
        }
        if (action == MotionEvent.ACTION_MOVE) {
            if (!mBackGestureStarted) {
                // Let the animation initialized here to make sure the onPointerDownOutsideFocus
@@ -330,6 +347,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
        IOnBackInvokedCallback targetCallback = shouldDispatchToLauncher
                ? mBackToLauncherCallback
                : mBackNavigationInfo.getOnBackInvokedCallback();
        if (shouldDispatchToLauncher) {
            startTransition();
        }
        if (mTriggerBack) {
            dispatchOnBackInvoked(targetCallback);
        } else {
@@ -401,6 +421,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
     * Sets to true when the back gesture has passed the triggering threshold, false otherwise.
     */
    public void setTriggerBack(boolean triggerBack) {
        if (mTransitionInProgress) {
            return;
        }
        mTriggerBack = triggerBack;
    }

@@ -432,6 +455,23 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
            mTransaction.remove(screenshotSurface);
        }
        mTransaction.apply();
        stopTransition();
        backNavigationInfo.onBackNavigationFinished(triggerBack);
    }

    private void startTransition() {
        if (mTransitionInProgress) {
            return;
        }
        mTransitionInProgress = true;
        mShellExecutor.executeDelayed(mResetTransitionRunnable, MAX_TRANSITION_DURATION);
    }

    private void stopTransition() {
        if (!mTransitionInProgress) {
            return;
        }
        mShellExecutor.removeCallbacks(mResetTransitionRunnable);
        mTransitionInProgress = false;
    }
}
+59 −2
Original line number Diff line number Diff line
@@ -26,7 +26,9 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

import android.app.IActivityTaskManager;
import android.app.WindowConfiguration;
@@ -47,7 +49,6 @@ import android.window.IOnBackInvokedCallback;
import androidx.test.filters.SmallTest;

import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;

import org.junit.Before;
import org.junit.Ignore;
@@ -64,7 +65,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
public class BackAnimationControllerTest {

    private final ShellExecutor mShellExecutor = new TestShellExecutor();
    private final TestShellExecutor mShellExecutor = new TestShellExecutor();

    @Mock
    private Context mContext;
@@ -221,4 +222,60 @@ public class BackAnimationControllerTest {
                BackEvent.EDGE_LEFT);
        verify(mIOnBackInvokedCallback).onBackInvoked();
    }

    @Test
    public void ignoresGesture_transitionInProgress() throws RemoteException {
        mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
        RemoteAnimationTarget animationTarget = createAnimationTarget();
        createNavigationInfo(animationTarget, null, null,
                BackNavigationInfo.TYPE_RETURN_TO_HOME);

        triggerBackGesture();
        // Check that back invocation is dispatched.
        verify(mIOnBackInvokedCallback).onBackInvoked();

        reset(mIOnBackInvokedCallback);
        // Verify that we prevent animation from restarting if another gestures happens before
        // the previous transition is finished.
        mController.onMotionEvent(
                MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0),
                MotionEvent.ACTION_DOWN,
                BackEvent.EDGE_LEFT);
        verifyNoMoreInteractions(mIOnBackInvokedCallback);

        // Verify that we start accepting gestures again once transition finishes.
        mController.onBackToLauncherAnimationFinished();
        mController.onMotionEvent(
                MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0),
                MotionEvent.ACTION_DOWN,
                BackEvent.EDGE_LEFT);
        mController.onMotionEvent(
                MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 100, 100, 0),
                MotionEvent.ACTION_MOVE,
                BackEvent.EDGE_LEFT);
        verify(mIOnBackInvokedCallback).onBackStarted();
    }

    @Test
    public void acceptsGesture_transitionTimeout() throws RemoteException {
        mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
        RemoteAnimationTarget animationTarget = createAnimationTarget();
        createNavigationInfo(animationTarget, null, null,
                BackNavigationInfo.TYPE_RETURN_TO_HOME);

        triggerBackGesture();
        reset(mIOnBackInvokedCallback);

        // Simulate transition timeout.
        mShellExecutor.flushAll();
        mController.onMotionEvent(
                MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0),
                MotionEvent.ACTION_DOWN,
                BackEvent.EDGE_LEFT);
        mController.onMotionEvent(
                MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 100, 100, 0),
                MotionEvent.ACTION_MOVE,
                BackEvent.EDGE_LEFT);
        verify(mIOnBackInvokedCallback).onBackStarted();
    }
}