diff options
-rw-r--r-- | api/current.txt | 2 | ||||
-rw-r--r-- | api/system-current.txt | 2 | ||||
-rw-r--r-- | core/java/android/inputmethodservice/ExtractEditText.java | 2 | ||||
-rw-r--r-- | core/java/android/widget/Editor.java | 237 | ||||
-rw-r--r-- | core/java/android/widget/TextView.java | 75 |
5 files changed, 185 insertions, 133 deletions
diff --git a/api/current.txt b/api/current.txt index c105345..51a1e79 100644 --- a/api/current.txt +++ b/api/current.txt @@ -41542,6 +41542,7 @@ package android.widget { method public int getCompoundPaddingTop(); method public final int getCurrentHintTextColor(); method public final int getCurrentTextColor(); + method public android.view.ActionMode.Callback getCustomInsertionActionModeCallback(); method public android.view.ActionMode.Callback getCustomSelectionActionModeCallback(); method protected boolean getDefaultEditable(); method protected android.text.method.MovementMethod getDefaultMovementMethod(); @@ -41644,6 +41645,7 @@ package android.widget { method public void setCompoundDrawablesWithIntrinsicBounds(int, int, int, int); method public void setCompoundDrawablesWithIntrinsicBounds(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable); method public void setCursorVisible(boolean); + method public void setCustomInsertionActionModeCallback(android.view.ActionMode.Callback); method public void setCustomSelectionActionModeCallback(android.view.ActionMode.Callback); method public final void setEditableFactory(android.text.Editable.Factory); method public void setElegantTextHeight(boolean); diff --git a/api/system-current.txt b/api/system-current.txt index 14c9939..7ce111c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -44121,6 +44121,7 @@ package android.widget { method public int getCompoundPaddingTop(); method public final int getCurrentHintTextColor(); method public final int getCurrentTextColor(); + method public android.view.ActionMode.Callback getCustomInsertionActionModeCallback(); method public android.view.ActionMode.Callback getCustomSelectionActionModeCallback(); method protected boolean getDefaultEditable(); method protected android.text.method.MovementMethod getDefaultMovementMethod(); @@ -44223,6 +44224,7 @@ package android.widget { method public void setCompoundDrawablesWithIntrinsicBounds(int, int, int, int); method public void setCompoundDrawablesWithIntrinsicBounds(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable); method public void setCursorVisible(boolean); + method public void setCustomInsertionActionModeCallback(android.view.ActionMode.Callback); method public void setCustomSelectionActionModeCallback(android.view.ActionMode.Callback); method public final void setEditableFactory(android.text.Editable.Factory); method public void setElegantTextHeight(boolean); diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java index 48b604c..f965f54 100644 --- a/core/java/android/inputmethodservice/ExtractEditText.java +++ b/core/java/android/inputmethodservice/ExtractEditText.java @@ -106,7 +106,7 @@ public class ExtractEditText extends EditText { if (mIME != null && mIME.onExtractTextContextMenuItem(id)) { // Mode was started on Extracted, needs to be stopped here. // Cut and paste will change the text, which stops selection mode. - if (id == android.R.id.copy) stopSelectionActionMode(); + if (id == android.R.id.copy) stopTextActionMode(); return true; } return super.onTextContextMenuItem(id); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 56f9b5c..4c98abf 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -134,7 +134,8 @@ public class Editor { // Cursor Controllers. InsertionPointCursorController mInsertionPointCursorController; SelectionModifierCursorController mSelectionModifierCursorController; - ActionMode mSelectionActionMode; + // Action mode used when text is selected or when actions on an insertion cursor are triggered. + ActionMode mTextActionMode; boolean mInsertionControllerEnabled; boolean mSelectionControllerEnabled; @@ -205,13 +206,14 @@ public class Editor { float mLastDownPositionX, mLastDownPositionY; Callback mCustomSelectionActionModeCallback; + Callback mCustomInsertionActionModeCallback; // Set when this TextView gained focus with some text selected. Will start selection mode. boolean mCreatedWithASelection; boolean mDoubleTap = false; - private Runnable mSelectionModeWithoutSelectionRunnable; + private Runnable mInsertionActionModeRunnable; // The span controller helps monitoring the changes to which the Editor needs to react: // - EasyEditSpans, for which we have some UI to display on attach and on hide @@ -236,8 +238,8 @@ public class Editor { private final Runnable mHideFloatingToolbar = new Runnable() { @Override public void run() { - if (mSelectionActionMode != null) { - mSelectionActionMode.snooze(ActionMode.SNOOZE_TIME_DEFAULT); + if (mTextActionMode != null) { + mTextActionMode.snooze(ActionMode.SNOOZE_TIME_DEFAULT); } } }; @@ -245,8 +247,8 @@ public class Editor { private final Runnable mShowFloatingToolbar = new Runnable() { @Override public void run() { - if (mSelectionActionMode != null) { - mSelectionActionMode.snooze(0); // snooze off. + if (mTextActionMode != null) { + mTextActionMode.snooze(0); // snooze off. } } }; @@ -310,7 +312,7 @@ public class Editor { void replace() { int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2; - stopSelectionActionMode(); + stopTextActionMode(); Selection.setSelection((Spannable) mTextView.getText(), middle); showSuggestions(); } @@ -343,7 +345,7 @@ public class Editor { mTextView.setHasTransientState(false); // We had an active selection from before, start the selection mode. - startSelectionActionModeWithSelection(); + startSelectionActionMode(); } getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true); @@ -372,8 +374,8 @@ public class Editor { } // Cancel the single tap delayed runnable. - if (mSelectionModeWithoutSelectionRunnable != null) { - mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); + if (mInsertionActionModeRunnable != null) { + mTextView.removeCallbacks(mInsertionActionModeRunnable); } mTextView.removeCallbacks(mHideFloatingToolbar); @@ -390,7 +392,7 @@ public class Editor { mPreserveDetachedSelection = true; hideControllers(); - stopSelectionActionMode(); + stopTextActionMode(); mPreserveDetachedSelection = false; mTemporaryDetach = false; } @@ -586,7 +588,7 @@ public class Editor { } if (!mSelectionControllerEnabled) { - stopSelectionActionMode(); + stopTextActionMode(); if (mSelectionModifierCursorController != null) { mSelectionModifierCursorController.onDetached(); mSelectionModifierCursorController = null; @@ -984,14 +986,14 @@ public class Editor { mInsertionControllerEnabled) { final int offset = mTextView.getOffsetForPosition(mLastDownPositionX, mLastDownPositionY); - stopSelectionActionMode(); + stopTextActionMode(); Selection.setSelection((Spannable) mTextView.getText(), offset); getInsertionController().show(); - startSelectionActionModeWithoutSelection(); + startInsertionActionMode(); handled = true; } - if (!handled && mSelectionActionMode != null) { + if (!handled && mTextActionMode != null) { if (touchPositionIsInSelection()) { // Start a drag final int start = mTextView.getSelectionStart(); @@ -1001,9 +1003,9 @@ public class Editor { DragLocalState localState = new DragLocalState(mTextView, start, end); mTextView.startDrag(data, getTextThumbnailBuilder(selectedText), localState, View.DRAG_FLAG_GLOBAL); - stopSelectionActionMode(); + stopTextActionMode(); } else { - stopSelectionActionMode(); + stopTextActionMode(); selectCurrentWordAndStartDrag(); } handled = true; @@ -1101,12 +1103,12 @@ public class Editor { final int selStart = mTextView.getSelectionStart(); final int selEnd = mTextView.getSelectionEnd(); hideControllers(); - stopSelectionActionMode(); + stopTextActionMode(); Selection.setSelection((Spannable) mTextView.getText(), selStart, selEnd); } else { if (mTemporaryDetach) mPreserveDetachedSelection = true; hideControllers(); - stopSelectionActionMode(); + stopTextActionMode(); if (mTemporaryDetach) mPreserveDetachedSelection = false; downgradeEasyCorrectionSpans(); } @@ -1149,7 +1151,7 @@ public class Editor { // We do not hide the span controllers, since they can be added when a new text is // inserted into the text view (voice IME). hideCursorControllers(); - stopSelectionActionMode(); + stopTextActionMode(); } private int getLastTapPosition() { @@ -1216,7 +1218,7 @@ public class Editor { } private void updateFloatingToolbarVisibility(MotionEvent event) { - if (mSelectionActionMode != null) { + if (mTextActionMode != null) { switch (event.getActionMasked()) { case MotionEvent.ACTION_MOVE: hideFloatingToolbar(); @@ -1229,7 +1231,7 @@ public class Editor { } private void hideFloatingToolbar() { - if (mSelectionActionMode != null) { + if (mTextActionMode != null) { mTextView.removeCallbacks(mShowFloatingToolbar); // Delay the "hide" a little bit just in case a "show" will happen almost immediately. mTextView.postDelayed(mHideFloatingToolbar, 100); @@ -1237,7 +1239,7 @@ public class Editor { } private void showFloatingToolbar() { - if (mSelectionActionMode != null) { + if (mTextActionMode != null) { mTextView.removeCallbacks(mHideFloatingToolbar); // Delay "show" so it doesn't interfere with click confirmations // or double-clicks that could "dismiss" the floating toolbar. @@ -1701,26 +1703,20 @@ public class Editor { /** * @return true if the selection mode was actually started. */ - private boolean startSelectionActionModeWithoutSelection() { + private boolean startInsertionActionMode() { + if (mInsertionActionModeRunnable != null) { + mTextView.removeCallbacks(mInsertionActionModeRunnable); + } if (extractedTextModeWillBeStarted()) { - // Cancel the single tap delayed runnable. - if (mSelectionModeWithoutSelectionRunnable != null) { - mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); - } - return false; } + stopTextActionMode(); - if (mSelectionActionMode != null) { - // Selection action mode is already started - // TODO: revisit invocations to minimize this case. - mSelectionActionMode.invalidate(); - return false; - } - ActionMode.Callback actionModeCallback = new SelectionActionModeCallback(); - mSelectionActionMode = mTextView.startActionMode( + ActionMode.Callback actionModeCallback = + new TextActionModeCallback(false /* hasSelection */); + mTextActionMode = mTextView.startActionMode( actionModeCallback, ActionMode.TYPE_FLOATING); - return mSelectionActionMode != null; + return mTextActionMode != null; } /** @@ -1730,8 +1726,8 @@ public class Editor { * * @return true if the selection mode was actually started. */ - boolean startSelectionActionModeWithSelection() { - boolean selectionStarted = startSelectionActionModeWithSelectionInternal(); + boolean startSelectionActionMode() { + boolean selectionStarted = startSelectionActionModeInternal(); if (selectionStarted) { getSelectionController().show(); } else if (getInsertionController() != null) { @@ -1749,13 +1745,13 @@ public class Editor { private boolean selectCurrentWordAndStartDrag() { if (extractedTextModeWillBeStarted()) { // Cancel the single tap delayed runnable. - if (mSelectionModeWithoutSelectionRunnable != null) { - mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); + if (mInsertionActionModeRunnable != null) { + mTextView.removeCallbacks(mInsertionActionModeRunnable); } return false; } - if (mSelectionActionMode != null) { - mSelectionActionMode.finish(); + if (mTextActionMode != null) { + mTextActionMode.finish(); } if (!checkFieldAndSelectCurrentWord()) { return false; @@ -1784,10 +1780,10 @@ public class Editor { return true; } - private boolean startSelectionActionModeWithSelectionInternal() { - if (mSelectionActionMode != null) { + private boolean startSelectionActionModeInternal() { + if (mTextActionMode != null) { // Selection action mode is already started - mSelectionActionMode.invalidate(); + mTextActionMode.invalidate(); return false; } @@ -1800,12 +1796,13 @@ public class Editor { // 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 SelectionActionModeCallback(); - mSelectionActionMode = mTextView.startActionMode( + ActionMode.Callback actionModeCallback = + new TextActionModeCallback(true /* hasSelection */); + mTextActionMode = mTextView.startActionMode( actionModeCallback, ActionMode.TYPE_FLOATING); } - final boolean selectionStarted = mSelectionActionMode != null || willExtract; + final boolean selectionStarted = mTextActionMode != null || willExtract; 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(); @@ -1906,7 +1903,7 @@ public class Editor { void onTouchUpEvent(MotionEvent event) { boolean selectAllGotFocus = mSelectAllOnFocus && mTextView.didTouchFocusSelect(); hideControllers(); - stopSelectionActionMode(); + stopTextActionMode(); CharSequence text = mTextView.getText(); if (!selectAllGotFocus && text.length() > 0) { // Move cursor @@ -1920,8 +1917,8 @@ public class Editor { if (!extractedTextModeWillBeStarted()) { if (isCursorInsideEasyCorrectionSpan()) { // Cancel the single tap delayed runnable. - if (mSelectionModeWithoutSelectionRunnable != null) { - mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); + if (mInsertionActionModeRunnable != null) { + mTextView.removeCallbacks(mInsertionActionModeRunnable); } mShowSuggestionRunnable = new Runnable() { @@ -1939,10 +1936,10 @@ public class Editor { } } - protected void stopSelectionActionMode() { - if (mSelectionActionMode != null) { + protected void stopTextActionMode() { + if (mTextActionMode != null) { // This will hide the mSelectionModifierCursorController - mSelectionActionMode.finish(); + mTextActionMode.finish(); } } @@ -2027,7 +2024,7 @@ public class Editor { mSuggestionsPopupWindow = new SuggestionsPopupWindow(); } hideControllers(); - stopSelectionActionMode(); + stopTextActionMode(); mSuggestionsPopupWindow.show(); } @@ -2035,8 +2032,8 @@ public class Editor { if (mPositionListener != null) { mPositionListener.onScrollChanged(); } - if (mSelectionActionMode != null) { - mSelectionActionMode.invalidateContentRect(); + if (mTextActionMode != null) { + mTextActionMode.invalidateContentRect(); } } @@ -3087,45 +3084,51 @@ public class Editor { } /** - * An ActionMode Callback class that is used to provide actions while in text selection mode. + * An ActionMode Callback class that is used to provide actions while in text insertion or + * selection mode. * - * The default callback provides a subset of Select All, Cut, Copy and Paste actions, depending - * on which of these this TextView supports. + * The default callback provides a subset of Select All, Cut, Copy, Paste, Share and Replace + * actions, depending on which of these this TextView supports and the current selection. */ - private class SelectionActionModeCallback extends ActionMode.Callback2 { + private class TextActionModeCallback extends ActionMode.Callback2 { private final Path mSelectionPath = new Path(); private final RectF mSelectionBounds = new RectF(); - - private int mSelectionHandleHeight; - private int mInsertionHandleHeight; - - public SelectionActionModeCallback() { - SelectionModifierCursorController selectionController = getSelectionController(); - if (selectionController.mStartHandle == null) { - // As these are for initializing selectionController, hide() must be called. - selectionController.initDrawables(); - selectionController.initHandles(); - selectionController.hide(); - } - mSelectionHandleHeight = Math.max( - mSelectHandleLeft.getMinimumHeight(), mSelectHandleRight.getMinimumHeight()); - InsertionPointCursorController insertionController = getInsertionController(); - if (insertionController != null) { - insertionController.getHandle(); - mInsertionHandleHeight = mSelectHandleCenter.getMinimumHeight(); + private final boolean mHasSelection; + + private int mHandleHeight; + + public TextActionModeCallback(boolean hasSelection) { + mHasSelection = hasSelection; + if (mHasSelection) { + SelectionModifierCursorController selectionController = getSelectionController(); + if (selectionController.mStartHandle == null) { + // As these are for initializing selectionController, hide() must be called. + selectionController.initDrawables(); + selectionController.initHandles(); + selectionController.hide(); + } + mHandleHeight = Math.max( + mSelectHandleLeft.getMinimumHeight(), + mSelectHandleRight.getMinimumHeight()); + } else { + InsertionPointCursorController insertionController = getInsertionController(); + if (insertionController != null) { + insertionController.getHandle(); + mHandleHeight = mSelectHandleCenter.getMinimumHeight(); + } } } @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { - mode.setTitle(mTextView.getContext().getString( - com.android.internal.R.string.textSelectionCABTitle)); + mode.setTitle(null); mode.setSubtitle(null); mode.setTitleOptionalHint(true); populateMenuWithItems(menu); - if (mCustomSelectionActionModeCallback != null) { - if (!mCustomSelectionActionModeCallback.onCreateActionMode(mode, menu)) { + Callback customCallback = getCustomCallback(); + if (customCallback != null) { + if (!customCallback.onCreateActionMode(mode, menu)) { // The custom mode can choose to cancel the action mode return false; } @@ -3141,6 +3144,12 @@ public class Editor { } } + private Callback getCustomCallback() { + return mHasSelection + ? mCustomSelectionActionModeCallback + : mCustomInsertionActionModeCallback; + } + private void populateMenuWithItems(Menu menu) { if (mTextView.canCut()) { menu.add(0, TextView.ID_CUT, 0, com.android.internal.R.string.cut). @@ -3203,8 +3212,9 @@ public class Editor { public boolean onPrepareActionMode(ActionMode mode, Menu menu) { updateReplaceItem(menu); - if (mCustomSelectionActionModeCallback != null) { - return mCustomSelectionActionModeCallback.onPrepareActionMode(mode, menu); + Callback customCallback = getCustomCallback(); + if (customCallback != null) { + return customCallback.onPrepareActionMode(mode, menu); } return true; } @@ -3230,8 +3240,8 @@ public class Editor { item.getIntent(), TextView.PROCESS_TEXT_REQUEST_CODE); return true; } - if (mCustomSelectionActionModeCallback != null && - mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) { + Callback customCallback = getCustomCallback(); + if (customCallback != null && customCallback.onActionItemClicked(mode, item)) { return true; } return mTextView.onTextContextMenuItem(item.getItemId()); @@ -3239,8 +3249,9 @@ public class Editor { @Override public void onDestroyActionMode(ActionMode mode) { - if (mCustomSelectionActionModeCallback != null) { - mCustomSelectionActionModeCallback.onDestroyActionMode(mode); + Callback customCallback = getCustomCallback(); + if (customCallback != null) { + customCallback.onDestroyActionMode(mode); } /* @@ -3259,7 +3270,7 @@ public class Editor { mSelectionModifierCursorController.resetTouchOffsets(); } - mSelectionActionMode = null; + mTextActionMode = null; } @Override @@ -3274,7 +3285,7 @@ public class Editor { mTextView.getLayout().getSelectionPath( mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mSelectionPath); mSelectionPath.computeBounds(mSelectionBounds, true); - mSelectionBounds.bottom += mSelectionHandleHeight; + mSelectionBounds.bottom += mHandleHeight; } else if (mCursorCount == 2) { // We have a split cursor. In this case, we take the rectangle that includes both // parts of the cursor to ensure we don't obscure either of them. @@ -3285,7 +3296,7 @@ public class Editor { Math.min(firstCursorBounds.top, secondCursorBounds.top), Math.max(firstCursorBounds.right, secondCursorBounds.right), Math.max(firstCursorBounds.bottom, secondCursorBounds.bottom) - + mInsertionHandleHeight); + + mHandleHeight); } else { // We have a single cursor. int line = mTextView.getLayout().getLineForOffset(mTextView.getSelectionStart()); @@ -3295,7 +3306,7 @@ public class Editor { primaryHorizontal, mTextView.getLayout().getLineTop(line), primaryHorizontal + 1, - mTextView.getLayout().getLineTop(line + 1) + mInsertionHandleHeight); + mTextView.getLayout().getLineTop(line + 1) + mHandleHeight); } // Take TextView's padding and scroll into account. int textHorizontalOffset = mTextView.viewportToContentHorizontalOffset(); @@ -3847,25 +3858,25 @@ public class Editor { SystemClock.uptimeMillis() - TextView.sLastCutCopyOrTextChangedTime; // Cancel the single tap delayed runnable. - if (mSelectionModeWithoutSelectionRunnable != null + if (mInsertionActionModeRunnable != null && (mDoubleTap || isCursorInsideEasyCorrectionSpan())) { - mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); + mTextView.removeCallbacks(mInsertionActionModeRunnable); } // Prepare and schedule the single tap runnable to run exactly after the double tap // timeout has passed. if (!mDoubleTap && !isCursorInsideEasyCorrectionSpan() && (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION)) { - if (mSelectionModeWithoutSelectionRunnable == null) { - mSelectionModeWithoutSelectionRunnable = new Runnable() { + if (mInsertionActionModeRunnable == null) { + mInsertionActionModeRunnable = new Runnable() { public void run() { - startSelectionActionModeWithoutSelection(); + startInsertionActionMode(); } }; } mTextView.postDelayed( - mSelectionModeWithoutSelectionRunnable, + mInsertionActionModeRunnable, ViewConfiguration.getDoubleTapTimeout() + 1); } @@ -3934,15 +3945,15 @@ public class Editor { if (distanceSquared < touchSlop * touchSlop) { // Tapping on the handle toggles the selection action mode. - if (mSelectionActionMode != null) { - mSelectionActionMode.finish(); + if (mTextActionMode != null) { + mTextActionMode.finish(); } else { - startSelectionActionModeWithoutSelection(); + startInsertionActionMode(); } } } else { - if (mSelectionActionMode != null) { - mSelectionActionMode.invalidateContentRect(); + if (mTextActionMode != null) { + mTextActionMode.invalidateContentRect(); } } hideAfterDelay(); @@ -3972,8 +3983,8 @@ public class Editor { @Override public void updatePosition(float x, float y) { positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false); - if (mSelectionActionMode != null) { - mSelectionActionMode.invalidate(); + if (mTextActionMode != null) { + mTextActionMode.invalidate(); } } @@ -4024,8 +4035,8 @@ public class Editor { Selection.setSelection((Spannable) mTextView.getText(), offset, mTextView.getSelectionEnd()); updateDrawable(); - if (mSelectionActionMode != null) { - mSelectionActionMode.invalidate(); + if (mTextActionMode != null) { + mTextActionMode.invalidate(); } } @@ -4150,8 +4161,8 @@ public class Editor { public void updateSelection(int offset) { Selection.setSelection((Spannable) mTextView.getText(), mTextView.getSelectionStart(), offset); - if (mSelectionActionMode != null) { - mSelectionActionMode.invalidate(); + if (mTextActionMode != null) { + mTextActionMode.invalidate(); } updateDrawable(); } @@ -4515,7 +4526,7 @@ public class Editor { mEndHandle.showAtLocation(endOffset); // No longer the first dragging motion, reset. - startSelectionActionModeWithSelection(); + startSelectionActionMode(); mDragAcceleratorActive = false; mStartOffset = -1; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 3a85476..6872caa 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -1481,7 +1481,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } if (mEditor.hasSelectionController()) { - mEditor.startSelectionActionModeWithSelection(); + mEditor.startSelectionActionMode(); } } } @@ -5282,7 +5282,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) { - mEditor.startSelectionActionModeWithSelection(); + mEditor.startSelectionActionMode(); mEditor.mCreatedWithASelection = false; } @@ -5290,7 +5290,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // ExtractEditText does not call onFocus when it is displayed, and mHasSelectionOnFocus can // not be set. Do the test here instead. if (this instanceof ExtractEditText && hasSelection() && mEditor != null) { - mEditor.startSelectionActionModeWithSelection(); + mEditor.startSelectionActionMode(); } unregisterForPreDraw(); @@ -5908,7 +5908,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean onKeyPreIme(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - boolean isInSelectionMode = mEditor != null && mEditor.mSelectionActionMode != null; + boolean isInSelectionMode = mEditor != null && mEditor.mTextActionMode != null; if (isInSelectionMode) { if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { @@ -5923,7 +5923,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener state.handleUpEvent(event); } if (event.isTracking() && !event.isCanceled()) { - stopSelectionActionMode(); + stopTextActionMode(); return true; } } @@ -6092,8 +6092,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Has to be done on key down (and not on key up) to correctly be intercepted. case KeyEvent.KEYCODE_BACK: - if (mEditor != null && mEditor.mSelectionActionMode != null) { - stopSelectionActionMode(); + if (mEditor != null && mEditor.mTextActionMode != null) { + stopTextActionMode(); return -1; } break; @@ -6423,7 +6423,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // extracted mode will start. Some text is selected though, and will trigger an action mode // in the extracted view. mEditor.hideControllers(); - stopSelectionActionMode(); + stopTextActionMode(); } /** @@ -8258,7 +8258,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener super.onVisibilityChanged(changedView, visibility); if (mEditor != null && visibility != VISIBLE) { mEditor.hideControllers(); - stopSelectionActionMode(); + stopTextActionMode(); } } @@ -8976,7 +8976,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Selection.setSelection((Spannable) text, start, end); // Make sure selection mode is engaged. if (mEditor != null) { - mEditor.startSelectionActionModeWithSelection(); + mEditor.startSelectionActionMode(); } return true; } @@ -9100,12 +9100,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case ID_CUT: setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max))); deleteText_internal(min, max); - stopSelectionActionMode(); + stopTextActionMode(); return true; case ID_COPY: setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max))); - stopSelectionActionMode(); + stopTextActionMode(); return true; case ID_REPLACE: @@ -9195,14 +9195,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * selection is initiated in this View. * * The standard implementation populates the menu with a subset of Select All, Cut, Copy, - * Paste and Share actions, depending on what this View supports. + * Paste, Replace and Share actions, depending on what this View supports. * * A custom implementation can add new entries in the default menu in its * {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The * default actions can also be removed from the menu using * {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll}, - * {@link android.R.id#cut}, {@link android.R.id#copy}, {@link android.R.id#paste} or - * {@link android.R.id#shareText} ids as parameters. + * {@link android.R.id#cut}, {@link android.R.id#copy}, {@link android.R.id#paste}, + * {@link android.R.id#replaceText} or {@link android.R.id#shareText} ids as parameters. * * Returning false from * {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent @@ -9230,11 +9230,48 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * If provided, this ActionMode.Callback will be used to create the ActionMode when text + * insertion is initiated in this View. + * + * The standard implementation populates the menu with a subset of Select All, + * Paste and Replace actions, depending on what this View supports. + * + * A custom implementation can add new entries in the default menu in its + * {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The + * default actions can also be removed from the menu using + * {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll}, + * {@link android.R.id#paste} or {@link android.R.id#replaceText} ids as parameters. + * + * Returning false from + * {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent + * the action mode from being started. + * + * Action click events should be handled by the custom implementation of + * {@link android.view.ActionMode.Callback#onActionItemClicked(ActionMode, MenuItem)}. + * + * Note that text insertion mode is not started when a TextView receives focus and the + * {@link android.R.attr#selectAllOnFocus} flag has been set. + */ + public void setCustomInsertionActionModeCallback(ActionMode.Callback actionModeCallback) { + createEditorIfNeeded(); + mEditor.mCustomInsertionActionModeCallback = actionModeCallback; + } + + /** + * Retrieves the value set in {@link #setCustomInsertionActionModeCallback}. Default is null. + * + * @return The current custom insertion callback. + */ + public ActionMode.Callback getCustomInsertionActionModeCallback() { + return mEditor == null ? null : mEditor.mCustomInsertionActionModeCallback; + } + + /** * @hide */ - protected void stopSelectionActionMode() { + protected void stopTextActionMode() { if (mEditor != null) { - mEditor.stopSelectionActionMode(); + mEditor.stopTextActionMode(); } } @@ -9346,7 +9383,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } } - stopSelectionActionMode(); + stopTextActionMode(); sLastCutCopyOrTextChangedTime = 0; } } @@ -9359,7 +9396,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)); - stopSelectionActionMode(); + stopTextActionMode(); } } |