Merge "Stop automatically creating selection action mode." into nyc-dev

This commit is contained in:
Keisuke Kuroyanagi
2016-03-15 07:41:06 +00:00
committed by Android (Google) Code Review
3 changed files with 97 additions and 29 deletions

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}