diff options
| author | Joanne Chung <joannechung@google.com> | 2020-03-23 22:40:14 +0800 |
|---|---|---|
| committer | Joanne Chung <joannechung@google.com> | 2020-04-16 17:37:05 +0800 |
| commit | cb3c97db0cf21df076735d71c573a754fa4ef072 (patch) | |
| tree | 10bfb10a54170b13cfaba75f274f73ed36d059b3 /core/java/android/widget/SelectionActionModeHelper.java | |
| parent | 82dc6147f16edc2106d509f97157bbc0c53a1ea4 (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.java | 73 |
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; } |
