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

Commit c18581d9 authored by Nikita Dubrovsky's avatar Nikita Dubrovsky Committed by Android (Google) Code Review
Browse files

Merge "Suppress cursor drag when the user swipes vertically"

parents 4d539605 cd36c5ea
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -5738,7 +5738,8 @@ public class Editor {
                    } else if (FLAG_ENABLE_CURSOR_DRAG
                                && mTextView.getLayout() != null
                                && mTextView.isFocused()
                                && mTouchState.isMovedEnoughForDrag()) {
                                && mTouchState.isMovedEnoughForDrag()
                                && !mTouchState.isDragCloseToVertical()) {
                        startCursorDrag(event);
                    }
                    break;
+23 −2
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ public class EditorTouchState {
    private boolean mMultiTapInSameArea;

    private boolean mMovedEnoughForDrag;
    private boolean mIsDragCloseToVertical;

    public float getLastDownX() {
        return mLastDownX;
@@ -94,6 +95,10 @@ public class EditorTouchState {
        return mMovedEnoughForDrag;
    }

    public boolean isDragCloseToVertical() {
        return mIsDragCloseToVertical;
    }

    /**
     * Updates the state based on the new event.
     */
@@ -129,6 +134,7 @@ public class EditorTouchState {
            mLastDownX = event.getX();
            mLastDownY = event.getY();
            mMovedEnoughForDrag = false;
            mIsDragCloseToVertical = false;
        } else if (action == MotionEvent.ACTION_UP) {
            if (TextView.DEBUG_CURSOR) {
                logCursor("EditorTouchState", "ACTION_UP");
@@ -137,9 +143,24 @@ public class EditorTouchState {
            mLastUpY = event.getY();
            mLastUpMillis = event.getEventTime();
            mMovedEnoughForDrag = false;
            mIsDragCloseToVertical = false;
        } else if (action == MotionEvent.ACTION_MOVE) {
            mMovedEnoughForDrag = !isDistanceWithin(mLastDownX, mLastDownY,
                    event.getX(), event.getY(), config.getScaledTouchSlop());
            if (!mMovedEnoughForDrag) {
                float deltaX = event.getX() - mLastDownX;
                float deltaY = event.getY() - mLastDownY;
                float deltaXSquared = deltaX * deltaX;
                float distanceSquared = (deltaXSquared) + (deltaY * deltaY);
                int touchSlop = config.getScaledTouchSlop();
                mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop;
                if (mMovedEnoughForDrag) {
                    // If the direction of the swipe motion is within 30 degrees of vertical, it is
                    // considered a vertical drag. We don't actually have to compute the angle to
                    // implement the check though. When the angle is exactly 30 degrees from
                    // vertical, 2*deltaX = distance. When the angle is less than 30 degrees from
                    // vertical, 2*deltaX < distance.
                    mIsDragCloseToVertical = (4 * deltaXSquared) <= distanceSquared;
                }
            }
        }
    }

+102 −54
Original line number Diff line number Diff line
@@ -16,13 +16,18 @@

package android.widget;

import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.dragOnText;
import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
import static android.widget.espresso.TextViewAssertions.hasSelection;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.replaceText;
import static androidx.test.espresso.matcher.ViewMatchers.withId;

import static org.hamcrest.Matchers.emptyString;
import static org.hamcrest.Matchers.not;

import android.app.Activity;
import android.app.Instrumentation;

@@ -70,107 +75,150 @@ public class EditorCursorDragTest {
        onView(withId(R.id.textview)).perform(replaceText(text));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));

        // Drag left to right. The cursor should end up at the position where the finger is lifted.
        // Swipe left to right to drag the cursor. The cursor should end up at the position where
        // the finger is lifted.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("llo"), text.indexOf("!")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(11));

        // Drag right to left. The cursor should end up at the position where the finger is lifted.
        // Swipe right to left to drag the cursor. The cursor should end up at the position where
        // the finger is lifted.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("!"), text.indexOf("llo")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(2));
    }

    @Test
    public void testCursorDrag_horizontal_whenTextViewContentsLargerThanScreen() throws Throwable {
        String text = "Hello world!"
                + Strings.repeat("\n", 500) + "012345middle"
                + Strings.repeat("\n", 10) + "012345last";
        String text = "Hello world!\n\n"
                + Strings.repeat("Bla\n\n", 200) + "Bye";
        onView(withId(R.id.textview)).perform(replaceText(text));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));

        // Drag left to right. The cursor should end up at the position where the finger is lifted.
        // Swipe left to right to drag the cursor. The cursor should end up at the position where
        // the finger is lifted.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("llo"), text.indexOf("!")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(11));

        // Drag right to left. The cursor should end up at the position where the finger is lifted.
        // Swipe right to left to drag the cursor. The cursor should end up at the position where
        // the finger is lifted.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("!"), text.indexOf("llo")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(2));
    }

    @Test
    public void testCursorDrag_diagonal_whenTextViewContentsLargerThanScreen() throws Throwable {
    public void testCursorDrag_diagonal_whenTextViewContentsFitOnScreen() throws Throwable {
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= 9; i++) {
            sb.append("line").append(i).append("\n");
        }
        sb.append(Strings.repeat("0123456789\n\n", 500)).append("Last line");
        String text = sb.toString();
        onView(withId(R.id.textview)).perform(replaceText(text));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));

        // Drag along a diagonal path.
        // Swipe along a diagonal path. This should drag the cursor.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("2")));

        // Drag along a steeper diagonal path.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("9")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("9")));
        // Swipe along a steeper diagonal path. This should still drag the cursor.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("3")));

        // Swipe right-down along a very steep diagonal path. This should not drag the cursor.
        // Normally this would trigger a scroll, but since the full view fits on the screen there
        // is nothing to scroll and the gesture will trigger a selection drag.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("7")));
        onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));

        // Swipe right-up along a very steep diagonal path. This should not drag the cursor.
        // Normally this would trigger a scroll, but since the full view fits on the screen there
        // is nothing to scroll and the gesture will trigger a selection drag.
        int index = text.indexOf("line9");
        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(index));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line7"), text.indexOf("1")));
        onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));
    }

        // Drag along an almost vertical path.
        // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("ne1"), text.indexOf("9")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("9")));
    @Test
    public void testCursorDrag_diagonal_whenTextViewContentsLargerThanScreen() throws Throwable {
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= 9; i++) {
            sb.append("line").append(i).append("\n");
        }
        sb.append(Strings.repeat("0123456789\n", 400)).append("Last");
        String text = sb.toString();
        onView(withId(R.id.textview)).perform(replaceText(text));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));

        // Drag along a vertical path from line 1 to line 9.
        // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("e1"), text.indexOf("e9")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("e9")));
        // Swipe along a diagonal path. This should drag the cursor.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("2")));

        // Drag along a vertical path from line 9 to line 1.
        // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("e9"), text.indexOf("e1")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("e1")));
        // Swipe along a steeper diagonal path. This should still drag the cursor.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("3")));

        // Swipe right-down along a very steep diagonal path. This should not drag the cursor.
        // Normally this would trigger a scroll up, but since the view is already at the top there
        // is nothing to scroll and the gesture will trigger a selection drag.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("7")));
        onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));

        // Swipe right-up along a very steep diagonal path. This should not drag the cursor. This
        // will trigger a downward scroll and the cursor position will not change.
        int index = text.indexOf("line9");
        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(index));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line7"), text.indexOf("1")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
    }

    @Test
    public void testCursorDrag_vertical_whenTextViewContentsFitOnScreen() throws Throwable {
        String text = "012first\n\n" + Strings.repeat("012345\n\n", 10) + "012last";
        String text = "012345_aaa\n"
                + "0123456789\n"
                + "012345_bbb\n"
                + "0123456789\n"
                + "012345_ccc\n"
                + "0123456789\n"
                + "012345_ddd";
        onView(withId(R.id.textview)).perform(replaceText(text));

        // Swipe up vertically. This should not drag the cursor. Since there's also nothing to
        // scroll, the gesture will trigger a selection drag.
        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("bbb"), text.indexOf("aaa")));
        onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));

        // Drag down. Since neither the TextView nor its container require scrolling, the cursor
        // drag should execute and the cursor should end up at the position where the finger is
        // lifted.
        onView(withId(R.id.textview)).perform(
                dragOnText(text.indexOf("first"), text.indexOf("last")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length() - 4));

        // Drag up. Since neither the TextView nor its container require scrolling, the cursor
        // drag should execute and the cursor should end up at the position where the finger is
        // lifted.
        onView(withId(R.id.textview)).perform(
                dragOnText(text.indexOf("last"), text.indexOf("first")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(3));
        // Swipe down vertically. This should not drag the cursor. Since there's also nothing to
        // scroll, the gesture will trigger a selection drag.
        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("ccc"), text.indexOf("ddd")));
        onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));
    }

    @Test
    public void testCursorDrag_vertical_whenTextViewContentsLargerThanScreen() throws Throwable {
        String text = "012345first\n\n"
                + Strings.repeat("0123456789\n\n", 10) + "012345middle"
                + Strings.repeat("0123456789\n\n", 500) + "012345last";
        String text = "012345_aaa\n"
                + "0123456789\n"
                + "012345_bbb\n"
                + "0123456789\n"
                + "012345_ccc\n"
                + "0123456789\n"
                + "012345_ddd\n"
                + Strings.repeat("0123456789\n", 400) + "012345_zzz";
        onView(withId(R.id.textview)).perform(replaceText(text));
        int initialCursorPosition = 0;
        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf("ddd")));
        int initialCursorPosition = text.indexOf("ddd");
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(initialCursorPosition));

        // Swipe up vertically. This should trigger a downward scroll.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("bbb"), text.indexOf("aaa")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(initialCursorPosition));

        // Drag up.
        // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
        onView(withId(R.id.textview)).perform(
                dragOnText(text.indexOf("middle"), text.indexOf("first")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("first")));

        // Drag down.
        // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
        onView(withId(R.id.textview)).perform(
                dragOnText(text.indexOf("first"), text.indexOf("middle")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("middle")));
        // Swipe down vertically. This should trigger an upward scroll.
        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("ccc"), text.indexOf("ddd")));
        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(initialCursorPosition));
    }
}