summaryrefslogtreecommitdiff
path: root/core/java/android/widget/SelectionActionModeHelper.java
diff options
context:
space:
mode:
authorJoanne Chung <joannechung@google.com>2020-03-23 22:40:14 +0800
committerJoanne Chung <joannechung@google.com>2020-04-16 17:37:05 +0800
commitcb3c97db0cf21df076735d71c573a754fa4ef072 (patch)
tree10bfb10a54170b13cfaba75f274f73ed36d059b3 /core/java/android/widget/SelectionActionModeHelper.java
parent82dc6147f16edc2106d509f97157bbc0c53a1ea4 (diff)
Fix app crash if the selection is from reverse direction.
When user types some text and then long presses on end of empty area to open context menu, if user opens Gboard and selects text from the reverse direction, SelectionEnd will be less than SelectionStart. The IllegalArgumentException occurred because TextClassification does not allow SelectionEnd is less than SelectionStart. We swap the start and end index if end index is less than start index. Bug: 150916165 Test: Manual. No crash occurs. Test: atest FrameworksCoreTests:android.widget.TextViewActivityTest Change-Id: I8dbc92f0f31e64b7e3a45ae91762e1b741629a8e
Diffstat (limited to 'core/java/android/widget/SelectionActionModeHelper.java')
-rw-r--r--core/java/android/widget/SelectionActionModeHelper.java73
1 files changed, 55 insertions, 18 deletions
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 45943f512c22..a069e643afcb 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -104,16 +104,43 @@ public final class SelectionActionModeHelper {
}
/**
+ * Swap the selection index if the start index is greater than end index.
+ *
+ * @return the swap result, index 0 is the start index and index 1 is the end index.
+ */
+ private static int[] sortSelctionIndices(int selectionStart, int selectionEnd) {
+ if (selectionStart < selectionEnd) {
+ return new int[]{selectionStart, selectionEnd};
+ }
+ return new int[]{selectionEnd, selectionStart};
+ }
+
+ /**
+ * The {@link TextView} selection start and end index may not be sorted, this method will swap
+ * the {@link TextView} selection index if the start index is greater than end index.
+ *
+ * @param textView the selected TextView.
+ * @return the swap result, index 0 is the start index and index 1 is the end index.
+ */
+ private static int[] sortSelctionIndicesFromTextView(TextView textView) {
+ int selectionStart = textView.getSelectionStart();
+ int selectionEnd = textView.getSelectionEnd();
+
+ return sortSelctionIndices(selectionStart, selectionEnd);
+ }
+
+ /**
* Starts Selection ActionMode.
*/
public void startSelectionActionModeAsync(boolean adjustSelection) {
// Check if the smart selection should run for editable text.
adjustSelection &= getTextClassificationSettings().isSmartSelectionEnabled();
+ int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
mSelectionTracker.onOriginalSelection(
getText(mTextView),
- mTextView.getSelectionStart(),
- mTextView.getSelectionEnd(),
+ sortedSelectionIndices[0],
+ sortedSelectionIndices[1],
false /*isLink*/);
cancelAsyncTask();
if (skipTextClassification()) {
@@ -138,12 +165,14 @@ public final class SelectionActionModeHelper {
* Starts Link ActionMode.
*/
public void startLinkActionModeAsync(int start, int end) {
- mSelectionTracker.onOriginalSelection(getText(mTextView), start, end, true /*isLink*/);
+ int[] indexResult = sortSelctionIndices(start, end);
+ mSelectionTracker.onOriginalSelection(getText(mTextView), indexResult[0], indexResult[1],
+ true /*isLink*/);
cancelAsyncTask();
if (skipTextClassification()) {
startLinkActionMode(null);
} else {
- resetTextClassificationHelper(start, end);
+ resetTextClassificationHelper(indexResult[0], indexResult[1]);
mTextClassificationAsyncTask = new TextClassificationAsyncTask(
mTextView,
mTextClassificationHelper.getTimeoutDuration(),
@@ -172,19 +201,23 @@ public final class SelectionActionModeHelper {
/** Reports a selection action event. */
public void onSelectionAction(int menuItemId, @Nullable String actionLabel) {
+ int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
mSelectionTracker.onSelectionAction(
- mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
+ sortedSelectionIndices[0], sortedSelectionIndices[1],
getActionType(menuItemId), actionLabel, mTextClassification);
}
public void onSelectionDrag() {
+ int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
mSelectionTracker.onSelectionAction(
- mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
+ sortedSelectionIndices[0], sortedSelectionIndices[1],
SelectionEvent.ACTION_DRAG, /* actionLabel= */ null, mTextClassification);
}
public void onTextChanged(int start, int end) {
- mSelectionTracker.onTextChanged(start, end, mTextClassification);
+ int[] sortedSelectionIndices = sortSelctionIndices(start, end);
+ mSelectionTracker.onTextChanged(sortedSelectionIndices[0], sortedSelectionIndices[1],
+ mTextClassification);
}
public boolean resetSelection(int textIndex) {
@@ -301,10 +334,10 @@ public final class SelectionActionModeHelper {
startSelectionActionMode(startSelectionResult);
};
// TODO do not trigger the animation if the change included only non-printable characters
+ int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
final boolean didSelectionChange =
- result != null && (mTextView.getSelectionStart() != result.mStart
- || mTextView.getSelectionEnd() != result.mEnd);
-
+ result != null && (sortedSelectionIndices[0] != result.mStart
+ || sortedSelectionIndices[1] != result.mEnd);
if (!didSelectionChange) {
onAnimationEndCallback.run();
return;
@@ -453,16 +486,18 @@ public final class SelectionActionModeHelper {
if (actionMode != null) {
actionMode.invalidate();
}
+ final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
mSelectionTracker.onSelectionUpdated(
- mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mTextClassification);
+ sortedSelectionIndices[0], sortedSelectionIndices[1], mTextClassification);
mTextClassificationAsyncTask = null;
}
private void resetTextClassificationHelper(int selectionStart, int selectionEnd) {
if (selectionStart < 0 || selectionEnd < 0) {
// Use selection indices
- selectionStart = mTextView.getSelectionStart();
- selectionEnd = mTextView.getSelectionEnd();
+ int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ selectionStart = sortedSelectionIndices[0];
+ selectionEnd = sortedSelectionIndices[1];
}
mTextClassificationHelper.init(
mTextView::getTextClassifier,
@@ -602,10 +637,11 @@ public final class SelectionActionModeHelper {
mAllowReset = false;
boolean selected = editor.selectCurrentWord();
if (selected) {
- mSelectionStart = editor.getTextView().getSelectionStart();
- mSelectionEnd = editor.getTextView().getSelectionEnd();
+ final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(textView);
+ mSelectionStart = sortedSelectionIndices[0];
+ mSelectionEnd = sortedSelectionIndices[1];
mLogger.logSelectionAction(
- textView.getSelectionStart(), textView.getSelectionEnd(),
+ sortedSelectionIndices[0], sortedSelectionIndices[1],
SelectionEvent.ACTION_RESET,
/* actionLabel= */ null, /* classification= */ null);
}
@@ -1178,8 +1214,9 @@ public final class SelectionActionModeHelper {
SelectionResult(int start, int end,
@Nullable TextClassification classification, @Nullable TextSelection selection) {
- mStart = start;
- mEnd = end;
+ int[] sortedIndices = sortSelctionIndices(start, end);
+ mStart = sortedIndices[0];
+ mEnd = sortedIndices[1];
mClassification = classification;
mSelection = selection;
}