From fae270c108f774d48f5b8f122d46c533ad4ef2f5 Mon Sep 17 00:00:00 2001 From: Andrei Stingaceanu Date: Wed, 29 Apr 2015 14:39:40 +0100 Subject: Editor: enable double tap for selection after cut/copy. * detect double tap in the TextView and pass it to Editor. * add a timer (with getDoubleTapTimeout) for executing the single tap job in Editor. * if double tap is detected then cancel the single tap job. This way one tap shows the toolbar if within the timeframe and two taps select the word and show the toolbar. Bug: 20442587 Change-Id: I0a117939c896fcb13669346e30c9c48d319576dd --- core/java/android/widget/Editor.java | 39 +++++++++++++++++++++++++++------- core/java/android/widget/TextView.java | 19 +++++++++++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 5e33f83dd82b..a1194f7dfa95 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -209,6 +209,10 @@ public class Editor { // Set when this TextView gained focus with some text selected. Will start selection mode. boolean mCreatedWithASelection; + boolean mDoubleTap = false; + + private Runnable mSelectionModeWithoutSelectionRunnable; + // The span controller helps monitoring the changes to which the Editor needs to react: // - EasyEditSpans, for which we have some UI to display on attach and on hide // - SelectionSpans, for which we need to call updateSelection if an IME is attached @@ -349,6 +353,11 @@ public class Editor { mTextView.removeCallbacks(mShowSuggestionRunnable); } + // Cancel the single tap delayed runnable. + if (mSelectionModeWithoutSelectionRunnable != null) { + mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); + } + destroyDisplayListsData(); if (mSpellChecker != null) { @@ -3722,10 +3731,28 @@ public class Editor { public void show() { super.show(); - final long durationSinceLastCutCopyOrTextChanged = + final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - TextView.sLastCutCopyOrTextChangedTime; - if (durationSinceLastCutCopyOrTextChanged < RECENT_CUT_COPY_DURATION) { - startSelectionActionModeWithoutSelection(); + + // Cancel the single tap delayed runnable. + if (mDoubleTap && mSelectionModeWithoutSelectionRunnable != null) { + mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); + } + + // Prepare and schedule the single tap runnable to run exactly after the double tap + // timeout has passed. + if (!mDoubleTap && (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION)) { + if (mSelectionModeWithoutSelectionRunnable == null) { + mSelectionModeWithoutSelectionRunnable = new Runnable() { + public void run() { + startSelectionActionModeWithoutSelection(); + } + }; + } + + mTextView.postDelayed( + mSelectionModeWithoutSelectionRunnable, + ViewConfiguration.getDoubleTapTimeout() + 1); } hideAfterDelay(); @@ -4159,8 +4186,6 @@ public class Editor { // The offsets of that last touch down event. Remembered to start selection there. private int mMinTouchOffset, mMaxTouchOffset; - // Double tap detection - private long mPreviousTapUpTime = 0; private float mDownPositionX, mDownPositionY; private boolean mGestureStayedInTapRegion; @@ -4242,8 +4267,7 @@ public class Editor { // Double tap detection if (mGestureStayedInTapRegion) { - long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime; - if (duration <= ViewConfiguration.getDoubleTapTimeout()) { + if (mDoubleTap) { final float deltaX = x - mDownPositionX; final float deltaY = y - mDownPositionY; final float distanceSquared = deltaX * deltaX + deltaY * deltaY; @@ -4352,7 +4376,6 @@ public class Editor { break; case MotionEvent.ACTION_UP: - mPreviousTapUpTime = SystemClock.uptimeMillis(); if (mDragAcceleratorActive) { // No longer dragging to select text, let the parent intercept events. mTextView.getParent().requestDisallowInterceptTouchEvent(false); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 3df218e6b4d2..5acd79ff761b 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -599,6 +599,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private final Paint mHighlightPaint; private boolean mHighlightPathBogus = true; + private boolean mFirstTouch = false; + private long mLastTouchUpTime = 0; + // Although these fields are specific to editable text, they are not added to Editor because // they are defined by the TextView's style and are theme-dependent. int mCursorDrawableRes; @@ -8279,6 +8282,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onTouchEvent(MotionEvent event) { final int action = event.getActionMasked(); + if (mEditor != null && action == MotionEvent.ACTION_DOWN) { + // Detect double tap and inform the Editor. + if (mFirstTouch && (SystemClock.uptimeMillis() - mLastTouchUpTime) <= + ViewConfiguration.getDoubleTapTimeout()) { + mEditor.mDoubleTap = true; + mFirstTouch = false; + } else { + mEditor.mDoubleTap = false; + mFirstTouch = true; + } + } + + if (action == MotionEvent.ACTION_UP) { + mLastTouchUpTime = SystemClock.uptimeMillis(); + } + if (mEditor != null) { mEditor.onTouchEvent(event); -- cgit v1.2.3-59-g8ed1b