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

Commit dbcccd65 authored by Ben Lin's avatar Ben Lin
Browse files

Move Right-click handling into UserInputHandler.

Not sure how I missed this the first try, but there's actually a
onDown() method for GestureDetector. What we can do then is override it,
and then call on rightClick if it's a mouse & a secondary click. This
get rid of a lot of hacky stuff we did.

We still have to keep mAteRightClick so we won't do singleTapUp(), which
is logic for single finger press. Also, we need to catch for right click
still in interceptEvent, so that it doesn't go down into its child views
(Who will try to generate a context menu themselves, and ... things stop
working.)

This also fixes the bug listed below, so now all menus are activated via
ACTION_DOWN only.

Bug: 29548676
Change-Id: I4d4acb3fc1f063379b508bc895c9cf70a2eeb245
parent 079dff25
Loading
Loading
Loading
Loading
+2 −9
Original line number Diff line number Diff line
@@ -46,20 +46,13 @@ final class ListeningGestureDetector extends GestureDetector

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        // TODO: If possible, move this into UserInputHandler.
        if (e.getAction() == MotionEvent.ACTION_DOWN && Events.isMouseEvent(e)) {
            mInputHandler.setLastButtonState(e.getButtonState());
        }

        // Detect drag events. When a drag is detected, intercept the rest of the gesture.
        View itemView = rv.findChildViewUnder(e.getX(), e.getY());
        if (itemView != null && mDragHelper.onTouch(itemView,  e)) {
            return true;
        }
        // Forward unhandled events to the GestureDetector.
        onTouchEvent(e);

        return false;
        return onTouchEvent(e);
    }

    @Override
@@ -79,7 +72,7 @@ final class ListeningGestureDetector extends GestureDetector
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
            return mInputHandler.onSingleRightClickUp(event);
            return mInputHandler.onRightClick(event);
        }
        return false;
    }
