Merge "Track difference between touch and word in x coordinate." into mnc-dev
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 814882a..30f373a 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3855,8 +3855,8 @@
     private class SelectionStartHandleView extends HandleView {
         // Indicates whether the cursor is making adjustments within a word.
         private boolean mInWord = false;
-        // Offset to track difference between touch and word boundary.
-        protected int mTouchWordOffset;
+        // Difference between touch position and word boundary position.
+        private float mTouchWordDelta;
 
         public SelectionStartHandleView(Drawable drawableLtr, Drawable drawableRtl) {
             super(drawableLtr, drawableRtl);
@@ -3908,18 +3908,36 @@
                         offset = mPreviousOffset;
                     }
                 }
-                mTouchWordOffset = Math.max(trueOffset - offset, 0);
-                positionCursor = true;
-            } else if (offset - mTouchWordOffset > mPreviousOffset || currLine > mPrevLine) {
-                // User is shrinking the selection.
-                if (currLine > mPrevLine) {
-                    // We're on a different line, so we'll snap to word boundaries.
-                    offset = start;
-                    mTouchWordOffset = Math.max(trueOffset - offset, 0);
+                final Layout layout = mTextView.getLayout();
+                if (layout != null && offset < trueOffset) {
+                    final float adjustedX = layout.getPrimaryHorizontal(offset);
+                    mTouchWordDelta =
+                            mTextView.convertToLocalHorizontalCoordinate(x) - adjustedX;
                 } else {
-                    offset -= mTouchWordOffset;
+                    mTouchWordDelta = 0.0f;
                 }
                 positionCursor = true;
+            } else {
+                final int adjustedOffset =
+                        mTextView.getOffsetAtCoordinate(currLine, x - mTouchWordDelta);
+                if (adjustedOffset > mPreviousOffset || currLine > mPrevLine) {
+                    // User is shrinking the selection.
+                    if (currLine > mPrevLine) {
+                        // We're on a different line, so we'll snap to word boundaries.
+                        offset = start;
+                        final Layout layout = mTextView.getLayout();
+                        if (layout != null && offset < trueOffset) {
+                            final float adjustedX = layout.getPrimaryHorizontal(offset);
+                            mTouchWordDelta =
+                                    mTextView.convertToLocalHorizontalCoordinate(x) - adjustedX;
+                        } else {
+                            mTouchWordDelta = 0.0f;
+                        }
+                    } else {
+                        offset = adjustedOffset;
+                    }
+                    positionCursor = true;
+                }
             }
 
             // Handles can not cross and selection is at least one character.
@@ -3934,7 +3952,7 @@
                     } else {
                         offset = alteredOffset;
                     }
-                    mTouchWordOffset = 0;
+                    mTouchWordDelta = 0.0f;
                 }
                 mInWord = !getWordIteratorWithText().isBoundary(offset);
                 positionAtCursorOffset(offset, false);
@@ -3946,7 +3964,7 @@
             boolean superResult = super.onTouchEvent(event);
             if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                 // Reset the touch word offset when the user has lifted their finger.
-                mTouchWordOffset = 0;
+                mTouchWordDelta = 0.0f;
             }
             return superResult;
         }
@@ -3955,8 +3973,8 @@
     private class SelectionEndHandleView extends HandleView {
         // Indicates whether the cursor is making adjustments within a word.
         private boolean mInWord = false;
-        // Offset to track difference between touch and word boundary.
-        protected int mTouchWordOffset;
+        // Difference between touch position and word boundary position.
+        private float mTouchWordDelta;
 
         public SelectionEndHandleView(Drawable drawableLtr, Drawable drawableRtl) {
             super(drawableLtr, drawableRtl);
@@ -4008,18 +4026,36 @@
                         offset = mPreviousOffset;
                     }
                 }
-                mTouchWordOffset = Math.max(offset - trueOffset, 0);
-                positionCursor = true;
-            } else if (offset + mTouchWordOffset < mPreviousOffset || currLine < mPrevLine) {
-                // User is shrinking the selection.
-                if (currLine < mPrevLine) {
-                    // We're on a different line, so we'll snap to word boundaries.
-                    offset = end;
-                    mTouchWordOffset = Math.max(offset - trueOffset, 0);
+                final Layout layout = mTextView.getLayout();
+                if (layout != null && offset > trueOffset) {
+                    final float adjustedX = layout.getPrimaryHorizontal(offset);
+                    mTouchWordDelta =
+                            adjustedX - mTextView.convertToLocalHorizontalCoordinate(x);
                 } else {
-                    offset += mTouchWordOffset;
+                    mTouchWordDelta = 0.0f;
                 }
                 positionCursor = true;
+            } else {
+                final int adjustedOffset =
+                        mTextView.getOffsetAtCoordinate(currLine, x + mTouchWordDelta);
+                if (adjustedOffset < mPreviousOffset || currLine < mPrevLine) {
+                    // User is shrinking the selection.
+                    if (currLine < mPrevLine) {
+                        // We're on a different line, so we'll snap to word boundaries.
+                        offset = end;
+                        final Layout layout = mTextView.getLayout();
+                        if (layout != null && offset > trueOffset) {
+                            final float adjustedX = layout.getPrimaryHorizontal(offset);
+                            mTouchWordDelta =
+                                    adjustedX - mTextView.convertToLocalHorizontalCoordinate(x);
+                        } else {
+                            mTouchWordDelta = 0.0f;
+                        }
+                    } else {
+                        offset = adjustedOffset;
+                    }
+                    positionCursor = true;
+                }
             }
 
             if (positionCursor) {
@@ -4034,7 +4070,7 @@
                     } else {
                         offset = Math.min(alteredOffset, length);
                     }
-                    mTouchWordOffset = 0;
+                    mTouchWordDelta = 0.0f;
                 }
                 mInWord = !getWordIteratorWithText().isBoundary(offset);
                 positionAtCursorOffset(offset, false);
@@ -4046,7 +4082,7 @@
             boolean superResult = super.onTouchEvent(event);
             if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                 // Reset the touch word offset when the user has lifted their finger.
-                mTouchWordOffset = 0;
+                mTouchWordDelta = 0.0f;
             }
             return superResult;
         }