From c0728cc042bd5cc6ab520d3395deec50481f8f5a Mon Sep 17 00:00:00 2001 From: Gilles Debunne Date: Mon, 11 Oct 2010 17:12:39 -0700 Subject: [PATCH] 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 --- core/java/android/widget/TextView.java | 57 ++++++++++++++++++-------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index ae5860ad23956..b3da9dd1c1263 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -91,6 +91,7 @@ import android.view.LayoutInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; @@ -6820,16 +6821,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean onTouchEvent(MotionEvent event) { final int action = event.getActionMasked(); - if (action == MotionEvent.ACTION_DOWN) { - if (hasInsertionController()) { - getInsertionController().onTouchEvent(event); - } - if (hasSelectionController()) { - getSelectionController().onTouchEvent(event); - } - // Reset this state; it will be re-set if super.onTouchEvent - // causes focus to move to the view. + if (mInsertionPointCursorController != null) { + mInsertionPointCursorController.onTouchEvent(event); + } + if (mSelectionModifierCursorController != null) { + mSelectionModifierCursorController.onTouchEvent(event); + } + + if (action == MotionEvent.ACTION_DOWN) { + // Reset this state; it will be re-set if super.onTouchEvent + // causes focus to move to the view. mTouchFocusSelected = false; mScrolled = false; } @@ -6847,13 +6849,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if ((mMovement != null || onCheckIsTextEditor()) && mText instanceof Spannable && mLayout != null) { - if (hasInsertionController()) { - getInsertionController().onTouchEvent(event); - } - if (hasSelectionController()) { - getSelectionController().onTouchEvent(event); - } - boolean handled = false; // Save previous selection, in case this event is used to show the IME. @@ -7782,7 +7777,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } mDrawable = mSelectHandleLeft; handleWidth = mDrawable.getIntrinsicWidth(); - mHotspotX = handleWidth / 4 * 3; + mHotspotX = (handleWidth * 3) / 4; break; } @@ -7949,6 +7944,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mIsDragging = true; break; } + case MotionEvent.ACTION_MOVE: { final float rawX = ev.getRawX(); final float rawY = ev.getRawY(); @@ -7959,6 +7955,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; } + case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mIsDragging = false; @@ -8073,6 +8070,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mMinTouchOffset, mMaxTouchOffset; // Whether selection anchors are active private boolean mIsShowing; + // Double tap detection + private long mPreviousTapUpTime = 0; + private int mPreviousTapPositionX; + private int mPreviousTapPositionY; SelectionModifierCursorController() { mStartHandle = new HandleView(this, HandleView.LEFT); @@ -8167,6 +8168,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Remember finger down position, to be able to start selection from there mMinTouchOffset = mMaxTouchOffset = getOffset(x, y); + // Double tap detection + long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime; + if (duration <= ViewConfiguration.getDoubleTapTimeout()) { + final int deltaX = x - mPreviousTapPositionX; + final int deltaY = y - mPreviousTapPositionY; + final int distanceSquared = deltaX * deltaX + deltaY * deltaY; + final int doubleTapSlop = ViewConfiguration.get(getContext()).getScaledDoubleTapSlop(); + final int slopSquared = doubleTapSlop * doubleTapSlop; + if (distanceSquared < slopSquared) { + startTextSelectionMode(); + // Hacky: onTapUpEvent will open a context menu with cut/copy + // Prevent this by hiding handles which will be revived instead. + hide(); + } + } + mPreviousTapPositionX = x; + mPreviousTapPositionY = y; + break; case MotionEvent.ACTION_POINTER_DOWN: @@ -8178,6 +8197,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener updateMinAndMaxOffsets(event); } break; + + case MotionEvent.ACTION_UP: + mPreviousTapUpTime = SystemClock.uptimeMillis(); + break; } } return false;