diff options
| -rw-r--r-- | core/java/android/widget/TextView.java | 132 | 
1 files changed, 80 insertions, 52 deletions
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index d63af4e9000a..1c954fcfc4f4 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4364,6 +4364,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener          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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener                  break;              case KeyEvent.KEYCODE_DPAD_CENTER: +                mDPadCenterIsDown = true;                  if (shouldAdvanceFocusOnEnter()) {                      return 0;                  } @@ -4490,6 +4492,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener          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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener                  return super.onKeyUp(keyCode, event);              case KeyEvent.KEYCODE_ENTER: +                mEnterKeyIsDown = false;                  if (mInputContentType != null                          && mInputContentType.onEditorActionListener != null                          && mInputContentType.enterDown) { @@ -7288,9 +7292,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener          }          // 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener              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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener          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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener          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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener                              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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener                          }                          stopSelectionActionMode();                      } -                      return true;                  case ID_CUT: @@ -8329,6 +8353,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener      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();  |