Merge "Stop automatically creating selection action mode." into nyc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
126fdf1082
@@ -217,6 +217,7 @@ public class Editor {
|
||||
boolean mInBatchEditControllers;
|
||||
boolean mShowSoftInputOnFocus = true;
|
||||
private boolean mPreserveSelection;
|
||||
private boolean mRestartActionModeOnNextRefresh;
|
||||
boolean mTemporaryDetach;
|
||||
|
||||
boolean mIsBeingLongClicked;
|
||||
@@ -381,9 +382,8 @@ public class Editor {
|
||||
updateSpellCheckSpans(0, mTextView.getText().length(),
|
||||
true /* create the spell checker if needed */);
|
||||
|
||||
if (mTextView.getSelectionStart() != mTextView.getSelectionEnd()) {
|
||||
// We had an active selection from before, start the selection mode.
|
||||
startSelectionActionMode();
|
||||
if (mTextView.hasSelection()) {
|
||||
refreshTextActionMode();
|
||||
}
|
||||
|
||||
getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true);
|
||||
@@ -1284,7 +1284,7 @@ public class Editor {
|
||||
}
|
||||
final InputMethodManager imm = InputMethodManager.peekInstance();
|
||||
if (mTextView.hasSelection() && !extractedTextModeWillBeStarted()) {
|
||||
startSelectionActionMode();
|
||||
refreshTextActionMode();
|
||||
}
|
||||
} else {
|
||||
if (mBlink != null) {
|
||||
@@ -1847,6 +1847,7 @@ public class Editor {
|
||||
|
||||
void refreshTextActionMode() {
|
||||
if (extractedTextModeWillBeStarted()) {
|
||||
mRestartActionModeOnNextRefresh = false;
|
||||
return;
|
||||
}
|
||||
final boolean hasSelection = mTextView.hasSelection();
|
||||
@@ -1855,12 +1856,19 @@ public class Editor {
|
||||
if ((selectionController != null && selectionController.isCursorBeingModified())
|
||||
|| (insertionController != null && insertionController.isCursorBeingModified())) {
|
||||
// ActionMode should be managed by the currently active cursor controller.
|
||||
mRestartActionModeOnNextRefresh = false;
|
||||
return;
|
||||
}
|
||||
if (hasSelection) {
|
||||
if (mTextActionMode == null || selectionController == null
|
||||
|| !selectionController.isActive()) {
|
||||
// Avoid dismissing the selection if it exists.
|
||||
hideInsertionPointCursorController();
|
||||
if (mTextActionMode == null) {
|
||||
if (mRestartActionModeOnNextRefresh || mTextView.isInExtractedMode()) {
|
||||
// To avoid distraction, newly start action mode only when selection action
|
||||
// mode is being restarted or in full screen extracted mode.
|
||||
startSelectionActionMode();
|
||||
}
|
||||
} else if (selectionController == null || !selectionController.isActive()) {
|
||||
// Insertion action mode is active. Avoid dismissing the selection.
|
||||
stopTextActionModeWithPreservingSelection();
|
||||
startSelectionActionMode();
|
||||
} else {
|
||||
@@ -1875,6 +1883,7 @@ public class Editor {
|
||||
mTextActionMode.invalidateContentRect();
|
||||
}
|
||||
}
|
||||
mRestartActionModeOnNextRefresh = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1905,11 +1914,12 @@ public class Editor {
|
||||
*
|
||||
* @return true if the selection mode was actually started.
|
||||
*/
|
||||
private boolean startSelectionActionMode() {
|
||||
boolean startSelectionActionMode() {
|
||||
boolean selectionStarted = startSelectionActionModeInternal();
|
||||
if (selectionStarted) {
|
||||
getSelectionController().show();
|
||||
}
|
||||
mRestartActionModeOnNextRefresh = false;
|
||||
return selectionStarted;
|
||||
}
|
||||
|
||||
@@ -2113,6 +2123,9 @@ public class Editor {
|
||||
}
|
||||
|
||||
private void stopTextActionModeWithPreservingSelection() {
|
||||
if (mTextActionMode != null) {
|
||||
mRestartActionModeOnNextRefresh = true;
|
||||
}
|
||||
mPreserveSelection = true;
|
||||
stopTextActionMode();
|
||||
mPreserveSelection = false;
|
||||
@@ -3707,6 +3720,8 @@ public class Editor {
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
// Clear mTextActionMode not to recursively destroy action mode by clearing selection.
|
||||
mTextActionMode = null;
|
||||
Callback customCallback = getCustomCallback();
|
||||
if (customCallback != null) {
|
||||
customCallback.onDestroyActionMode(mode);
|
||||
@@ -3725,8 +3740,6 @@ public class Editor {
|
||||
if (mSelectionModifierCursorController != null) {
|
||||
mSelectionModifierCursorController.hide();
|
||||
}
|
||||
|
||||
mTextActionMode = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -5129,27 +5142,12 @@ public class Editor {
|
||||
// No longer dragging to select text, let the parent intercept events.
|
||||
mTextView.getParent().requestDisallowInterceptTouchEvent(false);
|
||||
|
||||
int startOffset = mTextView.getSelectionStart();
|
||||
int endOffset = mTextView.getSelectionEnd();
|
||||
|
||||
// Since we don't let drag handles pass once they're visible, we need to
|
||||
// make sure the start / end locations are correct because the user *can*
|
||||
// switch directions during the initial drag.
|
||||
if (endOffset < startOffset) {
|
||||
int tmp = endOffset;
|
||||
endOffset = startOffset;
|
||||
startOffset = tmp;
|
||||
|
||||
// Also update the selection with the right offsets in this case.
|
||||
Selection.setSelection((Spannable) mTextView.getText(),
|
||||
startOffset, endOffset);
|
||||
}
|
||||
if (startOffset != endOffset) {
|
||||
startSelectionActionMode();
|
||||
}
|
||||
|
||||
// No longer the first dragging motion, reset.
|
||||
resetDragAcceleratorState();
|
||||
|
||||
if (mTextView.hasSelection()) {
|
||||
startSelectionActionMode();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9212,6 +9212,10 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ import com.android.frameworks.coretests.R;
|
||||
import android.support.test.espresso.action.EspressoKey;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.text.Selection;
|
||||
import android.text.Spannable;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
@@ -534,4 +536,68 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
|
||||
.perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('i')));
|
||||
onView(withId(R.id.textview)).check(hasSelection("hijk"));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testSetSelectionAndActionMode() throws Exception {
|
||||
final String text = "abc def";
|
||||
onView(withId(R.id.textview)).perform(click());
|
||||
onView(withId(R.id.textview)).perform(replaceText(text));
|
||||
|
||||
final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
|
||||
assertFloatingToolbarIsNotDisplayed();
|
||||
textView.post(() -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
|
||||
getInstrumentation().waitForIdleSync();
|
||||
sleepForFloatingToolbarPopup();
|
||||
// Don't automatically start action mode.
|
||||
assertFloatingToolbarIsNotDisplayed();
|
||||
// Make sure that "Select All" is included in the selection action mode when the entire text
|
||||
// is not selected.
|
||||
onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('e')));
|
||||
sleepForFloatingToolbarPopup();
|
||||
assertFloatingToolbarIsDisplayed();
|
||||
// Changing the selection range by API should not interrupt the selection action mode.
|
||||
textView.post(() -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
|
||||
getInstrumentation().waitForIdleSync();
|
||||
sleepForFloatingToolbarPopup();
|
||||
assertFloatingToolbarIsDisplayed();
|
||||
assertFloatingToolbarContainsItem(
|
||||
getActivity().getString(com.android.internal.R.string.selectAll));
|
||||
// Make sure that "Select All" is no longer included when the entire text is selected by
|
||||
// API.
|
||||
textView.post(
|
||||
() -> Selection.setSelection((Spannable) textView.getText(), 0, text.length()));
|
||||
getInstrumentation().waitForIdleSync();
|
||||
sleepForFloatingToolbarPopup();
|
||||
assertFloatingToolbarIsDisplayed();
|
||||
assertFloatingToolbarDoesNotContainItem(
|
||||
getActivity().getString(com.android.internal.R.string.selectAll));
|
||||
// Make sure that shrinking the selection range to cursor (an empty range) by API
|
||||
// terminates selection action mode and does not trigger the insertion action mode.
|
||||
textView.post(() -> Selection.setSelection((Spannable) textView.getText(), 0));
|
||||
getInstrumentation().waitForIdleSync();
|
||||
sleepForFloatingToolbarPopup();
|
||||
assertFloatingToolbarIsNotDisplayed();
|
||||
// Make sure that user click can trigger the insertion action mode.
|
||||
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
|
||||
onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
|
||||
sleepForFloatingToolbarPopup();
|
||||
assertFloatingToolbarIsDisplayed();
|
||||
// Make sure that an existing insertion action mode keeps alive after the insertion point is
|
||||
// moved by API.
|
||||
textView.post(() -> Selection.setSelection((Spannable) textView.getText(), 0));
|
||||
getInstrumentation().waitForIdleSync();
|
||||
sleepForFloatingToolbarPopup();
|
||||
assertFloatingToolbarIsDisplayed();
|
||||
assertFloatingToolbarDoesNotContainItem(
|
||||
getActivity().getString(com.android.internal.R.string.copy));
|
||||
// Make sure that selection action mode is started after selection is created by API when
|
||||
// insertion action mode is active.
|
||||
textView.post(
|
||||
() -> Selection.setSelection((Spannable) textView.getText(), 1, text.length()));
|
||||
getInstrumentation().waitForIdleSync();
|
||||
sleepForFloatingToolbarPopup();
|
||||
assertFloatingToolbarIsDisplayed();
|
||||
assertFloatingToolbarContainsItem(
|
||||
getActivity().getString(com.android.internal.R.string.copy));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user