Merge "resolved conflicts for merge of ba04dbc6 to master"
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d63af4e..1c954fc 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4364,6 +4364,7 @@
 
         switch (keyCode) {
             case KeyEvent.KEYCODE_ENTER:
+                mEnterKeyIsDown = true;
                 // If ALT modifier is held, then we always insert a
                 // newline character.
                 if ((event.getMetaState()&KeyEvent.META_ALT_ON) == 0) {
@@ -4396,6 +4397,7 @@
                 break;
                 
             case KeyEvent.KEYCODE_DPAD_CENTER:
+                mDPadCenterIsDown = true;
                 if (shouldAdvanceFocusOnEnter()) {
                     return 0;
                 }
@@ -4490,6 +4492,7 @@
 
         switch (keyCode) {
             case KeyEvent.KEYCODE_DPAD_CENTER:
+                mDPadCenterIsDown = false;
                 /*
                  * If there is a click listener, just call through to
                  * super, which will invoke it.
@@ -4510,6 +4513,7 @@
                 return super.onKeyUp(keyCode, event);
                 
             case KeyEvent.KEYCODE_ENTER:
+                mEnterKeyIsDown = false;
                 if (mInputContentType != null
                         && mInputContentType.onEditorActionListener != null
                         && mInputContentType.enterDown) {
@@ -7288,9 +7292,21 @@
         }
 
         // Two ints packed in a long
+        return packRangeInLong(start, end);
+    }
+
+    private static long packRangeInLong(int start, int end) {
         return (((long) start) << 32) | end;
     }
 
+    private static int extractRangeStartFromLong(long range) {
+        return (int) (range >>> 32);
+    }
+
+    private static int extractRangeEndFromLong(long range) {
+        return (int) (range & 0x00000000FFFFFFFFL);
+    }
+    
     private void selectCurrentWord() {
         // In case selection mode is started after an orientation change or after a select all,
         // use the current selection instead of creating one
@@ -7298,67 +7314,31 @@
             return;
         }
 
-        int selectionStart, selectionEnd;
+        int minOffset, maxOffset;
 
-        // selectionModifierCursorController is not null at that point
-        SelectionModifierCursorController selectionModifierCursorController =
-            ((SelectionModifierCursorController) mSelectionModifierCursorController);
-        int minOffset = selectionModifierCursorController.getMinTouchOffset();
-        int maxOffset = selectionModifierCursorController.getMaxTouchOffset();
-
-        if (minOffset == maxOffset) {
-            int offset = Math.max(0, Math.min(minOffset, mTransformed.length()));
-
-            // Tolerance, number of charaters around tapped position
-            final int range = 1;
-            final int max = mTransformed.length() - 1;
-
-            // 'Smart' word selection: detect position between words
-            for (int i = -range; i <= range; i++) {
-                int index = offset + i;
-                if (index >= 0 && index <= max) {
-                    if (Character.isSpaceChar(mTransformed.charAt(index))) {
-                        // Select current space
-                        selectionStart = index;
-                        selectionEnd = selectionStart + 1;
-
-                        // Extend selection to maximum space range
-                        while (selectionStart > 0 &&
-                                Character.isSpaceChar(mTransformed.charAt(selectionStart - 1))) {
-                            selectionStart--;
-                        }
-                        while (selectionEnd < max &&
-                                Character.isSpaceChar(mTransformed.charAt(selectionEnd))) {
-                            selectionEnd++;
-                        }
-
-                        Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
-                        return;
-                    }
-                }
-            }
-
-            // 'Smart' word selection: detect position at beginning or end of text.
-            if (offset <= range) {
-                Selection.setSelection((Spannable) mText, 0, 0);
-                return;
-            }
-            if (offset >= (max - range)) {
-                Selection.setSelection((Spannable) mText, max + 1, max + 1);
-                return;
-            }
+        if (mDPadCenterIsDown || mEnterKeyIsDown) {
+            minOffset = getSelectionStart();
+            maxOffset = getSelectionEnd();
+        } else {
+            // selectionModifierCursorController is not null at that point
+            SelectionModifierCursorController selectionModifierCursorController =
+                ((SelectionModifierCursorController) mSelectionModifierCursorController);
+            minOffset = selectionModifierCursorController.getMinTouchOffset();
+            maxOffset = selectionModifierCursorController.getMaxTouchOffset();
         }
 
+        int selectionStart, selectionEnd;
+
         long wordLimits = getWordLimitsAt(minOffset);
         if (wordLimits >= 0) {
-            selectionStart = (int) (wordLimits >>> 32);
+            selectionStart = extractRangeStartFromLong(wordLimits);
         } else {
             selectionStart = Math.max(minOffset - 5, 0);
         }
 
         wordLimits = getWordLimitsAt(maxOffset);
         if (wordLimits >= 0) {
-            selectionEnd = (int) (wordLimits & 0x00000000FFFFFFFFL);
+            selectionEnd = extractRangeEndFromLong(wordLimits);
         } else {
             selectionEnd = Math.min(maxOffset + 5, mText.length());
         }
@@ -7487,7 +7467,6 @@
 
         switch (id) {
             case ID_COPY_URL:
-
                 URLSpan[] urls = ((Spanned) mText).getSpans(min, max, URLSpan.class);
                 if (urls.length >= 1) {
                     ClipData clip = null;
@@ -7513,6 +7492,49 @@
         return false;
     }
 
+    /**
+     * Prepare text so that there are not zero or two spaces at beginning and end of region defined
+     * by [min, max] when replacing this region by paste.
+     */
+    private long prepareSpacesAroundPaste(int min, int max, CharSequence paste) {
+        // Paste adds/removes spaces before or after insertion as needed.
+        if (Character.isSpaceChar(paste.charAt(0))) {
+            if (min > 0 && Character.isSpaceChar(mTransformed.charAt(min - 1))) {
+                // Two spaces at beginning of paste: remove one
+                final int originalLength = mText.length();
+                ((Editable) mText).replace(min - 1, min, "");
+                // Due to filters, there is no garantee that exactly one character was
+                // removed. Count instead.
+                final int delta = mText.length() - originalLength;
+                min += delta;
+                max += delta;
+            }
+        } else {
+            if (min > 0 && !Character.isSpaceChar(mTransformed.charAt(min - 1))) {
+                // No space at beginning of paste: add one
+                final int originalLength = mText.length();
+                ((Editable) mText).replace(min, min, " ");
+                // Taking possible filters into account as above.
+                final int delta = mText.length() - originalLength;
+                min += delta;
+                max += delta;
+            }
+        }
+
+        if (Character.isSpaceChar(paste.charAt(paste.length() - 1))) {
+            if (max < mText.length() && Character.isSpaceChar(mTransformed.charAt(max))) {
+                // Two spaces at end of paste: remove one
+                ((Editable) mText).replace(max, max + 1, "");
+            }
+        } else {
+            if (max < mText.length() && !Character.isSpaceChar(mTransformed.charAt(max))) {
+                // No space at end of paste: add one
+                ((Editable) mText).replace(max, max, " ");
+            }
+        }
+        return packRangeInLong(min, max);
+    }
+
     @Override
     public boolean performLongClick() {
         if (super.performLongClick()) {
@@ -7704,6 +7726,9 @@
                             CharSequence paste = clip.getItem(i).coerceToText(getContext());
                             if (paste != null) {
                                 if (!didfirst) {
+                                    long minMax = prepareSpacesAroundPaste(min, max, paste);
+                                    min = extractRangeStartFromLong(minMax);
+                                    max = extractRangeEndFromLong(minMax);
                                     Selection.setSelection((Spannable) mText, max);
                                     ((Editable) mText).replace(min, max, paste);
                                 } else {
@@ -7714,7 +7739,6 @@
                         }
                         stopSelectionActionMode();
                     }
-
                     return true;
 
                 case ID_CUT:
@@ -8329,6 +8353,10 @@
     private CursorController        mSelectionModifierCursorController;
     private ActionMode              mSelectionActionMode;
     private int                     mLastTouchOffset = -1;
+    // These are needed to desambiguate a long click. If the long click comes from ones of these, we
+    // select from the current cursor position. Otherwise, select from long pressed position.
+    private boolean                 mDPadCenterIsDown = false;
+    private boolean                 mEnterKeyIsDown = false;
     // Created once and shared by different CursorController helper methods.
     // Only one cursor controller is active at any time which prevent race conditions.
     private static Rect             sCursorControllerTempRect = new Rect();