Loading libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +39 −2 Original line number Diff line number Diff line Loading @@ -67,8 +67,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont SETTING_VALUE_ON) != SETTING_VALUE_OFF; private static final int PROGRESS_THRESHOLD = SystemProperties .getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1); private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); /** * 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. Loading @@ -84,6 +88,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; Loading @@ -96,6 +102,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private IOnBackInvokedCallback mBackToLauncherCallback; private float mTriggerThreshold; private float mProgressThreshold; private final Runnable mResetTransitionRunnable = () -> { finishAnimation(); mTransitionInProgress = false; }; public BackAnimationController( @NonNull @ShellMainThread ShellExecutor shellExecutor, Loading Loading @@ -229,7 +239,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mBackToLauncherCallback = null; } private void onBackToLauncherAnimationFinished() { @VisibleForTesting void onBackToLauncherAnimationFinished() { if (mBackNavigationInfo != null) { IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback(); if (mTriggerBack) { Loading @@ -246,6 +257,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 Loading Loading @@ -370,6 +384,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont IOnBackInvokedCallback targetCallback = shouldDispatchToLauncher ? mBackToLauncherCallback : mBackNavigationInfo.getOnBackInvokedCallback(); if (shouldDispatchToLauncher) { startTransition(); } if (mTriggerBack) { dispatchOnBackInvoked(targetCallback); } else { Loading Loading @@ -436,6 +453,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; } Loading Loading @@ -467,6 +487,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; } } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +44 −1 Original line number Diff line number Diff line Loading @@ -26,8 +26,10 @@ 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.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.app.IActivityTaskManager; import android.app.WindowConfiguration; Loading Loading @@ -180,7 +182,8 @@ public class BackAnimationControllerTest { // b/207481538, we check that the surface is not moved for now, we can re-enable this once // we implement the animation verify(mTransaction, never()).setScale(eq(screenshotSurface), anyInt(), anyInt()); verify(mTransaction, never()).setPosition(animationTarget.leash, 100, 100); verify(mTransaction, never()).setPosition( animationTarget.leash, 100, 100); verify(mTransaction, atLeastOnce()).apply(); } Loading Loading @@ -251,6 +254,46 @@ public class BackAnimationControllerTest { verify(mIOnBackInvokedCallback, never()).onBackInvoked(); } public void ignoresGesture_transitionInProgress() throws RemoteException { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, BackNavigationInfo.TYPE_RETURN_TO_HOME, null); 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. doMotionEvent(MotionEvent.ACTION_DOWN, 0); verifyNoMoreInteractions(mIOnBackInvokedCallback); // Verify that we start accepting gestures again once transition finishes. mController.onBackToLauncherAnimationFinished(); doMotionEvent(MotionEvent.ACTION_DOWN, 0); doMotionEvent(MotionEvent.ACTION_MOVE, 100); 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, null); triggerBackGesture(); reset(mIOnBackInvokedCallback); // Simulate transition timeout. mShellExecutor.flushAll(); doMotionEvent(MotionEvent.ACTION_DOWN, 0); doMotionEvent(MotionEvent.ACTION_MOVE, 100); verify(mIOnBackInvokedCallback).onBackStarted(); } private void doMotionEvent(int actionDown, int coordinate) { mController.onMotionEvent( MotionEvent.obtain(0, mEventTime, actionDown, coordinate, coordinate, 0), Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +39 −2 Original line number Diff line number Diff line Loading @@ -67,8 +67,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont SETTING_VALUE_ON) != SETTING_VALUE_OFF; private static final int PROGRESS_THRESHOLD = SystemProperties .getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1); private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); /** * 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. Loading @@ -84,6 +88,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; Loading @@ -96,6 +102,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private IOnBackInvokedCallback mBackToLauncherCallback; private float mTriggerThreshold; private float mProgressThreshold; private final Runnable mResetTransitionRunnable = () -> { finishAnimation(); mTransitionInProgress = false; }; public BackAnimationController( @NonNull @ShellMainThread ShellExecutor shellExecutor, Loading Loading @@ -229,7 +239,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mBackToLauncherCallback = null; } private void onBackToLauncherAnimationFinished() { @VisibleForTesting void onBackToLauncherAnimationFinished() { if (mBackNavigationInfo != null) { IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback(); if (mTriggerBack) { Loading @@ -246,6 +257,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 Loading Loading @@ -370,6 +384,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont IOnBackInvokedCallback targetCallback = shouldDispatchToLauncher ? mBackToLauncherCallback : mBackNavigationInfo.getOnBackInvokedCallback(); if (shouldDispatchToLauncher) { startTransition(); } if (mTriggerBack) { dispatchOnBackInvoked(targetCallback); } else { Loading Loading @@ -436,6 +453,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; } Loading Loading @@ -467,6 +487,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; } }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +44 −1 Original line number Diff line number Diff line Loading @@ -26,8 +26,10 @@ 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.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.app.IActivityTaskManager; import android.app.WindowConfiguration; Loading Loading @@ -180,7 +182,8 @@ public class BackAnimationControllerTest { // b/207481538, we check that the surface is not moved for now, we can re-enable this once // we implement the animation verify(mTransaction, never()).setScale(eq(screenshotSurface), anyInt(), anyInt()); verify(mTransaction, never()).setPosition(animationTarget.leash, 100, 100); verify(mTransaction, never()).setPosition( animationTarget.leash, 100, 100); verify(mTransaction, atLeastOnce()).apply(); } Loading Loading @@ -251,6 +254,46 @@ public class BackAnimationControllerTest { verify(mIOnBackInvokedCallback, never()).onBackInvoked(); } public void ignoresGesture_transitionInProgress() throws RemoteException { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, BackNavigationInfo.TYPE_RETURN_TO_HOME, null); 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. doMotionEvent(MotionEvent.ACTION_DOWN, 0); verifyNoMoreInteractions(mIOnBackInvokedCallback); // Verify that we start accepting gestures again once transition finishes. mController.onBackToLauncherAnimationFinished(); doMotionEvent(MotionEvent.ACTION_DOWN, 0); doMotionEvent(MotionEvent.ACTION_MOVE, 100); 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, null); triggerBackGesture(); reset(mIOnBackInvokedCallback); // Simulate transition timeout. mShellExecutor.flushAll(); doMotionEvent(MotionEvent.ACTION_DOWN, 0); doMotionEvent(MotionEvent.ACTION_MOVE, 100); verify(mIOnBackInvokedCallback).onBackStarted(); } private void doMotionEvent(int actionDown, int coordinate) { mController.onMotionEvent( MotionEvent.obtain(0, mEventTime, actionDown, coordinate, coordinate, 0), Loading