Skip to content

Commit c0728cc

Browse files
author
Gilles Debunne
committed
Double tap triggers text selection in a TextView. DO NOT MERGE
This is a convenient way to switch to selection mode without the context menu. Context menu is still available and offerts Select Input method option. Browser overloads touch events and this feature is not available in Browser which limits the conflict with double tap zoom in browser. Change-Id: I286504cf03733d5c2fb9bc01765f713d14bbd2f4
1 parent 893ac28 commit c0728cc

File tree

1 file changed

+40
-17
lines changed

1 file changed

+40
-17
lines changed

core/java/android/widget/TextView.java

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
import android.view.MenuItem;
9292
import android.view.MotionEvent;
9393
import android.view.View;
94+
import android.view.ViewConfiguration;
9495
import android.view.ViewDebug;
9596
import android.view.ViewGroup;
9697
import android.view.ViewGroup.LayoutParams;
@@ -6820,16 +6821,17 @@ protected void onReceiveResult(int resultCode, Bundle resultData) {
68206821
@Override
68216822
public boolean onTouchEvent(MotionEvent event) {
68226823
final int action = event.getActionMasked();
6823-
if (action == MotionEvent.ACTION_DOWN) {
6824-
if (hasInsertionController()) {
6825-
getInsertionController().onTouchEvent(event);
6826-
}
6827-
if (hasSelectionController()) {
6828-
getSelectionController().onTouchEvent(event);
6829-
}
68306824

6831-
// Reset this state; it will be re-set if super.onTouchEvent
6832-
// causes focus to move to the view.
6825+
if (mInsertionPointCursorController != null) {
6826+
mInsertionPointCursorController.onTouchEvent(event);
6827+
}
6828+
if (mSelectionModifierCursorController != null) {
6829+
mSelectionModifierCursorController.onTouchEvent(event);
6830+
}
6831+
6832+
if (action == MotionEvent.ACTION_DOWN) {
6833+
// Reset this state; it will be re-set if super.onTouchEvent
6834+
// causes focus to move to the view.
68336835
mTouchFocusSelected = false;
68346836
mScrolled = false;
68356837
}
@@ -6847,13 +6849,6 @@ public boolean onTouchEvent(MotionEvent event) {
68476849
}
68486850

68496851
if ((mMovement != null || onCheckIsTextEditor()) && mText instanceof Spannable && mLayout != null) {
6850-
if (hasInsertionController()) {
6851-
getInsertionController().onTouchEvent(event);
6852-
}
6853-
if (hasSelectionController()) {
6854-
getSelectionController().onTouchEvent(event);
6855-
}
6856-
68576852
boolean handled = false;
68586853

68596854
// Save previous selection, in case this event is used to show the IME.
@@ -7782,7 +7777,7 @@ public void setOrientation(int pos) {
77827777
}
77837778
mDrawable = mSelectHandleLeft;
77847779
handleWidth = mDrawable.getIntrinsicWidth();
7785-
mHotspotX = handleWidth / 4 * 3;
7780+
mHotspotX = (handleWidth * 3) / 4;
77867781
break;
77877782
}
77887783

@@ -7949,6 +7944,7 @@ public boolean onTouchEvent(MotionEvent ev) {
79497944
mIsDragging = true;
79507945
break;
79517946
}
7947+
79527948
case MotionEvent.ACTION_MOVE: {
79537949
final float rawX = ev.getRawX();
79547950
final float rawY = ev.getRawY();
@@ -7959,6 +7955,7 @@ public boolean onTouchEvent(MotionEvent ev) {
79597955

79607956
break;
79617957
}
7958+
79627959
case MotionEvent.ACTION_UP:
79637960
case MotionEvent.ACTION_CANCEL:
79647961
mIsDragging = false;
@@ -8073,6 +8070,10 @@ private class SelectionModifierCursorController implements CursorController {
80738070
private int mMinTouchOffset, mMaxTouchOffset;
80748071
// Whether selection anchors are active
80758072
private boolean mIsShowing;
8073+
// Double tap detection
8074+
private long mPreviousTapUpTime = 0;
8075+
private int mPreviousTapPositionX;
8076+
private int mPreviousTapPositionY;
80768077

80778078
SelectionModifierCursorController() {
80788079
mStartHandle = new HandleView(this, HandleView.LEFT);
@@ -8167,6 +8168,24 @@ public boolean onTouchEvent(MotionEvent event) {
81678168
// Remember finger down position, to be able to start selection from there
81688169
mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
81698170

8171+
// Double tap detection
8172+
long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime;
8173+
if (duration <= ViewConfiguration.getDoubleTapTimeout()) {
8174+
final int deltaX = x - mPreviousTapPositionX;
8175+
final int deltaY = y - mPreviousTapPositionY;
8176+
final int distanceSquared = deltaX * deltaX + deltaY * deltaY;
8177+
final int doubleTapSlop = ViewConfiguration.get(getContext()).getScaledDoubleTapSlop();
8178+
final int slopSquared = doubleTapSlop * doubleTapSlop;
8179+
if (distanceSquared < slopSquared) {
8180+
startTextSelectionMode();
8181+
// Hacky: onTapUpEvent will open a context menu with cut/copy
8182+
// Prevent this by hiding handles which will be revived instead.
8183+
hide();
8184+
}
8185+
}
8186+
mPreviousTapPositionX = x;
8187+
mPreviousTapPositionY = y;
8188+
81708189
break;
81718190

81728191
case MotionEvent.ACTION_POINTER_DOWN:
@@ -8178,6 +8197,10 @@ public boolean onTouchEvent(MotionEvent event) {
81788197
updateMinAndMaxOffsets(event);
81798198
}
81808199
break;
8200+
8201+
case MotionEvent.ACTION_UP:
8202+
mPreviousTapUpTime = SystemClock.uptimeMillis();
8203+
break;
81818204
}
81828205
}
81838206
return false;

0 commit comments

Comments
 (0)