Loading core/java/android/widget/SpellChecker.java +66 −24 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 = Loading @@ -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); } } core/java/android/widget/TextView.java +14 −24 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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; Loading Loading
core/java/android/widget/SpellChecker.java +66 −24 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 = Loading @@ -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); } }
core/java/android/widget/TextView.java +14 −24 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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; Loading