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

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

Merge "Fix app callback broken when immediately restarting back gesture after cancel" into main

parents 470306c8 5f43140e
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -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 {
+1 −1
Original line number Diff line number Diff line
@@ -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) {
+12 −12
Original line number Diff line number Diff line
@@ -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();
                });
                if (callback == null) return;
                mProgressAnimator.onBackCancelled(callback::onBackCancelled);
            });
        }

+2 −2
Original line number Diff line number Diff line
@@ -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;
            }
        }
+32 −3
Original line number Diff line number Diff line
@@ -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;
@@ -371,6 +372,34 @@ public class WindowOnBackInvokedDispatcherTest {
        verify(mCallback1, never()).onBackCancelled();
    }

    @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);