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

Commit cb9309d0 authored by satok's avatar satok Committed by Android Git Automerger
Browse files

am 50956b2d: Merge "DO NOT MERGE. Backport If36b8a69e7fa22e837c99d Fix the...

am 50956b2d: Merge "DO NOT MERGE. Backport If36b8a69e7fa22e837c99d Fix the issue that the spell check doesn\'t start when the user changes the cursor position by touch" into jb-dev

* commit '50956b2d':
  DO NOT MERGE. Backport If36b8a69e7fa22e837c99d Fix the issue that the spell check doesn't start when the user changes the cursor position by touch
parents 1e754e8f 50956b2d
Loading
Loading
Loading
Loading
+110 −34
Original line number Diff line number Diff line
@@ -21,10 +21,12 @@ import android.text.Editable;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.WordIterator;
import android.text.style.SpellCheckSpan;
import android.text.style.SuggestionSpan;
import android.util.Log;
import android.util.LruCache;
import android.view.textservice.SentenceSuggestionsInfo;
import android.view.textservice.SpellCheckerSession;
import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
@@ -95,6 +97,10 @@ public class SpellChecker implements SpellCheckerSessionListener {

    private Runnable mSpellRunnable;

    private static final int SUGGESTION_SPAN_CACHE_SIZE = 10;
    private final LruCache<Long, SuggestionSpan> mSuggestionSpanCache =
            new LruCache<Long, SuggestionSpan>(SUGGESTION_SPAN_CACHE_SIZE);

    public SpellChecker(TextView textView) {
        mTextView = textView;

@@ -126,6 +132,7 @@ public class SpellChecker implements SpellCheckerSessionListener {

        // Restore SpellCheckSpans in pool
        for (int i = 0; i < mLength; i++) {
            // Resets id and progress to invalidate spell check span
            mSpellCheckSpans[i].setSpellCheckInProgress(false);
            mIds[i] = -1;
        }
@@ -133,6 +140,7 @@ public class SpellChecker implements SpellCheckerSessionListener {

        // Remove existing misspelled SuggestionSpans
        mTextView.removeMisspelledSpans((Editable) mTextView.getText());
        mSuggestionSpanCache.evictAll();
    }

    private void setLocale(Locale locale) {
@@ -199,6 +207,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
    public void removeSpellCheckSpan(SpellCheckSpan spellCheckSpan) {
        for (int i = 0; i < mLength; i++) {
            if (mSpellCheckSpans[i] == spellCheckSpan) {
                // Resets id and progress to invalidate spell check span
                mSpellCheckSpans[i].setSpellCheckInProgress(false);
                mIds[i] = -1;
                return;
@@ -211,6 +220,9 @@ public class SpellChecker implements SpellCheckerSessionListener {
    }

    public void spellCheck(int start, int end) {
        if (DBG) {
            Log.d(TAG, "Start spell-checking: " + start + ", " + end);
        }
        final Locale locale = mTextView.getTextServicesLocale();
        final boolean isSessionActive = isSessionActive();
        if (mCurrentLocale == null || (!(mCurrentLocale.equals(locale)))) {
@@ -238,6 +250,9 @@ public class SpellChecker implements SpellCheckerSessionListener {
            }
        }

        if (DBG) {
            Log.d(TAG, "new spell parser.");
        }
        // No available parser found in pool, create a new one
        SpellParser[] newSpellParsers = new SpellParser[length + 1];
        System.arraycopy(mSpellParsers, 0, newSpellParsers, 0, length);
@@ -260,13 +275,22 @@ public class SpellChecker implements SpellCheckerSessionListener {

        for (int i = 0; i < mLength; i++) {
            final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i];
            if (spellCheckSpan.isSpellCheckInProgress()) continue;
            if (mIds[i] < 0 || spellCheckSpan.isSpellCheckInProgress()) continue;

            final int start = editable.getSpanStart(spellCheckSpan);
            final int end = editable.getSpanEnd(spellCheckSpan);

            // Do not check this word if the user is currently editing it
            if (start >= 0 && end > start && (selectionEnd < start || selectionStart > end)) {
            final boolean isEditing;
            if (mIsSentenceSpellCheckSupported) {
                // Allow the overlap of the cursor and the first boundary of the spell check span
                // no to skip the spell check of the following word because the
                // following word will never be spell-checked even if the user finishes composing
                isEditing = selectionEnd <= start || selectionStart > end;
            } else {
                isEditing = selectionEnd < start || selectionStart > end;
            }
            if (start >= 0 && end > start && isEditing) {
                final String word = (editable instanceof SpannableStringBuilder) ?
                        ((SpannableStringBuilder) editable).substring(start, end) :
                        editable.subSequence(start, end).toString();
@@ -372,6 +396,9 @@ public class SpellChecker implements SpellCheckerSessionListener {
    }

    private void scheduleNewSpellCheck() {
        if (DBG) {
            Log.i(TAG, "schedule new spell check.");
        }
        if (mSpellRunnable == null) {
            mSpellRunnable = new Runnable() {
                @Override
@@ -423,6 +450,20 @@ public class SpellChecker implements SpellCheckerSessionListener {

        SuggestionSpan suggestionSpan = new SuggestionSpan(mTextView.getContext(), suggestions,
                SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED);
        // TODO: Remove mIsSentenceSpellCheckSupported by extracting an interface
        // to share the logic of word level spell checker and sentence level spell checker
        if (mIsSentenceSpellCheckSupported) {
            final long key = TextUtils.packRangeInLong(start, end);
            final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key);
            if (tempSuggestionSpan != null) {
                if (DBG) {
                    Log.i(TAG, "Cached span on the same position is cleard. "
                            + editable.subSequence(start, end));
                }
                editable.removeSpan(tempSuggestionSpan);
            }
            mSuggestionSpanCache.put(key, suggestionSpan);
        }
        editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        mTextView.invalidateRegion(start, end, false /* No cursor involved */);
@@ -447,10 +488,17 @@ public class SpellChecker implements SpellCheckerSessionListener {
        }

        private void setRangeSpan(Editable editable, int start, int end) {
            if (DBG) {
                Log.d(TAG, "set next range span: " + start + ", " + end);
            }
            editable.setSpan(mRange, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        private void removeRangeSpan(Editable editable) {
            if (DBG) {
                Log.d(TAG, "Remove range span." + editable.getSpanStart(editable)
                        + editable.getSpanEnd(editable));
            }
            editable.removeSpan(mRange);
        }

@@ -484,6 +532,9 @@ public class SpellChecker implements SpellCheckerSessionListener {
                wordEnd = mWordIterator.getEnd(wordStart);
            }
            if (wordEnd == BreakIterator.DONE) {
                if (DBG) {
                    Log.i(TAG, "No more spell check.");
                }
                removeRangeSpan(editable);
                return;
            }
@@ -499,47 +550,72 @@ public class SpellChecker implements SpellCheckerSessionListener {
            boolean scheduleOtherSpellCheck = false;

            if (mIsSentenceSpellCheckSupported) {
                int regionEnd;
                if (wordIteratorWindowEnd < end) {
                    if (DBG) {
                        Log.i(TAG, "schedule other spell check.");
                    }
                    // Several batches needed on that region. Cut after last previous word
                    regionEnd = mWordIterator.preceding(wordIteratorWindowEnd);
                    scheduleOtherSpellCheck = true;
                } else {
                    regionEnd = mWordIterator.preceding(end);
                }
                boolean correct = regionEnd != BreakIterator.DONE;
                int spellCheckEnd = mWordIterator.preceding(wordIteratorWindowEnd);
                boolean correct = spellCheckEnd != BreakIterator.DONE;
                if (correct) {
                    regionEnd = mWordIterator.getEnd(regionEnd);
                    correct = regionEnd != BreakIterator.DONE;
                    spellCheckEnd = mWordIterator.getEnd(spellCheckEnd);
                    correct = spellCheckEnd != BreakIterator.DONE;
                }
                if (!correct) {
                    editable.removeSpan(mRange);
                    return;
                    if (DBG) {
                        Log.i(TAG, "Incorrect range span.");
                    }
                // Stop spell checking when there are no characters in the range.
                if (wordEnd < start) {
                    removeRangeSpan(editable);
                    return;
                }
                do {
                    // TODO: Find the start position of the sentence.
                final int spellCheckStart =  wordStart;
                if (regionEnd <= spellCheckStart) {
                    return;
                    int spellCheckStart = wordStart;
                    boolean createSpellCheckSpan = true;
                    // Cancel or merge overlapped spell check spans
                    for (int i = 0; i < mLength; ++i) {
                        final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i];
                        if (mIds[i] < 0 || spellCheckSpan.isSpellCheckInProgress()) {
                            continue;
                        }
                final int selectionStart = Selection.getSelectionStart(editable);
                final int selectionEnd = Selection.getSelectionEnd(editable);
                        final int spanStart = editable.getSpanStart(spellCheckSpan);
                        final int spanEnd = editable.getSpanEnd(spellCheckSpan);
                        if (spanEnd < spellCheckStart || spellCheckEnd < spanStart) {
                            // No need to merge
                            continue;
                        }
                        if (spanStart <= spellCheckStart && spellCheckEnd <= spanEnd) {
                            // There is a completely overlapped spell check span
                            // skip this span
                            createSpellCheckSpan = false;
                            if (DBG) {
                                Log.i(TAG, "The range is overrapped. Skip spell check.");
                            }
                            break;
                        }
                        removeSpellCheckSpan(spellCheckSpan);
                        spellCheckStart = Math.min(spanStart, spellCheckStart);
                        spellCheckEnd = Math.max(spanEnd, spellCheckEnd);
                    }

                    if (DBG) {
                        Log.d(TAG, "addSpellCheckSpan: "
                            + editable.subSequence(spellCheckStart, regionEnd)
                            + ", regionEnd = " + regionEnd + ", spellCheckStart = "
                            + spellCheckStart + ", sel start = " + selectionStart + ", sel end ="
                            + selectionEnd);
                                + ", End = " + spellCheckEnd + ", Start = " + spellCheckStart
                                + ", next = " + scheduleOtherSpellCheck + "\n"
                                + editable.subSequence(spellCheckStart, spellCheckEnd));
                    }
                // Do not check this word if the user is currently editing it
                if (spellCheckStart >= 0 && regionEnd > spellCheckStart
                        && (selectionEnd < spellCheckStart || selectionStart > regionEnd)) {
                    addSpellCheckSpan(editable, spellCheckStart, regionEnd);

                    // Stop spell checking when there are no characters in the range.
                    if (spellCheckEnd < start) {
                        break;
                    }
                    if (createSpellCheckSpan) {
                        addSpellCheckSpan(editable, spellCheckStart, spellCheckEnd);
                    }
                wordStart = regionEnd;
                } while (false);
                wordStart = spellCheckEnd;
            } else {
                while (wordStart <= end) {
                    if (wordEnd >= start && wordEnd > wordStart) {