+42 −41
Original line number Diff line number Diff line
@@ -75,6 +75,20 @@ public final class UserInputHandler<T extends InputEvent>
        mKeyListener = new KeyInputHandler();
    }

    @Override
    public boolean onDown(MotionEvent e) {
        try (T event = mEventConverter.apply(e)) {
            return onDown(event);
        }
    }

    @VisibleForTesting
    boolean onDown(T event) {
        return event.isMouseEvent()
                ? mMouseDelegate.onDown(event)
                : mTouchDelegate.onDown(event);
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        try (T event = mEventConverter.apply(e)) {
@@ -132,9 +146,12 @@ public final class UserInputHandler<T extends InputEvent>
        mTouchDelegate.onLongPress(event);
    }

    public boolean onSingleRightClickUp(MotionEvent e) {
    // Only events from RecyclerView are fed into UserInputHandler#onDown.
    // ListeningGestureDetector#onTouch directly calls this method to support context menu in empty
    // view
    boolean onRightClick(MotionEvent e) {
        try (T event = mEventConverter.apply(e)) {
            return mMouseDelegate.onSingleRightClickUp(event);
            return mMouseDelegate.onRightClick(event);
        }
    }

@@ -143,11 +160,6 @@ public final class UserInputHandler<T extends InputEvent>
        return mKeyListener.onKey(doc, keyCode, event);
    }

    // TODO: Isolate this hack...see if we can't get this solved at the platform level.
    public void setLastButtonState(int state) {
        mMouseDelegate.setLastButtonState(state);
    }

    private boolean activateDocument(DocumentDetails doc) {
        return mActivateHandler.accept(doc);
    }
@@ -169,6 +181,10 @@ public final class UserInputHandler<T extends InputEvent>

    private final class TouchInputDelegate {

        boolean onDown(T event) {
            return false;
        }

        boolean onSingleTapUp(T event) {
            if (!event.isOverItem()) {
                if (DEBUG) Log.d(TAG, "Tap on non-item. Clearing selection.");
@@ -220,25 +236,28 @@ public final class UserInputHandler<T extends InputEvent>
    }

    private final class MouseInputDelegate {

        // From the RecyclerView, we get two events sent to
        // ListeningGestureDetector#onInterceptTouchEvent on a mouse click; we first get an
        // ACTION_DOWN Event for clicking on the mouse, and then an ACTION_UP event from releasing
        // the mouse click. ACTION_UP event doesn't have information regarding the button (primary
        // vs. secondary), so we have to save that somewhere first from ACTION_DOWN, and then reuse
        // it later. The ACTION_DOWN event doesn't get forwarded to UserInputListener,
        // so we have open up a public set method to set it.
        private int mLastButtonState = -1;

        // true when the previous event has consumed a right click motion event
        private boolean mAteRightClick;

        // The event has been handled in onSingleTapUp
        private boolean mHandledTapUp;
        // true when the previous event has consumed a right click motion event
        private boolean mHandledOnDown;

        boolean onDown(T event) {
            if (event.isSecondaryButtonPressed()) {
                assert(!mHandledOnDown);
                mHandledOnDown = true;
                return onRightClick(event);
            }
            return false;
        }

        boolean onSingleTapUp(T event) {
            if (eatRightClick()) {
                return onSingleRightClickUp(event);

            // See b/27377794. Since we don't get a button state back from UP events, we have to
            // explicitly save this state to know whether something was previously handled by
            // DOWN events or not.
            if (mHandledOnDown) {
                mHandledOnDown = false;
                return false;
            }

            if (!event.isOverItem()) {
@@ -272,10 +291,6 @@ public final class UserInputHandler<T extends InputEvent>
        }

        boolean onSingleTapConfirmed(T event) {
            if (mAteRightClick) {
                mAteRightClick = false;
                return false;
            }
            if (mHandledTapUp) {
                mHandledTapUp = false;
                return false;
@@ -316,23 +331,9 @@ public final class UserInputHandler<T extends InputEvent>
            }
        }

        private boolean onSingleRightClickUp(T event) {
        private boolean onRightClick(T event) {
            return mRightClickHandler.apply(event);
        }

        // hack alert from here through end of class.
        private void setLastButtonState(int state) {
            mLastButtonState = state;
        }

        private boolean eatRightClick() {
            if (mLastButtonState == MotionEvent.BUTTON_SECONDARY) {
                mLastButtonState = -1;
                mAteRightClick = true;
                return true;
            }
            return false;
        }
    }

    private final class KeyInputHandler {
+7 −0
Original line number Diff line number Diff line
@@ -105,6 +105,13 @@ public class TestInputEvent implements Events.InputEvent {
        return e;
    }

    public static TestInputEvent rightClick(int position) {
        TestInputEvent e = new TestInputEvent(position);
        e.mouseEvent = true;
        e.secondaryButtonPressed = true;
        return e;
    }

    public static TestInputEvent shiftClick(int position) {
        TestInputEvent e = new TestInputEvent(position);
        e.mouseEvent = true;
+6 −0
Original line number Diff line number Diff line
@@ -86,6 +86,12 @@ public final class UserInputHandler_MouseTest {
        mSelection.assertSelection(11);
    }

    @Test
    public void testRightClickDown_StartsContextMenu() {
        mInputHandler.onDown(mEvent.secondary().build());
        mRightClickHandler.assertLastArgument(mEvent.secondary().build());
    }

    @Test
    public void testUnconfirmedClick_AddsToExistingSelection() {
        mInputHandler.onSingleTapConfirmed(mEvent.at(7).build());
+6 −0
Original line number Diff line number Diff line
@@ -125,6 +125,11 @@ public class TestEvent extends TestInputEvent implements DocumentDetails {
            return this;
        }

        public Builder secondary() {
            mState.secondaryButtonPressed = true;
            return this;
        }

        public TestEvent build() {
            // Return a copy, so nobody can mess w/ our internal state.
            TestEvent e = new TestEvent();
@@ -132,6 +137,7 @@ public class TestEvent extends TestInputEvent implements DocumentDetails {
            e.modelId = mState.modelId;
            e.shiftKeyDow = mState.shiftKeyDow;
            e.mouseEvent = mState.mouseEvent;
            e.secondaryButtonPressed = mState.secondaryButtonPressed;
            return e;
        }
    }