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

Commit 6771c7fd authored by Gilles Debunne's avatar Gilles Debunne Committed by Android (Google) Code Review
Browse files

Merge "Bug 5384674: When only one suggestion is returned, it is displayed twice"

parents d3871c53 176cd0d3
Loading
Loading
Loading
Loading
+66 −24
Original line number Diff line number Diff line
@@ -30,8 +30,6 @@ import android.view.textservice.TextServicesManager;

import com.android.internal.util.ArrayUtils;

import java.util.Locale;


/**
 * Helper class for TextView. Bridge between the TextView and the Dictionnary service.
@@ -167,15 +165,12 @@ public class SpellChecker implements SpellCheckerSessionListener {

    @Override
    public void onGetSuggestions(SuggestionsInfo[] results) {
        final Editable editable = (Editable) mTextView.getText();
        for (int i = 0; i < results.length; i++) {
            SuggestionsInfo suggestionsInfo = results[i];
            if (suggestionsInfo.getCookie() != mCookie) continue;
            final int sequenceNumber = suggestionsInfo.getSequence();

            for (int j = 0; j < mLength; j++) {
                final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j];

                if (sequenceNumber == mIds[j]) {
                    final int attributes = suggestionsInfo.getSuggestionsAttributes();
                    boolean isInDictionary =
@@ -184,31 +179,78 @@ public class SpellChecker implements SpellCheckerSessionListener {
                            ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0);

                    if (!isInDictionary && looksLikeTypo) {
                        String[] suggestions = getSuggestions(suggestionsInfo);
                        SuggestionSpan suggestionSpan = new SuggestionSpan(
                                mTextView.getContext(), suggestions,
                                SuggestionSpan.FLAG_EASY_CORRECT |
                                SuggestionSpan.FLAG_MISSPELLED);
                        createMisspelledSuggestionSpan(suggestionsInfo, mSpellCheckSpans[j]);
                    }
                    break;
                }
            }
        }
    }

    private void createMisspelledSuggestionSpan(SuggestionsInfo suggestionsInfo,
            SpellCheckSpan spellCheckSpan) {
        final Editable editable = (Editable) mTextView.getText();
        final int start = editable.getSpanStart(spellCheckSpan);
        final int end = editable.getSpanEnd(spellCheckSpan);
                        editable.setSpan(suggestionSpan, start, end,
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                        // TODO limit to the word rectangle region
                        mTextView.invalidate();

        // Other suggestion spans may exist on that region, with identical suggestions, filter
        // them out to avoid duplicates. First, filter suggestion spans on that exact region.
        SuggestionSpan[] suggestionSpans = editable.getSpans(start, end, SuggestionSpan.class);
        final int length = suggestionSpans.length;
        for (int i = 0; i < length; i++) {
            final int spanStart = editable.getSpanStart(suggestionSpans[i]);
            final int spanEnd = editable.getSpanEnd(suggestionSpans[i]);
            if (spanStart != start || spanEnd != end) {
                suggestionSpans[i] = null;
                break;
            }
                    editable.removeSpan(spellCheckSpan);
        }

        final int suggestionsCount = suggestionsInfo.getSuggestionsCount();
        String[] suggestions;
        if (suggestionsCount <= 0) {
            // A negative suggestion count is possible
            suggestions = ArrayUtils.emptyArray(String.class);
        } else {
            int numberOfSuggestions = 0;
            suggestions = new String[suggestionsCount];

            for (int i = 0; i < suggestionsCount; i++) {
                final String spellSuggestion = suggestionsInfo.getSuggestionAt(i);
                if (spellSuggestion == null) break;
                boolean suggestionFound = false;

                for (int j = 0; j < length && !suggestionFound; j++) {
                    if (suggestionSpans[j] == null) break;

                    String[] suggests = suggestionSpans[j].getSuggestions();
                    for (int k = 0; k < suggests.length; k++) {
                        if (spellSuggestion.equals(suggests[k])) {
                            // The suggestion is already provided by an other SuggestionSpan
                            suggestionFound = true;
                            break;
                        }
                    }
                }

    private static String[] getSuggestions(SuggestionsInfo suggestionsInfo) {
        // A negative suggestion count is possible
        final int len = Math.max(0, suggestionsInfo.getSuggestionsCount());
        String[] suggestions = new String[len];
        for (int j = 0; j < len; j++) {
            suggestions[j] = suggestionsInfo.getSuggestionAt(j);
                if (!suggestionFound) {
                    suggestions[numberOfSuggestions++] = spellSuggestion;
                }
            }

            if (numberOfSuggestions != suggestionsCount) {
                String[] newSuggestions = new String[numberOfSuggestions];
                System.arraycopy(suggestions, 0, newSuggestions, 0, numberOfSuggestions);
                suggestions = newSuggestions;
            }
        return suggestions;
        }

        SuggestionSpan suggestionSpan = new SuggestionSpan(mTextView.getContext(), suggestions,
                SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED);
        editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        // TODO limit to the word rectangle region
        mTextView.invalidate();
        editable.removeSpan(spellCheckSpan);
    }
}
+14 −24
Original line number Diff line number Diff line
@@ -353,6 +353,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    // Set when this TextView gained focus with some text selected. Will start selection mode.
    private boolean mCreatedWithASelection = false;

    // Size of the window for the word iterator, should be greater than the longest word's length
    private static final int WORD_ITERATOR_WINDOW_WIDTH = 50;
    private WordIterator mWordIterator;

    private SpellChecker mSpellChecker;
@@ -2937,11 +2939,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

                Spannable sp = new SpannableString(mText);

                for (ChangeWatcher cw :
                     sp.getSpans(0, sp.length(), ChangeWatcher.class)) {
                for (ChangeWatcher cw : sp.getSpans(0, sp.length(), ChangeWatcher.class)) {
                    sp.removeSpan(cw);
                }

                SuggestionSpan[] suggestionSpans = sp.getSpans(0, sp.length(), SuggestionSpan.class);
                for (int i = 0; i < suggestionSpans.length; i++) {
                    int flags = suggestionSpans[i].getFlags();
                    if ((flags & SuggestionSpan.FLAG_EASY_CORRECT) != 0
                            && (flags & SuggestionSpan.FLAG_MISSPELLED) != 0) {
                        sp.removeSpan(suggestionSpans[i]);
                    }
                }

                sp.removeSpan(mSuggestionRangeSpan);

                ss.text = sp;
@@ -4449,7 +4459,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

        if (mSpellChecker != null) {
            mSpellChecker.closeSession();
            removeMisspelledSpans();
            // Forces the creation of a new SpellChecker next time this window is created.
            // Will handle the cases where the settings has been changed in the meantime.
            mSpellChecker = null;
@@ -8461,24 +8470,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        }
    }

    /**
     * Removes the suggestion spans for misspelled words.
     */
    private void removeMisspelledSpans() {
        if (mText instanceof Spannable) {
            Spannable spannable = (Spannable) mText;
            SuggestionSpan[] suggestionSpans = spannable.getSpans(0,
                    spannable.length(), SuggestionSpan.class);
            for (int i = 0; i < suggestionSpans.length; i++) {
                int flags = suggestionSpans[i].getFlags();
                if ((flags & SuggestionSpan.FLAG_EASY_CORRECT) != 0
                        && (flags & SuggestionSpan.FLAG_MISSPELLED) != 0) {
                    spannable.removeSpan(suggestionSpans[i]);
                }
            }
        }
    }

    @Override
    public boolean onGenericMotionEvent(MotionEvent event) {
        if (mMovement != null && mText instanceof Spannable && mLayout != null) {
@@ -8959,9 +8950,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            mWordIterator = new WordIterator();
        }

        final int TEXT_WINDOW_WIDTH = 50; // Should be larger than the longest word's length
        final int windowStart = Math.max(0, start - TEXT_WINDOW_WIDTH);
        final int windowEnd = Math.min(mText.length(), end + TEXT_WINDOW_WIDTH);
        final int windowStart = Math.max(0, start - WORD_ITERATOR_WINDOW_WIDTH);
        final int windowEnd = Math.min(mText.length(), end + WORD_ITERATOR_WINDOW_WIDTH);
        mWordIterator.setCharSequence(mText.subSequence(windowStart, windowEnd));

        return windowStart;