From 3fceabd64c95bfd2c563972e243117af308438d3 Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Tue, 19 Aug 2014 18:28:04 -0700 Subject: [PATCH] Update text selection handles to have a minimum touch target size. The text selection handles were traditionally sized based on the size of the provided drawables. When these drawables are visually small, do not restrict the size of the touch target beyond a certain minimum. Bug 16851360 Change-Id: If6a468c7b3a9a1a21913006633f00699637751c0 --- core/java/android/widget/Editor.java | 76 ++++++++++++++++++++++++++-- core/res/res/values/dimens.xml | 3 ++ core/res/res/values/symbols.xml | 1 + 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index b37ee06da93cd..3f5f04554c14b 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3158,6 +3158,7 @@ public class Editor { // Offset from touch position to mPosition private float mTouchToWindowOffsetX, mTouchToWindowOffsetY; protected int mHotspotX; + protected int mHorizontalGravity; // Offsets the hotspot point up, so that cursor is not hidden by the finger when moving up private float mTouchOffsetY; // Where the touch position should be on the handle to ensure a maximum cursor visibility @@ -3172,6 +3173,8 @@ public class Editor { private boolean mPositionHasChanged = true; // Used to delay the appearance of the action popup window private Runnable mActionPopupShower; + // Minimum touch target size for handles + private int mMinSize; public HandleView(Drawable drawableLtr, Drawable drawableRtl) { super(mTextView.getContext()); @@ -3184,10 +3187,12 @@ public class Editor { mDrawableLtr = drawableLtr; mDrawableRtl = drawableRtl; + mMinSize = mTextView.getContext().getResources().getDimensionPixelSize( + com.android.internal.R.dimen.text_handle_min_size); updateDrawable(); - final int handleHeight = mDrawable.getIntrinsicHeight(); + final int handleHeight = getPreferredHeight(); mTouchOffsetY = -0.3f * handleHeight; mIdealVerticalOffset = 0.7f * handleHeight; } @@ -3197,9 +3202,11 @@ public class Editor { final boolean isRtlCharAtOffset = mTextView.getLayout().isRtlCharAt(offset); mDrawable = isRtlCharAtOffset ? mDrawableRtl : mDrawableLtr; mHotspotX = getHotspotX(mDrawable, isRtlCharAtOffset); + mHorizontalGravity = getHorizontalGravity(isRtlCharAtOffset); } protected abstract int getHotspotX(Drawable drawable, boolean isRtlRun); + protected abstract int getHorizontalGravity(boolean isRtlRun); // Touch-up filter: number of previous positions remembered private static final int HISTORY_SIZE = 5; @@ -3244,7 +3251,15 @@ public class Editor { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - setMeasuredDimension(mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()); + setMeasuredDimension(getPreferredWidth(), getPreferredHeight()); + } + + private int getPreferredWidth() { + return Math.max(mDrawable.getIntrinsicWidth(), mMinSize); + } + + private int getPreferredHeight() { + return Math.max(mDrawable.getIntrinsicHeight(), mMinSize); } public void show() { @@ -3336,7 +3351,8 @@ public class Editor { } final int line = layout.getLineForOffset(offset); - mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX); + mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX - + getHorizontalOffset() + getCursorOffset()); mPositionY = layout.getLineBottom(line); // Take TextView's padding and scroll into account. @@ -3385,10 +3401,36 @@ public class Editor { @Override protected void onDraw(Canvas c) { - mDrawable.setBounds(0, 0, mRight - mLeft, mBottom - mTop); + final int drawWidth = mDrawable.getIntrinsicWidth(); + final int left = getHorizontalOffset(); + + mDrawable.setBounds(left, 0, left + drawWidth, mDrawable.getIntrinsicHeight()); mDrawable.draw(c); } + private int getHorizontalOffset() { + final int width = getPreferredWidth(); + final int drawWidth = mDrawable.getIntrinsicWidth(); + final int left; + switch (mHorizontalGravity) { + case Gravity.LEFT: + left = 0; + break; + default: + case Gravity.CENTER: + left = (width - drawWidth) / 2; + break; + case Gravity.RIGHT: + left = width - drawWidth; + break; + } + return left; + } + + protected int getCursorOffset() { + return 0; + } + @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getActionMasked()) { @@ -3507,6 +3549,22 @@ public class Editor { return drawable.getIntrinsicWidth() / 2; } + @Override + protected int getHorizontalGravity(boolean isRtlRun) { + return Gravity.CENTER_HORIZONTAL; + } + + @Override + protected int getCursorOffset() { + int offset = super.getCursorOffset(); + final Drawable cursor = mCursorCount > 0 ? mCursorDrawable[0] : null; + if (cursor != null) { + cursor.getPadding(mTempRect); + offset += (cursor.getIntrinsicWidth() - mTempRect.left - mTempRect.right) / 2; + } + return offset; + } + @Override public boolean onTouchEvent(MotionEvent ev) { final boolean result = super.onTouchEvent(ev); @@ -3593,6 +3651,11 @@ public class Editor { } } + @Override + protected int getHorizontalGravity(boolean isRtlRun) { + return isRtlRun ? Gravity.RIGHT : Gravity.LEFT; + } + @Override public int getCurrentCursorOffset() { return mTextView.getSelectionStart(); @@ -3636,6 +3699,11 @@ public class Editor { } } + @Override + protected int getHorizontalGravity(boolean isRtlRun) { + return isRtlRun ? Gravity.LEFT : Gravity.RIGHT; + } + @Override public int getCurrentCursorOffset() { return mTextView.getSelectionEnd(); diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index a29a34c2a45fc..d170fd442620b 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -394,4 +394,7 @@ 3dp 12dp 28dp + + 40dp + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1cb7b17f11f94..a2c1aa05c22ab 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1983,4 +1983,5 @@ +