Merge "Prevents multi touch among TextView and handle reviews." into rvc-dev am: 9b6af1dcc5
Change-Id: I8add9f69ccb0d582bbc6be0582ddb04696a8a8f6
This commit is contained in:
@@ -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()) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user