Added a touch up filter in text selection handles.
This filters the touch up event, so that in case the handles'
position is altered when the finger is lifted up, this unwanted
movement is discarded.
Bug 3282095
Change-Id: Ibfe8f49d979091ba49139449ecc13f47050608d9
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 623cd41..1604fde 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7931,9 +7931,6 @@
max = Math.max(0, Math.max(selStart, selEnd));
}
- ClipboardManager clipboard = (ClipboardManager)getContext()
- .getSystemService(Context.CLIPBOARD_SERVICE);
-
switch (id) {
case ID_COPY_URL:
URLSpan[] urls = ((Spanned) mText).getSpans(min, max, URLSpan.class);
@@ -8404,8 +8401,12 @@
*/
public void updatePosition(HandleView handle, int x, int y);
+ public void updateOffset(HandleView handle, int offset);
+
public void updatePosition();
+ public int getCurrentOffset(HandleView handle);
+
/**
* This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
* a chance to become active and/or visible.
@@ -8422,7 +8423,7 @@
}
private class PastePopupMenu implements OnClickListener {
- private PopupWindow mContainer;
+ private final PopupWindow mContainer;
private int mPositionX;
private int mPositionY;
private View mPasteView, mNoPasteView;
@@ -8521,12 +8522,11 @@
}
private class HandleView extends View {
- private boolean mPositionOnTop = false;
private Drawable mDrawable;
- private PopupWindow mContainer;
+ private final PopupWindow mContainer;
private int mPositionX;
private int mPositionY;
- private CursorController mController;
+ private final CursorController mController;
private boolean mIsDragging;
private float mTouchToWindowOffsetX;
private float mTouchToWindowOffsetY;
@@ -8543,6 +8543,46 @@
private PastePopupMenu mPastePopupWindow;
private Runnable mLongPressCallback;
+ // Touch-up filter: number of previous positions remembered
+ private static final int HISTORY_SIZE = 5;
+ private static final int TOUCH_UP_FILTER_DELAY = 150;
+ private final long[] mPreviousOffsetsTimes = new long[HISTORY_SIZE];
+ private final int[] mPreviousOffsets = new int[HISTORY_SIZE];
+ private int mPreviousOffsetIndex = 0;
+ private int mNumberPreviousOffsets = 0;
+
+ public void startTouchUpFilter(int offset) {
+ mNumberPreviousOffsets = 0;
+ addPositionToTouchUpFilter(offset);
+ }
+
+ public void addPositionToTouchUpFilter(int offset) {
+ if (mNumberPreviousOffsets > 0 &&
+ mPreviousOffsets[mPreviousOffsetIndex] == offset) {
+ // Make sure only actual changes of position are recorded.
+ return;
+ }
+
+ mPreviousOffsetIndex = (mPreviousOffsetIndex + 1) % HISTORY_SIZE;
+ mPreviousOffsets[mPreviousOffsetIndex] = offset;
+ mPreviousOffsetsTimes[mPreviousOffsetIndex] = SystemClock.uptimeMillis();
+ mNumberPreviousOffsets++;
+ }
+
+ public void filterOnTouchUp() {
+ final long now = SystemClock.uptimeMillis();
+ int i = 0;
+ int index = 0;
+ final int iMax = Math.min(mNumberPreviousOffsets, HISTORY_SIZE);
+ while (i < iMax) {
+ index = (mPreviousOffsetIndex - i + HISTORY_SIZE) % HISTORY_SIZE;
+ if ((now - mPreviousOffsetsTimes[index]) >= TOUCH_UP_FILTER_DELAY) break;
+ i++;
+ }
+
+ mController.updateOffset(this, mPreviousOffsets[index]);
+ }
+
public static final int LEFT = 0;
public static final int CENTER = 1;
public static final int RIGHT = 2;
@@ -8738,20 +8778,14 @@
@Override
protected void onDraw(Canvas c) {
mDrawable.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
- if (mPositionOnTop) {
- c.save();
- c.rotate(180, (mRight - mLeft) / 2, (mBottom - mTop) / 2);
- mDrawable.draw(c);
- c.restore();
- } else {
- mDrawable.draw(c);
- }
+ mDrawable.draw(c);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
+ startTouchUpFilter(mController.getCurrentOffset(this));
mDownPositionX = ev.getRawX();
mDownPositionY = ev.getRawY();
mTouchToWindowOffsetX = mDownPositionX - mPositionX;
@@ -8808,6 +8842,7 @@
}
}
}
+ filterOnTouchUp();
mIsDragging = false;
break;
@@ -8825,6 +8860,7 @@
}
void positionAtCursor(final int offset, boolean bottom) {
+ addPositionToTouchUpFilter(offset);
final int width = mDrawable.getIntrinsicWidth();
final int height = mDrawable.getIntrinsicHeight();
final int line = mLayout.getLineForOffset(offset);
@@ -8940,12 +8976,16 @@
int offset = getHysteresisOffset(x, y, previousOffset);
if (offset != previousOffset) {
- Selection.setSelection((Spannable) mText, offset);
- updatePosition();
+ updateOffset(handle, offset);
}
hideDelayed();
}
+ public void updateOffset(HandleView handle, int offset) {
+ Selection.setSelection((Spannable) mText, offset);
+ updatePosition();
+ }
+
public void updatePosition() {
final int offset = getSelectionStart();
@@ -8959,6 +8999,10 @@
getHandle().positionAtCursor(offset, true);
}
+ public int getCurrentOffset(HandleView handle) {
+ return getSelectionStart();
+ }
+
public boolean onTouchEvent(MotionEvent ev) {
return false;
}
@@ -9069,6 +9113,20 @@
updatePosition();
}
+ public void updateOffset(HandleView handle, int offset) {
+ int start = getSelectionStart();
+ int end = getSelectionEnd();
+
+ if (mStartHandle == handle) {
+ start = offset;
+ } else {
+ end = offset;
+ }
+
+ Selection.setSelection((Spannable) mText, start, end);
+ updatePosition();
+ }
+
public void updatePosition() {
if (!isShowing()) {
return;
@@ -9089,6 +9147,10 @@
mEndHandle.positionAtCursor(selectionEnd, true);
}
+ public int getCurrentOffset(HandleView handle) {
+ return mStartHandle == handle ? getSelectionStart() : getSelectionEnd();
+ }
+
public boolean onTouchEvent(MotionEvent event) {
// This is done even when the View does not have focus, so that long presses can start
// selection and tap can move cursor from this tap position.