Loading core/java/android/widget/TextView.java +80 −52 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -4396,6 +4397,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case KeyEvent.KEYCODE_DPAD_CENTER: mDPadCenterIsDown = true; if (shouldAdvanceFocusOnEnter()) { return 0; } Loading Loading @@ -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. Loading @@ -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) { Loading Loading @@ -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 Loading @@ -7298,67 +7314,31 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } int selectionStart, selectionEnd; int minOffset, maxOffset; if (mDPadCenterIsDown || mEnterKeyIsDown) { minOffset = getSelectionStart(); maxOffset = getSelectionEnd(); } else { // 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; } } minOffset = selectionModifierCursorController.getMinTouchOffset(); maxOffset = selectionModifierCursorController.getMaxTouchOffset(); } // '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; } } 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()); } Loading Loading @@ -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; Loading @@ -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()) { Loading Loading @@ -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 { Loading @@ -7714,7 +7739,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } stopSelectionActionMode(); } return true; case ID_CUT: Loading Loading @@ -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(); Loading Loading
core/java/android/widget/TextView.java +80 −52 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -4396,6 +4397,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case KeyEvent.KEYCODE_DPAD_CENTER: mDPadCenterIsDown = true; if (shouldAdvanceFocusOnEnter()) { return 0; } Loading Loading @@ -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. Loading @@ -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) { Loading Loading @@ -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 Loading @@ -7298,67 +7314,31 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } int selectionStart, selectionEnd; int minOffset, maxOffset; if (mDPadCenterIsDown || mEnterKeyIsDown) { minOffset = getSelectionStart(); maxOffset = getSelectionEnd(); } else { // 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; } } minOffset = selectionModifierCursorController.getMinTouchOffset(); maxOffset = selectionModifierCursorController.getMaxTouchOffset(); } // '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; } } 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()); } Loading Loading @@ -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; Loading @@ -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()) { Loading Loading @@ -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 { Loading @@ -7714,7 +7739,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } stopSelectionActionMode(); } return true; case ID_CUT: Loading Loading @@ -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(); Loading