Merge "Simplify state in SelectionModifierCursorController"
This commit is contained in:
committed by
Android (Google) Code Review
commit
28bd186e32
@@ -5921,8 +5921,6 @@ public class Editor {
|
||||
// The offsets of that last touch down event. Remembered to start selection there.
|
||||
private int mMinTouchOffset, mMaxTouchOffset;
|
||||
|
||||
private boolean mGestureStayedInTapRegion;
|
||||
|
||||
// Where the user first starts the drag motion.
|
||||
private int mStartOffset = -1;
|
||||
|
||||
@@ -6029,8 +6027,7 @@ public class Editor {
|
||||
eventX, eventY);
|
||||
|
||||
// Double tap detection
|
||||
if (mGestureStayedInTapRegion
|
||||
&& mTouchState.isMultiTapInSameArea()
|
||||
if (mTouchState.isMultiTapInSameArea()
|
||||
&& (isMouse || isPositionOnText(eventX, eventY))) {
|
||||
if (TextView.DEBUG_CURSOR) {
|
||||
logCursor("SelectionModifierCursorController: onTouchEvent",
|
||||
@@ -6043,7 +6040,6 @@ public class Editor {
|
||||
}
|
||||
mDiscardNextActionUp = true;
|
||||
}
|
||||
mGestureStayedInTapRegion = true;
|
||||
mHaventMovedEnoughToStartDrag = true;
|
||||
}
|
||||
break;
|
||||
@@ -6059,25 +6055,8 @@ public class Editor {
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final ViewConfiguration viewConfig = ViewConfiguration.get(
|
||||
mTextView.getContext());
|
||||
|
||||
if (mGestureStayedInTapRegion || mHaventMovedEnoughToStartDrag) {
|
||||
final float deltaX = eventX - mTouchState.getLastDownX();
|
||||
final float deltaY = eventY - mTouchState.getLastDownY();
|
||||
final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
|
||||
|
||||
if (mGestureStayedInTapRegion) {
|
||||
int doubleTapTouchSlop = viewConfig.getScaledDoubleTapTouchSlop();
|
||||
mGestureStayedInTapRegion =
|
||||
distanceSquared <= doubleTapTouchSlop * doubleTapTouchSlop;
|
||||
}
|
||||
if (mHaventMovedEnoughToStartDrag) {
|
||||
// We don't start dragging until the user has moved enough.
|
||||
int touchSlop = viewConfig.getScaledTouchSlop();
|
||||
mHaventMovedEnoughToStartDrag =
|
||||
distanceSquared <= touchSlop * touchSlop;
|
||||
}
|
||||
if (mHaventMovedEnoughToStartDrag) {
|
||||
mHaventMovedEnoughToStartDrag = !mTouchState.isMovedEnoughForDrag();
|
||||
}
|
||||
|
||||
if (isMouse && !isDragAcceleratorActive()) {
|
||||
|
||||
@@ -31,13 +31,15 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Helper class used by {@link Editor} to track state for touch events.
|
||||
* Helper class used by {@link Editor} to track state for touch events. Ideally the logic here
|
||||
* should be replaced with {@link android.view.GestureDetector}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public class EditorTouchState {
|
||||
private float mLastDownX, mLastDownY;
|
||||
private long mLastDownMillis;
|
||||
private float mLastUpX, mLastUpY;
|
||||
private long mLastUpMillis;
|
||||
|
||||
@@ -106,9 +108,18 @@ public class EditorTouchState {
|
||||
final int action = event.getActionMasked();
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
|
||||
|
||||
// We check both the time between the last up and current down event, as well as the
|
||||
// time between the first down and up events. The latter check is necessary to handle
|
||||
// the case when the user taps, drags/holds for some time, and then lifts up and
|
||||
// quickly taps in the same area. This scenario should not be treated as a double-tap.
|
||||
// This follows the behavior in GestureDetector.
|
||||
final long millisSinceLastUp = event.getEventTime() - mLastUpMillis;
|
||||
final long millisBetweenLastDownAndLastUp = mLastUpMillis - mLastDownMillis;
|
||||
|
||||
// Detect double tap and triple click.
|
||||
if (millisSinceLastUp <= ViewConfiguration.getDoubleTapTimeout()
|
||||
&& millisBetweenLastDownAndLastUp <= ViewConfiguration.getDoubleTapTimeout()
|
||||
&& (mMultiTapStatus == MultiTapStatus.FIRST_TAP
|
||||
|| (mMultiTapStatus == MultiTapStatus.DOUBLE_TAP && isMouse))) {
|
||||
if (mMultiTapStatus == MultiTapStatus.FIRST_TAP) {
|
||||
@@ -133,6 +144,7 @@ public class EditorTouchState {
|
||||
}
|
||||
mLastDownX = event.getX();
|
||||
mLastDownY = event.getY();
|
||||
mLastDownMillis = event.getEventTime();
|
||||
mMovedEnoughForDrag = false;
|
||||
mIsDragCloseToVertical = false;
|
||||
} else if (action == MotionEvent.ACTION_UP) {
|
||||
|
||||
@@ -225,6 +225,61 @@ public class EditorCursorDragTest {
|
||||
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(initialCursorPosition));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditor_onTouchEvent_quickTapAfterDrag() throws Throwable {
|
||||
String text = "Hi world!";
|
||||
onView(withId(R.id.textview)).perform(replaceText(text));
|
||||
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
|
||||
|
||||
TextView tv = mActivity.findViewById(R.id.textview);
|
||||
Editor editor = tv.getEditorForTesting();
|
||||
|
||||
// Simulate a tap-and-drag gesture.
|
||||
long event1Time = 1001;
|
||||
MotionEvent event1 = downEvent(event1Time, event1Time, 5f, 10f);
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
long event2Time = 1002;
|
||||
MotionEvent event2 = moveEvent(event1Time, event2Time, 50f, 10f);
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
|
||||
assertTrue(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
long event3Time = 1003;
|
||||
MotionEvent event3 = moveEvent(event1Time, event3Time, 100f, 10f);
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
|
||||
assertTrue(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
long event4Time = 2004;
|
||||
MotionEvent event4 = upEvent(event1Time, event4Time, 100f, 10f);
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
// Simulate a quick tap after the drag, near the location where the drag ended.
|
||||
long event5Time = 2005;
|
||||
MotionEvent event5 = downEvent(event5Time, event5Time, 90f, 10f);
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event5));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
long event6Time = 2006;
|
||||
MotionEvent event6 = upEvent(event5Time, event6Time, 90f, 10f);
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event6));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
// Simulate another quick tap in the same location; now selection should be triggered.
|
||||
long event7Time = 2007;
|
||||
MotionEvent event7 = downEvent(event7Time, event7Time, 90f, 10f);
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event7));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertTrue(editor.getSelectionController().isCursorBeingModified());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditor_onTouchEvent_cursorDrag() throws Throwable {
|
||||
String text = "testEditor_onTouchEvent_cursorDrag";
|
||||
@@ -237,29 +292,25 @@ public class EditorCursorDragTest {
|
||||
// Simulate a tap-and-drag gesture. This should trigger a cursor drag.
|
||||
long event1Time = 1001;
|
||||
MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
|
||||
mActivity.runOnUiThread(() -> editor.onTouchEvent(event1));
|
||||
mInstrumentation.waitForIdleSync();
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
long event2Time = 1002;
|
||||
MotionEvent event2 = moveEvent(event1Time, event2Time, 21f, 30f);
|
||||
mActivity.runOnUiThread(() -> editor.onTouchEvent(event2));
|
||||
mInstrumentation.waitForIdleSync();
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
long event3Time = 1003;
|
||||
MotionEvent event3 = moveEvent(event3Time, event3Time, 120f, 30f);
|
||||
mActivity.runOnUiThread(() -> editor.onTouchEvent(event3));
|
||||
mInstrumentation.waitForIdleSync();
|
||||
MotionEvent event3 = moveEvent(event1Time, event3Time, 120f, 30f);
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
|
||||
assertTrue(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
long event4Time = 1004;
|
||||
MotionEvent event4 = upEvent(event3Time, event4Time, 120f, 30f);
|
||||
mActivity.runOnUiThread(() -> editor.onTouchEvent(event4));
|
||||
mInstrumentation.waitForIdleSync();
|
||||
MotionEvent event4 = upEvent(event1Time, event4Time, 120f, 30f);
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
}
|
||||
@@ -276,36 +327,31 @@ public class EditorCursorDragTest {
|
||||
// Simulate a double-tap followed by a drag. This should trigger a selection drag.
|
||||
long event1Time = 1001;
|
||||
MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
|
||||
mActivity.runOnUiThread(() -> editor.onTouchEvent(event1));
|
||||
mInstrumentation.waitForIdleSync();
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
long event2Time = 1002;
|
||||
MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
|
||||
mActivity.runOnUiThread(() -> editor.onTouchEvent(event2));
|
||||
mInstrumentation.waitForIdleSync();
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
long event3Time = 1003;
|
||||
MotionEvent event3 = downEvent(event3Time, event3Time, 20f, 30f);
|
||||
mActivity.runOnUiThread(() -> editor.onTouchEvent(event3));
|
||||
mInstrumentation.waitForIdleSync();
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertTrue(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
long event4Time = 1004;
|
||||
MotionEvent event4 = moveEvent(event3Time, event4Time, 120f, 30f);
|
||||
mActivity.runOnUiThread(() -> editor.onTouchEvent(event4));
|
||||
mInstrumentation.waitForIdleSync();
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertTrue(editor.getSelectionController().isCursorBeingModified());
|
||||
|
||||
long event5Time = 1005;
|
||||
MotionEvent event5 = upEvent(event3Time, event5Time, 120f, 30f);
|
||||
mActivity.runOnUiThread(() -> editor.onTouchEvent(event5));
|
||||
mInstrumentation.waitForIdleSync();
|
||||
mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event5));
|
||||
assertFalse(editor.getInsertionController().isCursorBeingModified());
|
||||
assertFalse(editor.getSelectionController().isCursorBeingModified());
|
||||
}
|
||||
|
||||
@@ -119,6 +119,60 @@ public class EditorTouchStateTest {
|
||||
MultiTapStatus.DOUBLE_TAP, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate_doubleTap_delayAfterFirstDownEvent() throws Exception {
|
||||
// Simulate an ACTION_DOWN event.
|
||||
long event1Time = 1000;
|
||||
MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
|
||||
mTouchState.update(event1, mConfig);
|
||||
assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
|
||||
|
||||
// Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout.
|
||||
long event2Time = 1000 + ViewConfiguration.getDoubleTapTimeout() + 1;
|
||||
MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
|
||||
mTouchState.update(event2, mConfig);
|
||||
assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false);
|
||||
|
||||
// Generate an ACTION_DOWN event whose time is within the double-tap timeout when
|
||||
// calculated from the last ACTION_UP event time. Even though the time between the last up
|
||||
// and this down event is within the double-tap timeout, this should not be considered a
|
||||
// double-tap (since the first down event had a longer delay).
|
||||
long event3Time = event2Time + 1;
|
||||
MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f);
|
||||
mTouchState.update(event3, mConfig);
|
||||
assertSingleTap(mTouchState, 22f, 33f, 20f, 30f, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate_quickTapAfterDrag() throws Exception {
|
||||
// Simulate an ACTION_DOWN event.
|
||||
long event1Time = 1000;
|
||||
MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
|
||||
mTouchState.update(event1, mConfig);
|
||||
assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
|
||||
|
||||
// Simulate an ACTION_MOVE event.
|
||||
long event2Time = 1001;
|
||||
MotionEvent event2 = moveEvent(event1Time, event2Time, 200f, 31f);
|
||||
mTouchState.update(event2, mConfig);
|
||||
assertSingleTap(mTouchState, 20f, 30f, 0, 0, true);
|
||||
|
||||
// Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout.
|
||||
long event3Time = 5000;
|
||||
MotionEvent event3 = upEvent(event1Time, event3Time, 200f, 31f);
|
||||
mTouchState.update(event3, mConfig);
|
||||
assertSingleTap(mTouchState, 20f, 30f, 200f, 31f, false);
|
||||
|
||||
// Generate an ACTION_DOWN event whose time is within the double-tap timeout when
|
||||
// calculated from the last ACTION_UP event time. Even though the time between the last up
|
||||
// and this down event is within the double-tap timeout, this should not be considered a
|
||||
// double-tap (since the first down event had a longer delay).
|
||||
long event4Time = event3Time + 1;
|
||||
MotionEvent event4 = downEvent(event4Time, event4Time, 200f, 31f);
|
||||
mTouchState.update(event4, mConfig);
|
||||
assertSingleTap(mTouchState, 200f, 31f, 200f, 31f, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate_tripleClick_mouse() throws Exception {
|
||||
// Simulate an ACTION_DOWN event.
|
||||
|
||||
Reference in New Issue
Block a user