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

Commit 6e5f73a2 authored by Shu Chen's avatar Shu Chen Committed by Automerger Merge Worker
Browse files

Merge "Prevents multi touch among TextView and handle reviews." into rvc-dev...

Merge "Prevents multi touch among TextView and handle reviews." into rvc-dev am: 9b6af1dc am: d9b3d483 am: 8d097304

Change-Id: Ib3dedaf2c04c835e40a54a7474e9e3e75cc3c4ea
parents 157fb5ad 8d097304
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -5440,6 +5440,9 @@ public class Editor {

        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            if (!mTextView.isFromPrimePointer(ev, true)) {
                return true;
            }
            if (mFlagInsertionHandleGesturesEnabled && mFlagCursorDragFromAnywhereEnabled) {
                // Should only enable touch through when cursor drag is enabled.
                // Otherwise the insertion handle view cannot be moved.
@@ -5908,6 +5911,9 @@ public class Editor {

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (!mTextView.isFromPrimePointer(event, true)) {
                return true;
            }
            boolean superResult = super.onTouchEvent(event);

            switch (event.getActionMasked()) {
+46 −0
Original line number Diff line number Diff line
@@ -855,6 +855,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    int mTextEditSuggestionContainerLayout;
    int mTextEditSuggestionHighlightStyle;
    private static final int NO_POINTER_ID = -1;
    /**
     * The prime (the 1st finger) pointer id which is used as a lock to prevent multi touch among
     * TextView and the handle views which are rendered on popup windows.
     */
    private int mPrimePointerId = NO_POINTER_ID;
    /**
     * Whether the prime pointer is from the event delivered to selection handle or insertion
     * handle.
     */
    private boolean mIsPrimePointerFromHandleView;
    /**
     * {@link EditText} specific data, created on demand when one of the Editor fields is used.
     * See {@link #createEditorIfNeeded()}.
@@ -10886,6 +10899,36 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        }
    }
    /**
     * Called from onTouchEvent() to prevent the touches by secondary fingers.
     * Dragging on handles can revise cursor/selection, so can dragging on the text view.
     * This method is a lock to avoid processing multiple fingers on both text view and handles.
     * Note: multiple fingers on handles (e.g. 2 fingers on the 2 selection handles) should work.
     *
     * @param event The motion event that is being handled and carries the pointer info.
     * @param fromHandleView true if the event is delivered to selection handle or insertion
     * handle; false if this event is delivered to TextView.
     * @return Returns true to indicate that onTouchEvent() can continue processing the motion
     * event, otherwise false.
     *  - Always returns true for the first finger.
     *  - For secondary fingers, if the first or current finger is from TextView, returns false.
     *    This is to make touch mutually exclusive between the TextView and the handles, but
     *    not among the handles.
     */
    boolean isFromPrimePointer(MotionEvent event, boolean fromHandleView) {
        if (mPrimePointerId == NO_POINTER_ID)  {
            mPrimePointerId = event.getPointerId(0);
            mIsPrimePointerFromHandleView = fromHandleView;
        } else if (mPrimePointerId != event.getPointerId(0)) {
            return mIsPrimePointerFromHandleView && fromHandleView;
        }
        if (event.getActionMasked() == MotionEvent.ACTION_UP
            || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
            mPrimePointerId = -1;
        }
        return true;
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (DEBUG_CURSOR) {
@@ -10894,6 +10937,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                    MotionEvent.actionToString(event.getActionMasked()),
                    event.getX(), event.getY());
        }
        if (!isFromPrimePointer(event, false)) {
            return true;
        }
        final int action = event.getActionMasked();
        if (mEditor != null) {
+59 −0
Original line number Diff line number Diff line
@@ -527,6 +527,47 @@ public class EditorCursorDragTest {
                .isEqualTo(2);
    }

    @Test
    public void testCursorDrag_multiTouch() throws Throwable {
        String text = "line1: This is the 1st line: A";
        onView(withId(R.id.textview)).perform(replaceText(text));
        TextView tv = mActivity.findViewById(R.id.textview);
        Editor editor = tv.getEditorForTesting();
        final int startIndex = text.indexOf("1st line");
        Layout layout = tv.getLayout();
        final float cursorStartX =
                layout.getPrimaryHorizontal(startIndex) + tv.getTotalPaddingLeft();
        final float cursorStartY = layout.getLineTop(1) + tv.getTotalPaddingTop();

        // Taps to show the insertion handle.
        tapAtPoint(tv, cursorStartX, cursorStartY);
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex));
        View handleView = editor.getInsertionController().getHandle();

        // Taps & holds the insertion handle.
        long handleDownTime = sTicker.addAndGet(10_000);
        long eventTime = handleDownTime;
        dispatchTouchEvent(handleView, downEvent(handleView, handleDownTime, eventTime++, 0, 0));

        // Tries to Drag the cursor, with the pointer id > 0 (meaning the 2nd finger).
        long cursorDownTime = eventTime++;
        dispatchTouchEvent(tv, obtainTouchEventWithPointerId(
                tv, MotionEvent.ACTION_DOWN, cursorDownTime, eventTime++, 1,
                cursorStartX - 50, cursorStartY));
        dispatchTouchEvent(tv, obtainTouchEventWithPointerId(
                tv, MotionEvent.ACTION_MOVE, cursorDownTime, eventTime++, 1,
                cursorStartX - 100, cursorStartY));
        dispatchTouchEvent(tv, obtainTouchEventWithPointerId(
                tv, MotionEvent.ACTION_UP, cursorDownTime, eventTime++, 1,
                cursorStartX - 100, cursorStartY));

        // Checks the cursor drag doesn't work while the handle is being hold.
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex));

        // Finger up on the  insertion handle.
        dispatchTouchEvent(handleView, upEvent(handleView, handleDownTime, eventTime, 0, 0));
    }

    @Test
    public void testCursorDrag_snapDistance() throws Throwable {
        String text = "line1: This is the 1st line: A\n"
@@ -626,6 +667,24 @@ public class EditorCursorDragTest {
        return event;
    }

    private MotionEvent obtainTouchEventWithPointerId(
            View view, int action, long downTime, long eventTime, int pointerId, float x, float y) {
        Rect r = new Rect();
        view.getBoundsOnScreen(r);
        float rawX = x + r.left;
        float rawY = y + r.top;
        MotionEvent.PointerCoords coordinates = new MotionEvent.PointerCoords();
        coordinates.x = rawX;
        coordinates.y = rawY;
        MotionEvent event = MotionEvent.obtain(
                downTime, eventTime, action, 1, new int[] {pointerId},
                new MotionEvent.PointerCoords[] {coordinates},
                0, 1f, 1f, 0, 0, 0, 0);
        view.toLocalMotionEvent(event);
        mMotionEvents.add(event);
        return event;
    }

    private MotionEvent obtainMouseEvent(
            View view, int action, long downTime, long eventTime, float x, float y) {
        MotionEvent event = obtainTouchEvent(view, action, downTime, eventTime, x, y);