Merge "Separate the SelectionActionMode into Insertion and Selection." into mnc-dev

This commit is contained in:
Clara Bayarri
2015-06-05 00:40:41 +00:00
committed by Android (Google) Code Review
5 changed files with 183 additions and 131 deletions

View File

@@ -41542,6 +41542,7 @@ package android.widget {
method public int getCompoundPaddingTop(); method public int getCompoundPaddingTop();
method public final int getCurrentHintTextColor(); method public final int getCurrentHintTextColor();
method public final int getCurrentTextColor(); method public final int getCurrentTextColor();
method public android.view.ActionMode.Callback getCustomInsertionActionModeCallback();
method public android.view.ActionMode.Callback getCustomSelectionActionModeCallback(); method public android.view.ActionMode.Callback getCustomSelectionActionModeCallback();
method protected boolean getDefaultEditable(); method protected boolean getDefaultEditable();
method protected android.text.method.MovementMethod getDefaultMovementMethod(); 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(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 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 setCursorVisible(boolean);
method public void setCustomInsertionActionModeCallback(android.view.ActionMode.Callback);
method public void setCustomSelectionActionModeCallback(android.view.ActionMode.Callback); method public void setCustomSelectionActionModeCallback(android.view.ActionMode.Callback);
method public final void setEditableFactory(android.text.Editable.Factory); method public final void setEditableFactory(android.text.Editable.Factory);
method public void setElegantTextHeight(boolean); method public void setElegantTextHeight(boolean);

View File

@@ -44121,6 +44121,7 @@ package android.widget {
method public int getCompoundPaddingTop(); method public int getCompoundPaddingTop();
method public final int getCurrentHintTextColor(); method public final int getCurrentHintTextColor();
method public final int getCurrentTextColor(); method public final int getCurrentTextColor();
method public android.view.ActionMode.Callback getCustomInsertionActionModeCallback();
method public android.view.ActionMode.Callback getCustomSelectionActionModeCallback(); method public android.view.ActionMode.Callback getCustomSelectionActionModeCallback();
method protected boolean getDefaultEditable(); method protected boolean getDefaultEditable();
method protected android.text.method.MovementMethod getDefaultMovementMethod(); 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(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 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 setCursorVisible(boolean);
method public void setCustomInsertionActionModeCallback(android.view.ActionMode.Callback);
method public void setCustomSelectionActionModeCallback(android.view.ActionMode.Callback); method public void setCustomSelectionActionModeCallback(android.view.ActionMode.Callback);
method public final void setEditableFactory(android.text.Editable.Factory); method public final void setEditableFactory(android.text.Editable.Factory);
method public void setElegantTextHeight(boolean); method public void setElegantTextHeight(boolean);

View File

@@ -106,7 +106,7 @@ public class ExtractEditText extends EditText {
if (mIME != null && mIME.onExtractTextContextMenuItem(id)) { if (mIME != null && mIME.onExtractTextContextMenuItem(id)) {
// Mode was started on Extracted, needs to be stopped here. // Mode was started on Extracted, needs to be stopped here.
// Cut and paste will change the text, which stops selection mode. // 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 true;
} }
return super.onTextContextMenuItem(id); return super.onTextContextMenuItem(id);

View File

@@ -134,7 +134,8 @@ public class Editor {
// Cursor Controllers. // Cursor Controllers.
InsertionPointCursorController mInsertionPointCursorController; InsertionPointCursorController mInsertionPointCursorController;
SelectionModifierCursorController mSelectionModifierCursorController; 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 mInsertionControllerEnabled;
boolean mSelectionControllerEnabled; boolean mSelectionControllerEnabled;
@@ -205,13 +206,14 @@ public class Editor {
float mLastDownPositionX, mLastDownPositionY; float mLastDownPositionX, mLastDownPositionY;
Callback mCustomSelectionActionModeCallback; Callback mCustomSelectionActionModeCallback;
Callback mCustomInsertionActionModeCallback;
// Set when this TextView gained focus with some text selected. Will start selection mode. // Set when this TextView gained focus with some text selected. Will start selection mode.
boolean mCreatedWithASelection; boolean mCreatedWithASelection;
boolean mDoubleTap = false; boolean mDoubleTap = false;
private Runnable mSelectionModeWithoutSelectionRunnable; private Runnable mInsertionActionModeRunnable;
// The span controller helps monitoring the changes to which the Editor needs to react: // 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 // - 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() { private final Runnable mHideFloatingToolbar = new Runnable() {
@Override @Override
public void run() { public void run() {
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
mSelectionActionMode.snooze(ActionMode.SNOOZE_TIME_DEFAULT); mTextActionMode.snooze(ActionMode.SNOOZE_TIME_DEFAULT);
} }
} }
}; };
@@ -245,8 +247,8 @@ public class Editor {
private final Runnable mShowFloatingToolbar = new Runnable() { private final Runnable mShowFloatingToolbar = new Runnable() {
@Override @Override
public void run() { public void run() {
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
mSelectionActionMode.snooze(0); // snooze off. mTextActionMode.snooze(0); // snooze off.
} }
} }
}; };
@@ -310,7 +312,7 @@ public class Editor {
void replace() { void replace() {
int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2; int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2;
stopSelectionActionMode(); stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), middle); Selection.setSelection((Spannable) mTextView.getText(), middle);
showSuggestions(); showSuggestions();
} }
@@ -343,7 +345,7 @@ public class Editor {
mTextView.setHasTransientState(false); mTextView.setHasTransientState(false);
// We had an active selection from before, start the selection mode. // We had an active selection from before, start the selection mode.
startSelectionActionModeWithSelection(); startSelectionActionMode();
} }
getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true); getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true);
@@ -372,8 +374,8 @@ public class Editor {
} }
// Cancel the single tap delayed runnable. // Cancel the single tap delayed runnable.
if (mSelectionModeWithoutSelectionRunnable != null) { if (mInsertionActionModeRunnable != null) {
mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); mTextView.removeCallbacks(mInsertionActionModeRunnable);
} }
mTextView.removeCallbacks(mHideFloatingToolbar); mTextView.removeCallbacks(mHideFloatingToolbar);
@@ -390,7 +392,7 @@ public class Editor {
mPreserveDetachedSelection = true; mPreserveDetachedSelection = true;
hideControllers(); hideControllers();
stopSelectionActionMode(); stopTextActionMode();
mPreserveDetachedSelection = false; mPreserveDetachedSelection = false;
mTemporaryDetach = false; mTemporaryDetach = false;
} }
@@ -586,7 +588,7 @@ public class Editor {
} }
if (!mSelectionControllerEnabled) { if (!mSelectionControllerEnabled) {
stopSelectionActionMode(); stopTextActionMode();
if (mSelectionModifierCursorController != null) { if (mSelectionModifierCursorController != null) {
mSelectionModifierCursorController.onDetached(); mSelectionModifierCursorController.onDetached();
mSelectionModifierCursorController = null; mSelectionModifierCursorController = null;
@@ -984,14 +986,14 @@ public class Editor {
mInsertionControllerEnabled) { mInsertionControllerEnabled) {
final int offset = mTextView.getOffsetForPosition(mLastDownPositionX, final int offset = mTextView.getOffsetForPosition(mLastDownPositionX,
mLastDownPositionY); mLastDownPositionY);
stopSelectionActionMode(); stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), offset); Selection.setSelection((Spannable) mTextView.getText(), offset);
getInsertionController().show(); getInsertionController().show();
startSelectionActionModeWithoutSelection(); startInsertionActionMode();
handled = true; handled = true;
} }
if (!handled && mSelectionActionMode != null) { if (!handled && mTextActionMode != null) {
if (touchPositionIsInSelection()) { if (touchPositionIsInSelection()) {
// Start a drag // Start a drag
final int start = mTextView.getSelectionStart(); final int start = mTextView.getSelectionStart();
@@ -1001,9 +1003,9 @@ public class Editor {
DragLocalState localState = new DragLocalState(mTextView, start, end); DragLocalState localState = new DragLocalState(mTextView, start, end);
mTextView.startDrag(data, getTextThumbnailBuilder(selectedText), localState, mTextView.startDrag(data, getTextThumbnailBuilder(selectedText), localState,
View.DRAG_FLAG_GLOBAL); View.DRAG_FLAG_GLOBAL);
stopSelectionActionMode(); stopTextActionMode();
} else { } else {
stopSelectionActionMode(); stopTextActionMode();
selectCurrentWordAndStartDrag(); selectCurrentWordAndStartDrag();
} }
handled = true; handled = true;
@@ -1101,12 +1103,12 @@ public class Editor {
final int selStart = mTextView.getSelectionStart(); final int selStart = mTextView.getSelectionStart();
final int selEnd = mTextView.getSelectionEnd(); final int selEnd = mTextView.getSelectionEnd();
hideControllers(); hideControllers();
stopSelectionActionMode(); stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), selStart, selEnd); Selection.setSelection((Spannable) mTextView.getText(), selStart, selEnd);
} else { } else {
if (mTemporaryDetach) mPreserveDetachedSelection = true; if (mTemporaryDetach) mPreserveDetachedSelection = true;
hideControllers(); hideControllers();
stopSelectionActionMode(); stopTextActionMode();
if (mTemporaryDetach) mPreserveDetachedSelection = false; if (mTemporaryDetach) mPreserveDetachedSelection = false;
downgradeEasyCorrectionSpans(); 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 // We do not hide the span controllers, since they can be added when a new text is
// inserted into the text view (voice IME). // inserted into the text view (voice IME).
hideCursorControllers(); hideCursorControllers();
stopSelectionActionMode(); stopTextActionMode();
} }
private int getLastTapPosition() { private int getLastTapPosition() {
@@ -1216,7 +1218,7 @@ public class Editor {
} }
private void updateFloatingToolbarVisibility(MotionEvent event) { private void updateFloatingToolbarVisibility(MotionEvent event) {
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
switch (event.getActionMasked()) { switch (event.getActionMasked()) {
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
hideFloatingToolbar(); hideFloatingToolbar();
@@ -1229,7 +1231,7 @@ public class Editor {
} }
private void hideFloatingToolbar() { private void hideFloatingToolbar() {
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
mTextView.removeCallbacks(mShowFloatingToolbar); mTextView.removeCallbacks(mShowFloatingToolbar);
// Delay the "hide" a little bit just in case a "show" will happen almost immediately. // Delay the "hide" a little bit just in case a "show" will happen almost immediately.
mTextView.postDelayed(mHideFloatingToolbar, 100); mTextView.postDelayed(mHideFloatingToolbar, 100);
@@ -1237,7 +1239,7 @@ public class Editor {
} }
private void showFloatingToolbar() { private void showFloatingToolbar() {
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
mTextView.removeCallbacks(mHideFloatingToolbar); mTextView.removeCallbacks(mHideFloatingToolbar);
// Delay "show" so it doesn't interfere with click confirmations // Delay "show" so it doesn't interfere with click confirmations
// or double-clicks that could "dismiss" the floating toolbar. // 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. * @return true if the selection mode was actually started.
*/ */
private boolean startSelectionActionModeWithoutSelection() { private boolean startInsertionActionMode() {
if (mInsertionActionModeRunnable != null) {
mTextView.removeCallbacks(mInsertionActionModeRunnable);
}
if (extractedTextModeWillBeStarted()) { if (extractedTextModeWillBeStarted()) {
// Cancel the single tap delayed runnable.
if (mSelectionModeWithoutSelectionRunnable != null) {
mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
}
return false; return false;
} }
stopTextActionMode();
if (mSelectionActionMode != null) { ActionMode.Callback actionModeCallback =
// Selection action mode is already started new TextActionModeCallback(false /* hasSelection */);
// TODO: revisit invocations to minimize this case. mTextActionMode = mTextView.startActionMode(
mSelectionActionMode.invalidate();
return false;
}
ActionMode.Callback actionModeCallback = new SelectionActionModeCallback();
mSelectionActionMode = mTextView.startActionMode(
actionModeCallback, ActionMode.TYPE_FLOATING); 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. * @return true if the selection mode was actually started.
*/ */
boolean startSelectionActionModeWithSelection() { boolean startSelectionActionMode() {
boolean selectionStarted = startSelectionActionModeWithSelectionInternal(); boolean selectionStarted = startSelectionActionModeInternal();
if (selectionStarted) { if (selectionStarted) {
getSelectionController().show(); getSelectionController().show();
} else if (getInsertionController() != null) { } else if (getInsertionController() != null) {
@@ -1749,13 +1745,13 @@ public class Editor {
private boolean selectCurrentWordAndStartDrag() { private boolean selectCurrentWordAndStartDrag() {
if (extractedTextModeWillBeStarted()) { if (extractedTextModeWillBeStarted()) {
// Cancel the single tap delayed runnable. // Cancel the single tap delayed runnable.
if (mSelectionModeWithoutSelectionRunnable != null) { if (mInsertionActionModeRunnable != null) {
mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); mTextView.removeCallbacks(mInsertionActionModeRunnable);
} }
return false; return false;
} }
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
mSelectionActionMode.finish(); mTextActionMode.finish();
} }
if (!checkFieldAndSelectCurrentWord()) { if (!checkFieldAndSelectCurrentWord()) {
return false; return false;
@@ -1784,10 +1780,10 @@ public class Editor {
return true; return true;
} }
private boolean startSelectionActionModeWithSelectionInternal() { private boolean startSelectionActionModeInternal() {
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
// Selection action mode is already started // Selection action mode is already started
mSelectionActionMode.invalidate(); mTextActionMode.invalidate();
return false; 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 // 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. // immediately hide the newly created action bar and would be visually distracting.
if (!willExtract) { if (!willExtract) {
ActionMode.Callback actionModeCallback = new SelectionActionModeCallback(); ActionMode.Callback actionModeCallback =
mSelectionActionMode = mTextView.startActionMode( new TextActionModeCallback(true /* hasSelection */);
mTextActionMode = mTextView.startActionMode(
actionModeCallback, ActionMode.TYPE_FLOATING); actionModeCallback, ActionMode.TYPE_FLOATING);
} }
final boolean selectionStarted = mSelectionActionMode != null || willExtract; final boolean selectionStarted = mTextActionMode != null || willExtract;
if (selectionStarted && !mTextView.isTextSelectable() && mShowSoftInputOnFocus) { if (selectionStarted && !mTextView.isTextSelectable() && mShowSoftInputOnFocus) {
// Show the IME to be able to replace text, except when selecting non editable text. // Show the IME to be able to replace text, except when selecting non editable text.
final InputMethodManager imm = InputMethodManager.peekInstance(); final InputMethodManager imm = InputMethodManager.peekInstance();
@@ -1906,7 +1903,7 @@ public class Editor {
void onTouchUpEvent(MotionEvent event) { void onTouchUpEvent(MotionEvent event) {
boolean selectAllGotFocus = mSelectAllOnFocus && mTextView.didTouchFocusSelect(); boolean selectAllGotFocus = mSelectAllOnFocus && mTextView.didTouchFocusSelect();
hideControllers(); hideControllers();
stopSelectionActionMode(); stopTextActionMode();
CharSequence text = mTextView.getText(); CharSequence text = mTextView.getText();
if (!selectAllGotFocus && text.length() > 0) { if (!selectAllGotFocus && text.length() > 0) {
// Move cursor // Move cursor
@@ -1920,8 +1917,8 @@ public class Editor {
if (!extractedTextModeWillBeStarted()) { if (!extractedTextModeWillBeStarted()) {
if (isCursorInsideEasyCorrectionSpan()) { if (isCursorInsideEasyCorrectionSpan()) {
// Cancel the single tap delayed runnable. // Cancel the single tap delayed runnable.
if (mSelectionModeWithoutSelectionRunnable != null) { if (mInsertionActionModeRunnable != null) {
mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); mTextView.removeCallbacks(mInsertionActionModeRunnable);
} }
mShowSuggestionRunnable = new Runnable() { mShowSuggestionRunnable = new Runnable() {
@@ -1939,10 +1936,10 @@ public class Editor {
} }
} }
protected void stopSelectionActionMode() { protected void stopTextActionMode() {
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
// This will hide the mSelectionModifierCursorController // This will hide the mSelectionModifierCursorController
mSelectionActionMode.finish(); mTextActionMode.finish();
} }
} }
@@ -2027,7 +2024,7 @@ public class Editor {
mSuggestionsPopupWindow = new SuggestionsPopupWindow(); mSuggestionsPopupWindow = new SuggestionsPopupWindow();
} }
hideControllers(); hideControllers();
stopSelectionActionMode(); stopTextActionMode();
mSuggestionsPopupWindow.show(); mSuggestionsPopupWindow.show();
} }
@@ -2035,8 +2032,8 @@ public class Editor {
if (mPositionListener != null) { if (mPositionListener != null) {
mPositionListener.onScrollChanged(); mPositionListener.onScrollChanged();
} }
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
mSelectionActionMode.invalidateContentRect(); mTextActionMode.invalidateContentRect();
} }
} }
@@ -3087,19 +3084,22 @@ 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 * The default callback provides a subset of Select All, Cut, Copy, Paste, Share and Replace
* on which of these this TextView supports. * 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 Path mSelectionPath = new Path();
private final RectF mSelectionBounds = new RectF(); private final RectF mSelectionBounds = new RectF();
private final boolean mHasSelection;
private int mSelectionHandleHeight; private int mHandleHeight;
private int mInsertionHandleHeight;
public SelectionActionModeCallback() { public TextActionModeCallback(boolean hasSelection) {
mHasSelection = hasSelection;
if (mHasSelection) {
SelectionModifierCursorController selectionController = getSelectionController(); SelectionModifierCursorController selectionController = getSelectionController();
if (selectionController.mStartHandle == null) { if (selectionController.mStartHandle == null) {
// As these are for initializing selectionController, hide() must be called. // As these are for initializing selectionController, hide() must be called.
@@ -3107,25 +3107,28 @@ public class Editor {
selectionController.initHandles(); selectionController.initHandles();
selectionController.hide(); selectionController.hide();
} }
mSelectionHandleHeight = Math.max( mHandleHeight = Math.max(
mSelectHandleLeft.getMinimumHeight(), mSelectHandleRight.getMinimumHeight()); mSelectHandleLeft.getMinimumHeight(),
mSelectHandleRight.getMinimumHeight());
} else {
InsertionPointCursorController insertionController = getInsertionController(); InsertionPointCursorController insertionController = getInsertionController();
if (insertionController != null) { if (insertionController != null) {
insertionController.getHandle(); insertionController.getHandle();
mInsertionHandleHeight = mSelectHandleCenter.getMinimumHeight(); mHandleHeight = mSelectHandleCenter.getMinimumHeight();
}
} }
} }
@Override @Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) { public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.setTitle(mTextView.getContext().getString( mode.setTitle(null);
com.android.internal.R.string.textSelectionCABTitle));
mode.setSubtitle(null); mode.setSubtitle(null);
mode.setTitleOptionalHint(true); mode.setTitleOptionalHint(true);
populateMenuWithItems(menu); populateMenuWithItems(menu);
if (mCustomSelectionActionModeCallback != null) { Callback customCallback = getCustomCallback();
if (!mCustomSelectionActionModeCallback.onCreateActionMode(mode, menu)) { if (customCallback != null) {
if (!customCallback.onCreateActionMode(mode, menu)) {
// The custom mode can choose to cancel the action mode // The custom mode can choose to cancel the action mode
return false; return false;
} }
@@ -3141,6 +3144,12 @@ public class Editor {
} }
} }
private Callback getCustomCallback() {
return mHasSelection
? mCustomSelectionActionModeCallback
: mCustomInsertionActionModeCallback;
}
private void populateMenuWithItems(Menu menu) { private void populateMenuWithItems(Menu menu) {
if (mTextView.canCut()) { if (mTextView.canCut()) {
menu.add(0, TextView.ID_CUT, 0, com.android.internal.R.string.cut). 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) { public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
updateReplaceItem(menu); updateReplaceItem(menu);
if (mCustomSelectionActionModeCallback != null) { Callback customCallback = getCustomCallback();
return mCustomSelectionActionModeCallback.onPrepareActionMode(mode, menu); if (customCallback != null) {
return customCallback.onPrepareActionMode(mode, menu);
} }
return true; return true;
} }
@@ -3230,8 +3240,8 @@ public class Editor {
item.getIntent(), TextView.PROCESS_TEXT_REQUEST_CODE); item.getIntent(), TextView.PROCESS_TEXT_REQUEST_CODE);
return true; return true;
} }
if (mCustomSelectionActionModeCallback != null && Callback customCallback = getCustomCallback();
mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) { if (customCallback != null && customCallback.onActionItemClicked(mode, item)) {
return true; return true;
} }
return mTextView.onTextContextMenuItem(item.getItemId()); return mTextView.onTextContextMenuItem(item.getItemId());
@@ -3239,8 +3249,9 @@ public class Editor {
@Override @Override
public void onDestroyActionMode(ActionMode mode) { public void onDestroyActionMode(ActionMode mode) {
if (mCustomSelectionActionModeCallback != null) { Callback customCallback = getCustomCallback();
mCustomSelectionActionModeCallback.onDestroyActionMode(mode); if (customCallback != null) {
customCallback.onDestroyActionMode(mode);
} }
/* /*
@@ -3259,7 +3270,7 @@ public class Editor {
mSelectionModifierCursorController.resetTouchOffsets(); mSelectionModifierCursorController.resetTouchOffsets();
} }
mSelectionActionMode = null; mTextActionMode = null;
} }
@Override @Override
@@ -3274,7 +3285,7 @@ public class Editor {
mTextView.getLayout().getSelectionPath( mTextView.getLayout().getSelectionPath(
mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mSelectionPath); mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mSelectionPath);
mSelectionPath.computeBounds(mSelectionBounds, true); mSelectionPath.computeBounds(mSelectionBounds, true);
mSelectionBounds.bottom += mSelectionHandleHeight; mSelectionBounds.bottom += mHandleHeight;
} else if (mCursorCount == 2) { } else if (mCursorCount == 2) {
// We have a split cursor. In this case, we take the rectangle that includes both // 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. // 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.min(firstCursorBounds.top, secondCursorBounds.top),
Math.max(firstCursorBounds.right, secondCursorBounds.right), Math.max(firstCursorBounds.right, secondCursorBounds.right),
Math.max(firstCursorBounds.bottom, secondCursorBounds.bottom) Math.max(firstCursorBounds.bottom, secondCursorBounds.bottom)
+ mInsertionHandleHeight); + mHandleHeight);
} else { } else {
// We have a single cursor. // We have a single cursor.
int line = mTextView.getLayout().getLineForOffset(mTextView.getSelectionStart()); int line = mTextView.getLayout().getLineForOffset(mTextView.getSelectionStart());
@@ -3295,7 +3306,7 @@ public class Editor {
primaryHorizontal, primaryHorizontal,
mTextView.getLayout().getLineTop(line), mTextView.getLayout().getLineTop(line),
primaryHorizontal + 1, primaryHorizontal + 1,
mTextView.getLayout().getLineTop(line + 1) + mInsertionHandleHeight); mTextView.getLayout().getLineTop(line + 1) + mHandleHeight);
} }
// Take TextView's padding and scroll into account. // Take TextView's padding and scroll into account.
int textHorizontalOffset = mTextView.viewportToContentHorizontalOffset(); int textHorizontalOffset = mTextView.viewportToContentHorizontalOffset();
@@ -3847,25 +3858,25 @@ public class Editor {
SystemClock.uptimeMillis() - TextView.sLastCutCopyOrTextChangedTime; SystemClock.uptimeMillis() - TextView.sLastCutCopyOrTextChangedTime;
// Cancel the single tap delayed runnable. // Cancel the single tap delayed runnable.
if (mSelectionModeWithoutSelectionRunnable != null if (mInsertionActionModeRunnable != null
&& (mDoubleTap || isCursorInsideEasyCorrectionSpan())) { && (mDoubleTap || isCursorInsideEasyCorrectionSpan())) {
mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); mTextView.removeCallbacks(mInsertionActionModeRunnable);
} }
// Prepare and schedule the single tap runnable to run exactly after the double tap // Prepare and schedule the single tap runnable to run exactly after the double tap
// timeout has passed. // timeout has passed.
if (!mDoubleTap && !isCursorInsideEasyCorrectionSpan() if (!mDoubleTap && !isCursorInsideEasyCorrectionSpan()
&& (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION)) { && (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION)) {
if (mSelectionModeWithoutSelectionRunnable == null) { if (mInsertionActionModeRunnable == null) {
mSelectionModeWithoutSelectionRunnable = new Runnable() { mInsertionActionModeRunnable = new Runnable() {
public void run() { public void run() {
startSelectionActionModeWithoutSelection(); startInsertionActionMode();
} }
}; };
} }
mTextView.postDelayed( mTextView.postDelayed(
mSelectionModeWithoutSelectionRunnable, mInsertionActionModeRunnable,
ViewConfiguration.getDoubleTapTimeout() + 1); ViewConfiguration.getDoubleTapTimeout() + 1);
} }
@@ -3934,15 +3945,15 @@ public class Editor {
if (distanceSquared < touchSlop * touchSlop) { if (distanceSquared < touchSlop * touchSlop) {
// Tapping on the handle toggles the selection action mode. // Tapping on the handle toggles the selection action mode.
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
mSelectionActionMode.finish(); mTextActionMode.finish();
} else { } else {
startSelectionActionModeWithoutSelection(); startInsertionActionMode();
} }
} }
} else { } else {
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
mSelectionActionMode.invalidateContentRect(); mTextActionMode.invalidateContentRect();
} }
} }
hideAfterDelay(); hideAfterDelay();
@@ -3972,8 +3983,8 @@ public class Editor {
@Override @Override
public void updatePosition(float x, float y) { public void updatePosition(float x, float y) {
positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false); positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false);
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
mSelectionActionMode.invalidate(); mTextActionMode.invalidate();
} }
} }
@@ -4024,8 +4035,8 @@ public class Editor {
Selection.setSelection((Spannable) mTextView.getText(), offset, Selection.setSelection((Spannable) mTextView.getText(), offset,
mTextView.getSelectionEnd()); mTextView.getSelectionEnd());
updateDrawable(); updateDrawable();
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
mSelectionActionMode.invalidate(); mTextActionMode.invalidate();
} }
} }
@@ -4150,8 +4161,8 @@ public class Editor {
public void updateSelection(int offset) { public void updateSelection(int offset) {
Selection.setSelection((Spannable) mTextView.getText(), Selection.setSelection((Spannable) mTextView.getText(),
mTextView.getSelectionStart(), offset); mTextView.getSelectionStart(), offset);
if (mSelectionActionMode != null) { if (mTextActionMode != null) {
mSelectionActionMode.invalidate(); mTextActionMode.invalidate();
} }
updateDrawable(); updateDrawable();
} }
@@ -4515,7 +4526,7 @@ public class Editor {
mEndHandle.showAtLocation(endOffset); mEndHandle.showAtLocation(endOffset);
// No longer the first dragging motion, reset. // No longer the first dragging motion, reset.
startSelectionActionModeWithSelection(); startSelectionActionMode();
mDragAcceleratorActive = false; mDragAcceleratorActive = false;
mStartOffset = -1; mStartOffset = -1;
} }

View File

@@ -1481,7 +1481,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
} }
} }
if (mEditor.hasSelectionController()) { 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 // - 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. // a screen rotation) since layout is not yet initialized at that point.
if (mEditor != null && mEditor.mCreatedWithASelection) { if (mEditor != null && mEditor.mCreatedWithASelection) {
mEditor.startSelectionActionModeWithSelection(); mEditor.startSelectionActionMode();
mEditor.mCreatedWithASelection = false; 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 // ExtractEditText does not call onFocus when it is displayed, and mHasSelectionOnFocus can
// not be set. Do the test here instead. // not be set. Do the test here instead.
if (this instanceof ExtractEditText && hasSelection() && mEditor != null) { if (this instanceof ExtractEditText && hasSelection() && mEditor != null) {
mEditor.startSelectionActionModeWithSelection(); mEditor.startSelectionActionMode();
} }
unregisterForPreDraw(); unregisterForPreDraw();
@@ -5908,7 +5908,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override @Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) { public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) { if (keyCode == KeyEvent.KEYCODE_BACK) {
boolean isInSelectionMode = mEditor != null && mEditor.mSelectionActionMode != null; boolean isInSelectionMode = mEditor != null && mEditor.mTextActionMode != null;
if (isInSelectionMode) { if (isInSelectionMode) {
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
@@ -5923,7 +5923,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
state.handleUpEvent(event); state.handleUpEvent(event);
} }
if (event.isTracking() && !event.isCanceled()) { if (event.isTracking() && !event.isCanceled()) {
stopSelectionActionMode(); stopTextActionMode();
return true; 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. // Has to be done on key down (and not on key up) to correctly be intercepted.
case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_BACK:
if (mEditor != null && mEditor.mSelectionActionMode != null) { if (mEditor != null && mEditor.mTextActionMode != null) {
stopSelectionActionMode(); stopTextActionMode();
return -1; return -1;
} }
break; 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 // extracted mode will start. Some text is selected though, and will trigger an action mode
// in the extracted view. // in the extracted view.
mEditor.hideControllers(); mEditor.hideControllers();
stopSelectionActionMode(); stopTextActionMode();
} }
/** /**
@@ -8258,7 +8258,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
super.onVisibilityChanged(changedView, visibility); super.onVisibilityChanged(changedView, visibility);
if (mEditor != null && visibility != VISIBLE) { if (mEditor != null && visibility != VISIBLE) {
mEditor.hideControllers(); mEditor.hideControllers();
stopSelectionActionMode(); stopTextActionMode();
} }
} }
@@ -8976,7 +8976,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Selection.setSelection((Spannable) text, start, end); Selection.setSelection((Spannable) text, start, end);
// Make sure selection mode is engaged. // Make sure selection mode is engaged.
if (mEditor != null) { if (mEditor != null) {
mEditor.startSelectionActionModeWithSelection(); mEditor.startSelectionActionMode();
} }
return true; return true;
} }
@@ -9100,12 +9100,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case ID_CUT: case ID_CUT:
setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max))); setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max)));
deleteText_internal(min, max); deleteText_internal(min, max);
stopSelectionActionMode(); stopTextActionMode();
return true; return true;
case ID_COPY: case ID_COPY:
setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max))); setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max)));
stopSelectionActionMode(); stopTextActionMode();
return true; return true;
case ID_REPLACE: case ID_REPLACE:
@@ -9195,14 +9195,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* selection is initiated in this View. * selection is initiated in this View.
* *
* The standard implementation populates the menu with a subset of Select All, Cut, Copy, * 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 * A custom implementation can add new entries in the default menu in its
* {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The * {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The
* default actions can also be removed from the menu using * 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.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#cut}, {@link android.R.id#copy}, {@link android.R.id#paste},
* {@link android.R.id#shareText} ids as parameters. * {@link android.R.id#replaceText} or {@link android.R.id#shareText} ids as parameters.
* *
* Returning false from * Returning false from
* {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent * {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent
@@ -9229,12 +9229,49 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return mEditor == null ? null : mEditor.mCustomSelectionActionModeCallback; return mEditor == null ? null : mEditor.mCustomSelectionActionModeCallback;
} }
/**
* 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 * @hide
*/ */
protected void stopSelectionActionMode() { protected void stopTextActionMode() {
if (mEditor != null) { if (mEditor != null) {
mEditor.stopSelectionActionMode(); mEditor.stopTextActionMode();
} }
} }
@@ -9346,7 +9383,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
} }
} }
} }
stopSelectionActionMode(); stopTextActionMode();
sLastCutCopyOrTextChangedTime = 0; sLastCutCopyOrTextChangedTime = 0;
} }
} }
@@ -9359,7 +9396,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sharingIntent.removeExtra(android.content.Intent.EXTRA_TEXT); sharingIntent.removeExtra(android.content.Intent.EXTRA_TEXT);
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText); sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText);
getContext().startActivity(Intent.createChooser(sharingIntent, null)); getContext().startActivity(Intent.createChooser(sharingIntent, null));
stopSelectionActionMode(); stopTextActionMode();
} }
} }