diff options
| -rw-r--r-- | core/java/android/widget/SpellChecker.java | 90 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 38 |
2 files changed, 80 insertions, 48 deletions
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index ac9535a425d8..772c129a7e64 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -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); - 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(); + createMisspelledSuggestionSpan(suggestionsInfo, mSpellCheckSpans[j]); } - editable.removeSpan(spellCheckSpan); + 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); + 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); + + // 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; + } + } + + 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; + } + } + } + + 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); } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5cd79024f7ad..052cbbc72d2e 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -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; |