diff options
5 files changed, 51 insertions, 21 deletions
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 11ad86c2b95d..ab529e6fe60a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2654,9 +2654,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager ViewRootImpl viewRootImpl = getViewRootImpl(); if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; - final boolean isDispatchingBack = (viewRootImpl != null - && viewRootImpl.getOnBackInvokedDispatcher().isDispatching()); - if (!disallowIntercept || isDispatchingBack) { // Allow back to intercept touch + final boolean isBackGestureInProgress = (viewRootImpl != null + && viewRootImpl.getOnBackInvokedDispatcher().isBackGestureInProgress()); + if (!disallowIntercept || isBackGestureInProgress) { + // Allow back to intercept touch intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } else { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f51d909e9e8b..63121bd8800c 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -7228,7 +7228,7 @@ public final class ViewRootImpl implements ViewParent, private int doOnBackKeyEvent(KeyEvent keyEvent) { WindowOnBackInvokedDispatcher dispatcher = getOnBackInvokedDispatcher(); OnBackInvokedCallback topCallback = dispatcher.getTopCallback(); - if (dispatcher.isDispatching()) { + if (dispatcher.isBackGestureInProgress()) { return FINISH_NOT_HANDLED; } if (topCallback instanceof OnBackAnimationCallback) { diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index cf0c0159bccd..e351d6ba3e56 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -114,7 +114,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { /** Updates the dispatcher state on a new {@link MotionEvent}. */ public void onMotionEvent(MotionEvent ev) { - if (!isDispatching() || ev == null || ev.getAction() != MotionEvent.ACTION_MOVE) { + if (!isBackGestureInProgress() || ev == null || ev.getAction() != MotionEvent.ACTION_MOVE) { return; } mTouchTracker.update(ev.getX(), ev.getY(), Float.NaN, Float.NaN); @@ -246,9 +246,9 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { } /** - * Indicates if the dispatcher is actively dispatching to a callback. + * Indicates if a user gesture is currently in progress. */ - public boolean isDispatching() { + public boolean isBackGestureInProgress() { synchronized (mLock) { return mTouchTracker.isActive() || mImeDispatchingActive; } @@ -475,12 +475,17 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @Override public void onBackStarted(BackMotionEvent backEvent) { mHandler.post(() -> { + final OnBackAnimationCallback callback = getBackAnimationCallback(); + + // reset progress animator before dispatching onBackStarted to callback. This + // ensures that onBackCancelled (of a previous gesture) is always dispatched + // before onBackStarted + if (callback != null) mProgressAnimator.reset(); mTouchTracker.setState(BackTouchTracker.TouchTrackerState.ACTIVE); mTouchTracker.setShouldUpdateStartLocation(true); mTouchTracker.setGestureStartLocation( backEvent.getTouchX(), backEvent.getTouchY(), backEvent.getSwipeEdge()); - final OnBackAnimationCallback callback = getBackAnimationCallback(); if (callback != null) { callback.onBackStarted(new BackEvent( backEvent.getTouchX(), @@ -499,14 +504,9 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { public void onBackCancelled() { mHandler.post(() -> { final OnBackAnimationCallback callback = getBackAnimationCallback(); - if (callback == null) { - mTouchTracker.reset(); - return; - } - mProgressAnimator.onBackCancelled(() -> { - mTouchTracker.reset(); - callback.onBackCancelled(); - }); + mTouchTracker.reset(); + if (callback == null) return; + mProgressAnimator.onBackCancelled(callback::onBackCancelled); }); } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index f4315e3988ac..74c2325d45a2 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -523,8 +523,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind ViewRootImpl viewRootImpl = getViewRootImpl(); if (viewRootImpl != null) { viewRootImpl.getOnBackInvokedDispatcher().onMotionEvent(event); - // Intercept touch if back dispatching is active. - if (viewRootImpl.getOnBackInvokedDispatcher().isDispatching()) { + // Intercept touch if back gesture is in progress. + if (viewRootImpl.getOnBackInvokedDispatcher().isBackGestureInProgress()) { return true; } } diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index 1a8e75a6554d..d54b862dfd34 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -51,6 +51,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @@ -372,6 +373,34 @@ public class WindowOnBackInvokedDispatcherTest { } @Test + public void onBackCancelled_calledBeforeOnBackStartedOfNewGesture() throws RemoteException { + mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1); + OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo(); + + callbackInfo.getCallback().onBackStarted(mBackEvent); + + waitForIdle(); + verify(mCallback1).onBackStarted(any(BackEvent.class)); + clearInvocations(mCallback1); + + callbackInfo.getCallback().onBackCancelled(); + + waitForIdle(); + // verify onBackCancelled not yet called (since BackProgressAnimator animates + // progress to 0 first) + verify(mCallback1, never()).onBackCancelled(); + + // simulate start of new gesture while cancel animation is still running + callbackInfo.getCallback().onBackStarted(mBackEvent); + waitForIdle(); + + // verify that onBackCancelled is called before onBackStarted + InOrder orderVerifier = Mockito.inOrder(mCallback1); + orderVerifier.verify(mCallback1).onBackCancelled(); + orderVerifier.verify(mCallback1).onBackStarted(any(BackEvent.class)); + } + + @Test public void onDetachFromWindow_cancelCallbackAndIgnoreOnBackInvoked() throws RemoteException { mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1); @@ -399,11 +428,11 @@ public class WindowOnBackInvokedDispatcherTest { callbackInfo.getCallback().onBackStarted(mBackEvent); waitForIdle(); - assertTrue(mDispatcher.isDispatching()); + assertTrue(mDispatcher.isBackGestureInProgress()); callbackInfo.getCallback().onBackInvoked(); waitForIdle(); - assertFalse(mDispatcher.isDispatching()); + assertFalse(mDispatcher.isBackGestureInProgress()); } @Test @@ -418,7 +447,7 @@ public class WindowOnBackInvokedDispatcherTest { callbackInfo.getCallback().onBackStarted(mBackEvent); waitForIdle(); - assertTrue(mDispatcher.isDispatching()); + assertTrue(mDispatcher.isBackGestureInProgress()); assertTrue(mDispatcher.mTouchTracker.isActive()); main.runWithScissors(() -> mDispatcher.onMotionEvent(mMotionEvent), 100); |