Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6dfbe831 authored by Joanne Chung's avatar Joanne Chung
Browse files

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
Bug: 157452302
Test: Manual. No crash occurs.
Test: atest FrameworksCoreTests:android.widget.TextViewActivityTest
(cherry picked from commit cb3c97db)

Merged-In: I8dbc92f0f31e64b7e3a45ae91762e1b741629a8e
Change-Id: Ie0e2b5840e147f98174cae4521eb777e1a080706
parent 1835a248
Loading
Loading
Loading
Loading
+55 −17
Original line number Original line Diff line number Diff line
@@ -104,17 +104,44 @@ 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.
     * Starts Selection ActionMode.
     */
     */
    public void startSelectionActionModeAsync(boolean adjustSelection) {
    public void startSelectionActionModeAsync(boolean adjustSelection) {
        // Check if the smart selection should run for editable text.
        // Check if the smart selection should run for editable text.
        adjustSelection &= getTextClassificationSettings().isSmartSelectionEnabled();
        adjustSelection &= getTextClassificationSettings().isSmartSelectionEnabled();
        int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);


        mSelectionTracker.onOriginalSelection(
        mSelectionTracker.onOriginalSelection(
                getText(mTextView),
                getText(mTextView),
                mTextView.getSelectionStart(),
                sortedSelectionIndices[0],
                mTextView.getSelectionEnd(),
                sortedSelectionIndices[1],
                false /*isLink*/);
                false /*isLink*/);
        cancelAsyncTask();
        cancelAsyncTask();
        if (skipTextClassification()) {
        if (skipTextClassification()) {
@@ -139,12 +166,14 @@ public final class SelectionActionModeHelper {
     * Starts Link ActionMode.
     * Starts Link ActionMode.
     */
     */
    public void startLinkActionModeAsync(int start, int end) {
    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();
        cancelAsyncTask();
        if (skipTextClassification()) {
        if (skipTextClassification()) {
            startLinkActionMode(null);
            startLinkActionMode(null);
        } else {
        } else {
            resetTextClassificationHelper(start, end);
            resetTextClassificationHelper(indexResult[0], indexResult[1]);
            mTextClassificationAsyncTask = new TextClassificationAsyncTask(
            mTextClassificationAsyncTask = new TextClassificationAsyncTask(
                    mTextView,
                    mTextView,
                    mTextClassificationHelper.getTimeoutDuration(),
                    mTextClassificationHelper.getTimeoutDuration(),
@@ -173,19 +202,23 @@ public final class SelectionActionModeHelper {


    /** Reports a selection action event. */
    /** Reports a selection action event. */
    public void onSelectionAction(int menuItemId, @Nullable String actionLabel) {
    public void onSelectionAction(int menuItemId, @Nullable String actionLabel) {
        int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
        mSelectionTracker.onSelectionAction(
        mSelectionTracker.onSelectionAction(
                mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
                sortedSelectionIndices[0], sortedSelectionIndices[1],
                getActionType(menuItemId), actionLabel, mTextClassification);
                getActionType(menuItemId), actionLabel, mTextClassification);
    }
    }


    public void onSelectionDrag() {
    public void onSelectionDrag() {
        int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
        mSelectionTracker.onSelectionAction(
        mSelectionTracker.onSelectionAction(
                mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
                sortedSelectionIndices[0], sortedSelectionIndices[1],
                SelectionEvent.ACTION_DRAG, /* actionLabel= */ null, mTextClassification);
                SelectionEvent.ACTION_DRAG, /* actionLabel= */ null, mTextClassification);
    }
    }


    public void onTextChanged(int start, int end) {
    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) {
    public boolean resetSelection(int textIndex) {
@@ -302,9 +335,10 @@ public final class SelectionActionModeHelper {
            startSelectionActionMode(startSelectionResult);
            startSelectionActionMode(startSelectionResult);
        };
        };
        // TODO do not trigger the animation if the change included only non-printable characters
        // TODO do not trigger the animation if the change included only non-printable characters
        int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
        final boolean didSelectionChange =
        final boolean didSelectionChange =
                result != null && (mTextView.getSelectionStart() != result.mStart
                result != null && (sortedSelectionIndices[0] != result.mStart
                        || mTextView.getSelectionEnd() != result.mEnd);
                        || sortedSelectionIndices[1] != result.mEnd);


        if (!didSelectionChange) {
        if (!didSelectionChange) {
            onAnimationEndCallback.run();
            onAnimationEndCallback.run();
@@ -454,16 +488,18 @@ public final class SelectionActionModeHelper {
        if (actionMode != null) {
        if (actionMode != null) {
            actionMode.invalidate();
            actionMode.invalidate();
        }
        }
        final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
        mSelectionTracker.onSelectionUpdated(
        mSelectionTracker.onSelectionUpdated(
                mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mTextClassification);
                sortedSelectionIndices[0], sortedSelectionIndices[1], mTextClassification);
        mTextClassificationAsyncTask = null;
        mTextClassificationAsyncTask = null;
    }
    }


    private void resetTextClassificationHelper(int selectionStart, int selectionEnd) {
    private void resetTextClassificationHelper(int selectionStart, int selectionEnd) {
        if (selectionStart < 0 || selectionEnd < 0) {
        if (selectionStart < 0 || selectionEnd < 0) {
            // Use selection indices
            // Use selection indices
            selectionStart = mTextView.getSelectionStart();
            int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
            selectionEnd = mTextView.getSelectionEnd();
            selectionStart = sortedSelectionIndices[0];
            selectionEnd = sortedSelectionIndices[1];
        }
        }
        mTextClassificationHelper.init(
        mTextClassificationHelper.init(
                mTextView::getTextClassifier,
                mTextView::getTextClassifier,
@@ -603,10 +639,11 @@ public final class SelectionActionModeHelper {
                mAllowReset = false;
                mAllowReset = false;
                boolean selected = editor.selectCurrentWord();
                boolean selected = editor.selectCurrentWord();
                if (selected) {
                if (selected) {
                    mSelectionStart = editor.getTextView().getSelectionStart();
                    final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(textView);
                    mSelectionEnd = editor.getTextView().getSelectionEnd();
                    mSelectionStart = sortedSelectionIndices[0];
                    mSelectionEnd = sortedSelectionIndices[1];
                    mLogger.logSelectionAction(
                    mLogger.logSelectionAction(
                            textView.getSelectionStart(), textView.getSelectionEnd(),
                            sortedSelectionIndices[0], sortedSelectionIndices[1],
                            SelectionEvent.ACTION_RESET,
                            SelectionEvent.ACTION_RESET,
                            /* actionLabel= */ null, /* classification= */ null);
                            /* actionLabel= */ null, /* classification= */ null);
                }
                }
@@ -1179,8 +1216,9 @@ public final class SelectionActionModeHelper {


        SelectionResult(int start, int end,
        SelectionResult(int start, int end,
                @Nullable TextClassification classification, @Nullable TextSelection selection) {
                @Nullable TextClassification classification, @Nullable TextSelection selection) {
            mStart = start;
            int[] sortedIndices = sortSelctionIndices(start, end);
            mEnd = end;
            mStart = sortedIndices[0];
            mEnd = sortedIndices[1];
            mClassification = classification;
            mClassification = classification;
            mSelection = selection;
            mSelection = selection;
        }
        }