Loading core/java/android/widget/Editor.java +2 −1 Original line number Diff line number Diff line Loading @@ -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; Loading core/java/android/widget/EditorTouchState.java +23 −2 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ public class EditorTouchState { private boolean mMultiTapInSameArea; private boolean mMovedEnoughForDrag; private boolean mIsDragCloseToVertical; public float getLastDownX() { return mLastDownX; Loading Loading @@ -94,6 +95,10 @@ public class EditorTouchState { return mMovedEnoughForDrag; } public boolean isDragCloseToVertical() { return mIsDragCloseToVertical; } /** * Updates the state based on the new event. */ Loading Loading @@ -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"); Loading @@ -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; } } } } Loading core/tests/coretests/src/android/widget/EditorCursorDragTest.java +102 −54 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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)); } } Loading
core/java/android/widget/Editor.java +2 −1 Original line number Diff line number Diff line Loading @@ -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; Loading
core/java/android/widget/EditorTouchState.java +23 −2 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ public class EditorTouchState { private boolean mMultiTapInSameArea; private boolean mMovedEnoughForDrag; private boolean mIsDragCloseToVertical; public float getLastDownX() { return mLastDownX; Loading Loading @@ -94,6 +95,10 @@ public class EditorTouchState { return mMovedEnoughForDrag; } public boolean isDragCloseToVertical() { return mIsDragCloseToVertical; } /** * Updates the state based on the new event. */ Loading Loading @@ -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"); Loading @@ -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; } } } } Loading
core/tests/coretests/src/android/widget/EditorCursorDragTest.java +102 −54 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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)); } }