diff options
| -rw-r--r-- | core/java/android/widget/Editor.java | 173 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 57 |
2 files changed, 122 insertions, 108 deletions
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index e949c52fc58b..a7b12c1336c2 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -215,7 +215,7 @@ public class Editor { boolean mInBatchEditControllers; boolean mShowSoftInputOnFocus = true; - boolean mPreserveDetachedSelection; + private boolean mPreserveDetachedSelection; boolean mTemporaryDetach; boolean mIsBeingLongClicked; @@ -352,7 +352,6 @@ public class Editor { void replace() { int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2; - stopTextActionMode(); Selection.setSelection((Spannable) mTextView.getText(), middle); showSuggestions(); } @@ -429,10 +428,8 @@ public class Editor { mSpellChecker = null; } - mPreserveDetachedSelection = true; hideCursorAndSpanControllers(); - stopTextActionMode(); - mPreserveDetachedSelection = false; + stopTextActionModeWithPreservingSelection(); mTemporaryDetach = false; } @@ -1104,7 +1101,6 @@ public class Editor { mInsertionControllerEnabled) { final int offset = mTextView.getOffsetForPosition(mLastDownPositionX, mLastDownPositionY); - stopTextActionMode(); Selection.setSelection((Spannable) mTextView.getText(), offset); getInsertionController().show(); mIsInsertionActionModeStartPending = true; @@ -1208,18 +1204,15 @@ public class Editor { mTextView.onEndBatchEdit(); if (mTextView.isInExtractedMode()) { - // terminateTextSelectionMode removes selection, which we want to keep when - // ExtractEditText goes out of focus. - final int selStart = mTextView.getSelectionStart(); - final int selEnd = mTextView.getSelectionEnd(); hideCursorAndSpanControllers(); - stopTextActionMode(); - Selection.setSelection((Spannable) mTextView.getText(), selStart, selEnd); + stopTextActionModeWithPreservingSelection(); } else { - if (mTemporaryDetach) mPreserveDetachedSelection = true; hideCursorAndSpanControllers(); - stopTextActionMode(); - if (mTemporaryDetach) mPreserveDetachedSelection = false; + if (mTemporaryDetach) { + stopTextActionModeWithPreservingSelection(); + } else { + stopTextActionMode(); + } downgradeEasyCorrectionSpans(); } // No need to create the controller @@ -1290,10 +1283,8 @@ public class Editor { makeBlink(); } final InputMethodManager imm = InputMethodManager.peekInstance(); - final boolean immFullScreen = (imm != null && imm.isFullscreenMode()); - if (mSelectionModifierCursorController != null && mTextView.hasSelection() - && !immFullScreen && mTextActionMode != null) { - mSelectionModifierCursorController.show(); + if (mTextView.hasSelection() && !extractedTextModeWillBeStarted()) { + startSelectionActionMode(); } } else { if (mBlink != null) { @@ -1304,9 +1295,7 @@ public class Editor { } // Order matters! Must be done before onParentLostFocus to rely on isShowingUp hideCursorAndSpanControllers(); - if (mSelectionModifierCursorController != null) { - mSelectionModifierCursorController.hide(); - } + stopTextActionModeWithPreservingSelection(); if (mSuggestionsPopupWindow != null) { mSuggestionsPopupWindow.onParentLostFocus(); } @@ -1856,6 +1845,38 @@ public class Editor { } } + void refreshTextActionMode() { + if (extractedTextModeWillBeStarted()) { + return; + } + final boolean hasSelection = mTextView.hasSelection(); + final SelectionModifierCursorController selectionController = getSelectionController(); + final InsertionPointCursorController insertionController = getInsertionController(); + if ((selectionController != null && selectionController.isCursorBeingModified()) + || (insertionController != null && insertionController.isCursorBeingModified())) { + // ActionMode should be managed by the currently active cursor controller. + return; + } + if (hasSelection) { + if (mTextActionMode == null || selectionController == null + || !selectionController.isActive()) { + // Avoid dismissing the selection if it exists. + stopTextActionModeWithPreservingSelection(); + startSelectionActionMode(); + } else { + mTextActionMode.invalidateContentRect(); + } + } else { + // Insertion action mode is started only when insertion controller is explicitly + // activated. + if (insertionController == null || !insertionController.isActive()) { + stopTextActionMode(); + } else if (mTextActionMode != null) { + mTextActionMode.invalidateContentRect(); + } + } + } + /** * Start an Insertion action mode. */ @@ -1879,17 +1900,15 @@ public class Editor { /** * Starts a Selection Action Mode with the current selection and ensures the selection handles - * are shown if there is a selection, otherwise the insertion handle is shown. This should be - * used when the mode is started from a non-touch event. + * are shown if there is a selection. This should be used when the mode is started from a + * non-touch event. * * @return true if the selection mode was actually started. */ - boolean startSelectionActionMode() { + private boolean startSelectionActionMode() { boolean selectionStarted = startSelectionActionModeInternal(); if (selectionStarted) { getSelectionController().show(); - } else if (getInsertionController() != null) { - getInsertionController().show(); } return selectionStarted; } @@ -1907,66 +1926,52 @@ public class Editor { if (extractedTextModeWillBeStarted()) { return false; } - if (mTextActionMode != null) { - mTextActionMode.finish(); + if (!checkField()) { + return false; } - if (!checkFieldAndSelectCurrentWord()) { + if (!mTextView.hasSelection() && !selectCurrentWord()) { + // No selection and cannot select a word. return false; } - - // Avoid dismissing the selection if it exists. - mPreserveDetachedSelection = true; - stopTextActionMode(); - mPreserveDetachedSelection = false; - + stopTextActionModeWithPreservingSelection(); getSelectionController().enterDrag( SelectionModifierCursorController.DRAG_ACCELERATOR_MODE_WORD); return true; } /** - * Checks whether a selection can be performed on the current TextView and if so selects - * the current word. + * Checks whether a selection can be performed on the current TextView. * - * @return true if there already was a selection or if the current word was selected. + * @return true if a selection can be performed */ - boolean checkFieldAndSelectCurrentWord() { + boolean checkField() { if (!mTextView.canSelectText() || !mTextView.requestFocus()) { Log.w(TextView.LOG_TAG, "TextView does not support text selection. Selection cancelled."); return false; } - - if (!mTextView.hasSelection()) { - // There may already be a selection on device rotation - return selectCurrentWord(); - } return true; } private boolean startSelectionActionModeInternal() { + if (extractedTextModeWillBeStarted()) { + return false; + } if (mTextActionMode != null) { // Text action mode is already started mTextActionMode.invalidate(); return false; } - if (!checkFieldAndSelectCurrentWord()) { + if (!checkField() || !mTextView.hasSelection()) { return false; } - boolean willExtract = extractedTextModeWillBeStarted(); - - // Do not start the action mode when extracted text will show up full screen, which would - // immediately hide the newly created action bar and would be visually distracting. - if (!willExtract) { - ActionMode.Callback actionModeCallback = - new TextActionModeCallback(true /* hasSelection */); - mTextActionMode = mTextView.startActionMode( - actionModeCallback, ActionMode.TYPE_FLOATING); - } + ActionMode.Callback actionModeCallback = + new TextActionModeCallback(true /* hasSelection */); + mTextActionMode = mTextView.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING); - final boolean selectionStarted = mTextActionMode != null || willExtract; + final boolean selectionStarted = mTextActionMode != null; if (selectionStarted && !mTextView.isTextSelectable() && mShowSoftInputOnFocus) { // Show the IME to be able to replace text, except when selecting non editable text. final InputMethodManager imm = InputMethodManager.peekInstance(); @@ -2107,6 +2112,12 @@ public class Editor { } } + private void stopTextActionModeWithPreservingSelection() { + mPreserveDetachedSelection = true; + stopTextActionMode(); + mPreserveDetachedSelection = false; + } + /** * @return True if this view supports insertion handles. */ @@ -2436,16 +2447,14 @@ public class Editor { if (offset == -1) { return; } - mPreserveDetachedSelection = true; - stopTextActionMode(); - mPreserveDetachedSelection = false; + stopTextActionModeWithPreservingSelection(); final boolean isOnSelection = mTextView.hasSelection() && offset >= mTextView.getSelectionStart() && offset <= mTextView.getSelectionEnd(); if (!isOnSelection) { // Right clicked position is not on the selection. Remove the selection and move the // cursor to the right clicked position. - stopTextActionMode(); Selection.setSelection((Spannable) mTextView.getText(), offset); + stopTextActionMode(); } if (shouldOfferToShowSuggestions()) { @@ -3488,7 +3497,6 @@ public class Editor { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Editable editable = (Editable) mTextView.getText(); SuggestionInfo suggestionInfo = mSuggestionInfos[position]; - final int spanStart = editable.getSpanStart(suggestionInfo.mSuggestionSpan); final int spanEnd = editable.getSpanEnd(suggestionInfo.mSuggestionSpan); if (spanStart < 0 || spanEnd <= spanStart) { @@ -4391,7 +4399,7 @@ public class Editor { if (distanceSquared < touchSlop * touchSlop) { // Tapping on the handle toggles the insertion action mode. if (mTextActionMode != null) { - mTextActionMode.finish(); + stopTextActionMode(); } else { startInsertionActionMode(); } @@ -4808,6 +4816,10 @@ public class Editor { * preventing the activity from being recycled. */ public void onDetached(); + + public boolean isCursorBeingModified(); + + public boolean isActive(); } private class InsertionPointCursorController implements CursorController { @@ -4851,6 +4863,16 @@ public class Editor { if (mHandle != null) mHandle.onDetached(); } + + @Override + public boolean isCursorBeingModified() { + return mHandle != null && mHandle.isDragging(); + } + + @Override + public boolean isActive() { + return mHandle != null && mHandle.isShowing(); + } } class SelectionModifierCursorController implements CursorController { @@ -5040,9 +5062,7 @@ public class Editor { if (mStartOffset != offset) { // Start character based drag accelerator. - if (mTextActionMode != null) { - mTextActionMode.finish(); - } + stopTextActionMode(); enterDrag(DRAG_ACCELERATOR_MODE_CHARACTER); mDiscardNextActionUp = true; mHaventMovedEnoughToStartDrag = false; @@ -5116,9 +5136,7 @@ public class Editor { if (mInsertionActionModeRunnable != null) { mTextView.removeCallbacks(mInsertionActionModeRunnable); } - if (mTextActionMode != null) { - mTextActionMode.finish(); - } + stopTextActionMode(); if (!selectCurrentParagraph()) { return false; } @@ -5227,6 +5245,12 @@ public class Editor { mStartOffset = -1; mDragAcceleratorMode = DRAG_ACCELERATOR_MODE_INACTIVE; mSwitchedLines = false; + final int selectionStart = mTextView.getSelectionStart(); + final int selectionEnd = mTextView.getSelectionEnd(); + if (selectionStart > selectionEnd) { + Selection.setSelection((Spannable) mTextView.getText(), + selectionEnd, selectionStart); + } } /** @@ -5236,6 +5260,12 @@ public class Editor { return mStartHandle != null && mStartHandle.isDragging(); } + @Override + public boolean isCursorBeingModified() { + return isDragAcceleratorActive() || isSelectionStartDragged() + || (mEndHandle != null && mEndHandle.isDragging()); + } + /** * @return true if the user is selecting text using the drag accelerator. */ @@ -5257,6 +5287,11 @@ public class Editor { if (mStartHandle != null) mStartHandle.onDetached(); if (mEndHandle != null) mEndHandle.onDetached(); } + + @Override + public boolean isActive() { + return mStartHandle != null && mStartHandle.isShowing(); + } } private class CorrectionHighlighter { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 18f1ae5ae592..73f8fdcb4a92 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -1511,6 +1511,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (result != null) { if (isTextEditable()) { replaceSelectionWithText(result); + if (mEditor != null) { + mEditor.refreshTextActionMode(); + } } else { if (result.length() > 0) { Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG) @@ -1520,12 +1523,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } else if (mText instanceof Spannable) { // Reset the selection. - stopTextActionMode(); - Selection.setSelection((Spannable) mText, getSelectionStart(), getSelectionEnd()); - } - - if (mEditor.hasSelectionController()) { - mEditor.startSelectionActionMode(); + Selection.setSelection((Spannable) mText, getSelectionEnd()); } } } @@ -5393,11 +5391,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // - onFocusChanged cannot start it when focus is given to a view with selected text (after // a screen rotation) since layout is not yet initialized at that point. if (mEditor != null && mEditor.mCreatedWithASelection) { - if (mEditor.extractedTextModeWillBeStarted()) { - mEditor.checkFieldAndSelectCurrentWord(); - } else { - mEditor.startSelectionActionMode(); - } + mEditor.refreshTextActionMode(); mEditor.mCreatedWithASelection = false; } @@ -6594,6 +6588,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // in the extracted view. mEditor.hideCursorAndSpanControllers(); stopTextActionMode(); + if (mEditor.mSelectionModifierCursorController != null) { + mEditor.mSelectionModifierCursorController.resetTouchOffsets(); + } } /** @@ -8289,6 +8286,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (newSelEnd < 0) { newSelEnd = Selection.getSelectionEnd(buf); } + if (mEditor != null) { + mEditor.refreshTextActionMode(); + } onSelectionChanged(newSelStart, newSelEnd); } } @@ -9199,10 +9199,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (start >= 0 && start <= end && end <= text.length()) { Selection.setSelection((Spannable) text, start, end); - // Make sure selection mode is engaged. - if (mEditor != null) { - mEditor.startSelectionActionMode(); - } return true; } } @@ -9393,16 +9389,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener switch (id) { case ID_SELECT_ALL: - // This starts an action mode if triggered from another action mode. Text is - // highlighted, so that it can be bulk edited, like selectAllOnFocus does. Returns - // true even if text is empty. - boolean shouldRestartActionMode = - mEditor != null && mEditor.mTextActionMode != null; - stopTextActionMode(); selectAllText(); - if (shouldRestartActionMode) { - mEditor.startSelectionActionMode(); - } return true; case ID_UNDO: @@ -9428,7 +9415,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case ID_CUT: setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max))); deleteText_internal(min, max); - stopTextActionMode(); return true; case ID_COPY: @@ -9684,12 +9670,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean selectAllText() { - // Need to hide insert point cursor controller before settings selection, otherwise insert - // point cursor controller obtains cursor update event and update cursor with cancelling - // selection. - if (mEditor != null) { - mEditor.hideInsertionPointCursorController(); - } final int length = mText.length(); Selection.setSelection((Spannable) mText, 0, length); return length > 0; @@ -9728,7 +9708,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } } - stopTextActionMode(); sLastCutCopyOrTextChangedTime = 0; } } @@ -9741,7 +9720,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener sharingIntent.removeExtra(android.content.Intent.EXTRA_TEXT); sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText); getContext().startActivity(Intent.createChooser(sharingIntent, null)); - stopTextActionMode(); + Selection.setSelection((Spannable) mText, getSelectionEnd()); } } @@ -10059,6 +10038,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener && getAccessibilitySelectionEnd() == end) { return; } + CharSequence text = getIterableTextForAccessibility(); + if (Math.min(start, end) >= 0 && Math.max(start, end) <= text.length()) { + Selection.setSelection((Spannable) text, start, end); + } else { + Selection.removeSelection((Spannable) text); + } // Hide all selection controllers used for adjusting selection // since we are doing so explicitlty by other means and these // controllers interact with how selection behaves. @@ -10066,12 +10051,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mEditor.hideCursorAndSpanControllers(); mEditor.stopTextActionMode(); } - CharSequence text = getIterableTextForAccessibility(); - if (Math.min(start, end) >= 0 && Math.max(start, end) <= text.length()) { - Selection.setSelection((Spannable) text, start, end); - } else { - Selection.removeSelection((Spannable) text); - } } /** @hide */ |