Loading core/java/android/text/SpannableStringBuilder.java +5 −6 Original line number Diff line number Diff line Loading @@ -710,18 +710,17 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable for (int i = 0; i < spanCount; i++) { int spanStart = starts[i]; int spanEnd = ends[i]; if (spanStart > gapstart) { spanStart -= gaplen; } if (spanEnd > gapstart) { spanEnd -= gaplen; } if (spanStart > queryEnd) { continue; } int spanEnd = ends[i]; if (spanEnd > gapstart) { spanEnd -= gaplen; } if (spanEnd < queryStart) { continue; } Loading core/java/android/text/style/SpellCheckSpan.java +2 −2 Original line number Diff line number Diff line Loading @@ -39,8 +39,8 @@ public class SpellCheckSpan implements ParcelableSpan { mSpellCheckInProgress = (src.readInt() != 0); } public void setSpellCheckInProgress() { mSpellCheckInProgress = true; public void setSpellCheckInProgress(boolean inProgress) { mSpellCheckInProgress = inProgress; } public boolean isSpellCheckInProgress() { Loading core/java/android/widget/SpellChecker.java +34 −76 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import android.text.Selection; import android.text.Spanned; import android.text.style.SpellCheckSpan; import android.text.style.SuggestionSpan; import android.util.Log; import android.view.textservice.SpellCheckerSession; import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener; import android.view.textservice.SuggestionsInfo; Loading @@ -40,23 +39,21 @@ import java.util.Locale; * @hide */ public class SpellChecker implements SpellCheckerSessionListener { private static final String LOG_TAG = "SpellChecker"; private static final boolean DEBUG_SPELL_CHECK = false; private static final int DELAY_BEFORE_SPELL_CHECK = 400; // milliseconds private final TextView mTextView; final SpellCheckerSession mSpellCheckerSession; final int mCookie; // Paired arrays for the (id, spellCheckSpan) pair. mIndex is the next available position // Paired arrays for the (id, spellCheckSpan) pair. A negative id means the associated // SpellCheckSpan has been recycled and can be-reused. // May contain null SpellCheckSpans after a given index. private int[] mIds; private SpellCheckSpan[] mSpellCheckSpans; // The actual current number of used slots in the above arrays // The mLength first elements of the above arrays have been initialized private int mLength; private int mSpanSequenceCounter = 0; private Runnable mChecker; public SpellChecker(TextView textView) { mTextView = textView; Loading @@ -69,7 +66,7 @@ public class SpellChecker implements SpellCheckerSessionListener { mCookie = hashCode(); // Arbitrary: 4 simultaneous spell check spans. Will automatically double size on demand final int size = ArrayUtils.idealObjectArraySize(4); final int size = ArrayUtils.idealObjectArraySize(1); mIds = new int[size]; mSpellCheckSpans = new SpellCheckSpan[size]; mLength = 0; Loading @@ -89,73 +86,50 @@ public class SpellChecker implements SpellCheckerSessionListener { } } public void addSpellCheckSpan(SpellCheckSpan spellCheckSpan) { int length = mIds.length; if (mLength >= length) { final int newSize = length * 2; private int nextSpellCheckSpanIndex() { for (int i = 0; i < mLength; i++) { if (mIds[i] < 0) return i; } if (mLength == mSpellCheckSpans.length) { final int newSize = mLength * 2; int[] newIds = new int[newSize]; SpellCheckSpan[] newSpellCheckSpans = new SpellCheckSpan[newSize]; System.arraycopy(mIds, 0, newIds, 0, length); System.arraycopy(mSpellCheckSpans, 0, newSpellCheckSpans, 0, length); System.arraycopy(mIds, 0, newIds, 0, mLength); System.arraycopy(mSpellCheckSpans, 0, newSpellCheckSpans, 0, mLength); mIds = newIds; mSpellCheckSpans = newSpellCheckSpans; } mIds[mLength] = mSpanSequenceCounter++; mSpellCheckSpans[mLength] = spellCheckSpan; mSpellCheckSpans[mLength] = new SpellCheckSpan(); mLength++; if (DEBUG_SPELL_CHECK) { final Editable mText = (Editable) mTextView.getText(); int start = mText.getSpanStart(spellCheckSpan); int end = mText.getSpanEnd(spellCheckSpan); if (start >= 0 && end >= 0) { Log.d(LOG_TAG, "Schedule check " + mText.subSequence(start, end)); } else { Log.d(LOG_TAG, "Schedule check EMPTY!"); } return mLength - 1; } scheduleSpellCheck(); public void addSpellCheckSpan(int wordStart, int wordEnd) { final int index = nextSpellCheckSpanIndex(); ((Editable) mTextView.getText()).setSpan(mSpellCheckSpans[index], wordStart, wordEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mIds[index] = mSpanSequenceCounter++; } public void removeSpellCheckSpan(SpellCheckSpan spellCheckSpan) { for (int i = 0; i < mLength; i++) { if (mSpellCheckSpans[i] == spellCheckSpan) { removeAtIndex(i); mSpellCheckSpans[i].setSpellCheckInProgress(false); mIds[i] = -1; return; } } } private void removeAtIndex(int i) { System.arraycopy(mIds, i + 1, mIds, i, mLength - i - 1); System.arraycopy(mSpellCheckSpans, i + 1, mSpellCheckSpans, i, mLength - i - 1); mLength--; } public void onSelectionChanged() { scheduleSpellCheck(); spellCheck(); } private void scheduleSpellCheck() { if (mLength == 0) return; public void spellCheck() { if (mSpellCheckerSession == null) return; if (mChecker != null) { mTextView.removeCallbacks(mChecker); } if (mChecker == null) { mChecker = new Runnable() { public void run() { spellCheck(); } }; } mTextView.postDelayed(mChecker, DELAY_BEFORE_SPELL_CHECK); } private void spellCheck() { final Editable editable = (Editable) mTextView.getText(); final int selectionStart = Selection.getSelectionStart(editable); final int selectionEnd = Selection.getSelectionEnd(editable); Loading @@ -164,8 +138,7 @@ public class SpellChecker implements SpellCheckerSessionListener { int textInfosCount = 0; for (int i = 0; i < mLength; i++) { SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i]; final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i]; if (spellCheckSpan.isSpellCheckInProgress()) continue; final int start = editable.getSpanStart(spellCheckSpan); Loading @@ -174,7 +147,7 @@ public class SpellChecker implements SpellCheckerSessionListener { // Do not check this word if the user is currently editing it if (start >= 0 && end > start && (selectionEnd < start || selectionStart > end)) { final String word = editable.subSequence(start, end).toString(); spellCheckSpan.setSpellCheckInProgress(); spellCheckSpan.setSpellCheckInProgress(true); textInfos[textInfosCount++] = new TextInfo(word, mCookie, mIds[i]); } } Loading @@ -196,27 +169,18 @@ public class SpellChecker implements SpellCheckerSessionListener { for (int i = 0; i < results.length; i++) { SuggestionsInfo suggestionsInfo = results[i]; if (suggestionsInfo.getCookie() != mCookie) continue; final int sequenceNumber = suggestionsInfo.getSequence(); // Starting from the end, to limit the number of array copy while removing for (int j = mLength - 1; j >= 0; j--) { for (int j = 0; j < mLength; j++) { final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j]; if (sequenceNumber == mIds[j]) { SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j]; final int attributes = suggestionsInfo.getSuggestionsAttributes(); boolean isInDictionary = ((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0); boolean looksLikeTypo = ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0); if (DEBUG_SPELL_CHECK) { final int start = editable.getSpanStart(spellCheckSpan); final int end = editable.getSpanEnd(spellCheckSpan); Log.d(LOG_TAG, "Result sequence=" + suggestionsInfo.getSequence() + " " + editable.subSequence(start, end) + "\t" + (isInDictionary?"IN_DICT" : "NOT_DICT") + "\t" + (looksLikeTypo?"TYPO" : "NOT_TYPO")); } if (!isInDictionary && looksLikeTypo) { String[] suggestions = getSuggestions(suggestionsInfo); if (suggestions.length > 0) { Loading @@ -230,13 +194,6 @@ public class SpellChecker implements SpellCheckerSessionListener { Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // TODO limit to the word rectangle region mTextView.invalidate(); if (DEBUG_SPELL_CHECK) { String suggestionsString = ""; for (String s : suggestions) { suggestionsString += s + "|"; } Log.d(LOG_TAG, " Suggestions for " + sequenceNumber + " " + editable.subSequence(start, end)+ " " + suggestionsString); } } } editable.removeSpan(spellCheckSpan); Loading @@ -246,9 +203,10 @@ public class SpellChecker implements SpellCheckerSessionListener { } 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) { for (int j = 0; j < len; j++) { suggestions[j] = suggestionsInfo.getSuggestionAt(j); } return suggestions; Loading core/java/android/widget/TextView.java +49 −32 Original line number Diff line number Diff line Loading @@ -7492,9 +7492,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ protected void onSelectionChanged(int selStart, int selEnd) { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED); if (mSpellChecker != null) { mSpellChecker.onSelectionChanged(); } } /** Loading Loading @@ -7553,6 +7550,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener for (int i = 0; i < length; i++) { final int s = text.getSpanStart(spans[i]); final int e = text.getSpanEnd(spans[i]); // Spans that are adjacent to the edited region will be handled in // updateSpellCheckSpans. Result depends on what will be added (space or text) if (e == start || s == end) break; text.removeSpan(spans[i]); } Loading Loading @@ -7735,12 +7734,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } if (what instanceof SpellCheckSpan) { if (newStart < 0) { if (newStart < 0 && what instanceof SpellCheckSpan) { getSpellChecker().removeSpellCheckSpan((SpellCheckSpan) what); } else if (oldStart < 0) { getSpellChecker().addSpellCheckSpan((SpellCheckSpan) what); } } } Loading @@ -7750,8 +7745,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void updateSpellCheckSpans(int start, int end) { if (!isTextEditable() || !isSuggestionsEnabled() || !getSpellChecker().isSessionActive()) return; Editable text = (Editable) mText; Editable text = (Editable) mText; WordIterator wordIterator = getWordIterator(); wordIterator.setCharSequence(text); Loading @@ -7770,57 +7765,75 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } // We need to expand by one character because we want to include the spans that end/start // at position start/end respectively. SpellCheckSpan[] spellCheckSpans = text.getSpans(start - 1, end + 1, SpellCheckSpan.class); SuggestionSpan[] suggestionSpans = text.getSpans(start - 1, end + 1, SuggestionSpan.class); final int numberOfSpellCheckSpans = spellCheckSpans.length; // Iterate over the newly added text and schedule new SpellCheckSpans while (wordStart <= end) { if (wordEnd >= start) { // A word across the interval boundaries must remove boundary edition spans // A new word has been created across the interval boundaries. Remove previous spans if (wordStart < start && wordEnd > start) { removeEditionSpansAt(start, text); removeSpansAt(start, spellCheckSpans, text); removeSpansAt(start, suggestionSpans, text); } if (wordStart < end && wordEnd > end) { removeEditionSpansAt(end, text); removeSpansAt(end, spellCheckSpans, text); removeSpansAt(end, suggestionSpans, text); } // Do not create new boundary spans if they already exist boolean createSpellCheckSpan = true; if (wordEnd == start) { SpellCheckSpan[] spellCheckSpans = text.getSpans(start, start, SpellCheckSpan.class); if (spellCheckSpans.length > 0) createSpellCheckSpan = false; for (int i = 0; i < numberOfSpellCheckSpans; i++) { final int spanEnd = text.getSpanEnd(spellCheckSpans[i]); if (spanEnd == start) { createSpellCheckSpan = false; break; } } } if (wordStart == end) { SpellCheckSpan[] spellCheckSpans = text.getSpans(end, end, SpellCheckSpan.class); if (spellCheckSpans.length > 0) createSpellCheckSpan = false; for (int i = 0; i < numberOfSpellCheckSpans; i++) { final int spanStart = text.getSpanEnd(spellCheckSpans[i]); if (spanStart == end) { createSpellCheckSpan = false; break; } } } if (createSpellCheckSpan) { text.setSpan(new SpellCheckSpan(), wordStart, wordEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mSpellChecker.addSpellCheckSpan(wordStart, wordEnd); } } // iterate word by word wordEnd = wordIterator.following(wordEnd); if (wordEnd == BreakIterator.DONE) return; if (wordEnd == BreakIterator.DONE) break; wordStart = wordIterator.getBeginning(wordEnd); if (wordStart == BreakIterator.DONE) { Log.e(LOG_TAG, "Unable to find word beginning from " + wordEnd + "in " + mText); return; } break; } } private static void removeEditionSpansAt(int offset, Editable text) { SuggestionSpan[] suggestionSpans = text.getSpans(offset, offset, SuggestionSpan.class); for (int i = 0; i < suggestionSpans.length; i++) { text.removeSpan(suggestionSpans[i]); mSpellChecker.spellCheck(); } SpellCheckSpan[] spellCheckSpans = text.getSpans(offset, offset, SpellCheckSpan.class); for (int i = 0; i < spellCheckSpans.length; i++) { text.removeSpan(spellCheckSpans[i]); private static <T> void removeSpansAt(int offset, T[] spans, Editable text) { final int length = spans.length; for (int i = 0; i < length; i++) { final T span = spans[i]; final int start = text.getSpanStart(span); if (start > offset) continue; final int end = text.getSpanEnd(span); if (end < offset) continue; text.removeSpan(span); } } Loading Loading @@ -8381,6 +8394,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean selectAllGotFocus = mSelectAllOnFocus && didTouchFocusSelect(); hideControllers(); if (!selectAllGotFocus && mText.length() > 0) { if (mSpellChecker != null) { // When the cursor moves, the word that was typed may need spell check mSpellChecker.onSelectionChanged(); } if (isCursorInsideEasyCorrectionSpan()) { showSuggestions(); } else if (hasInsertionController()) { Loading Loading
core/java/android/text/SpannableStringBuilder.java +5 −6 Original line number Diff line number Diff line Loading @@ -710,18 +710,17 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable for (int i = 0; i < spanCount; i++) { int spanStart = starts[i]; int spanEnd = ends[i]; if (spanStart > gapstart) { spanStart -= gaplen; } if (spanEnd > gapstart) { spanEnd -= gaplen; } if (spanStart > queryEnd) { continue; } int spanEnd = ends[i]; if (spanEnd > gapstart) { spanEnd -= gaplen; } if (spanEnd < queryStart) { continue; } Loading
core/java/android/text/style/SpellCheckSpan.java +2 −2 Original line number Diff line number Diff line Loading @@ -39,8 +39,8 @@ public class SpellCheckSpan implements ParcelableSpan { mSpellCheckInProgress = (src.readInt() != 0); } public void setSpellCheckInProgress() { mSpellCheckInProgress = true; public void setSpellCheckInProgress(boolean inProgress) { mSpellCheckInProgress = inProgress; } public boolean isSpellCheckInProgress() { Loading
core/java/android/widget/SpellChecker.java +34 −76 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import android.text.Selection; import android.text.Spanned; import android.text.style.SpellCheckSpan; import android.text.style.SuggestionSpan; import android.util.Log; import android.view.textservice.SpellCheckerSession; import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener; import android.view.textservice.SuggestionsInfo; Loading @@ -40,23 +39,21 @@ import java.util.Locale; * @hide */ public class SpellChecker implements SpellCheckerSessionListener { private static final String LOG_TAG = "SpellChecker"; private static final boolean DEBUG_SPELL_CHECK = false; private static final int DELAY_BEFORE_SPELL_CHECK = 400; // milliseconds private final TextView mTextView; final SpellCheckerSession mSpellCheckerSession; final int mCookie; // Paired arrays for the (id, spellCheckSpan) pair. mIndex is the next available position // Paired arrays for the (id, spellCheckSpan) pair. A negative id means the associated // SpellCheckSpan has been recycled and can be-reused. // May contain null SpellCheckSpans after a given index. private int[] mIds; private SpellCheckSpan[] mSpellCheckSpans; // The actual current number of used slots in the above arrays // The mLength first elements of the above arrays have been initialized private int mLength; private int mSpanSequenceCounter = 0; private Runnable mChecker; public SpellChecker(TextView textView) { mTextView = textView; Loading @@ -69,7 +66,7 @@ public class SpellChecker implements SpellCheckerSessionListener { mCookie = hashCode(); // Arbitrary: 4 simultaneous spell check spans. Will automatically double size on demand final int size = ArrayUtils.idealObjectArraySize(4); final int size = ArrayUtils.idealObjectArraySize(1); mIds = new int[size]; mSpellCheckSpans = new SpellCheckSpan[size]; mLength = 0; Loading @@ -89,73 +86,50 @@ public class SpellChecker implements SpellCheckerSessionListener { } } public void addSpellCheckSpan(SpellCheckSpan spellCheckSpan) { int length = mIds.length; if (mLength >= length) { final int newSize = length * 2; private int nextSpellCheckSpanIndex() { for (int i = 0; i < mLength; i++) { if (mIds[i] < 0) return i; } if (mLength == mSpellCheckSpans.length) { final int newSize = mLength * 2; int[] newIds = new int[newSize]; SpellCheckSpan[] newSpellCheckSpans = new SpellCheckSpan[newSize]; System.arraycopy(mIds, 0, newIds, 0, length); System.arraycopy(mSpellCheckSpans, 0, newSpellCheckSpans, 0, length); System.arraycopy(mIds, 0, newIds, 0, mLength); System.arraycopy(mSpellCheckSpans, 0, newSpellCheckSpans, 0, mLength); mIds = newIds; mSpellCheckSpans = newSpellCheckSpans; } mIds[mLength] = mSpanSequenceCounter++; mSpellCheckSpans[mLength] = spellCheckSpan; mSpellCheckSpans[mLength] = new SpellCheckSpan(); mLength++; if (DEBUG_SPELL_CHECK) { final Editable mText = (Editable) mTextView.getText(); int start = mText.getSpanStart(spellCheckSpan); int end = mText.getSpanEnd(spellCheckSpan); if (start >= 0 && end >= 0) { Log.d(LOG_TAG, "Schedule check " + mText.subSequence(start, end)); } else { Log.d(LOG_TAG, "Schedule check EMPTY!"); } return mLength - 1; } scheduleSpellCheck(); public void addSpellCheckSpan(int wordStart, int wordEnd) { final int index = nextSpellCheckSpanIndex(); ((Editable) mTextView.getText()).setSpan(mSpellCheckSpans[index], wordStart, wordEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mIds[index] = mSpanSequenceCounter++; } public void removeSpellCheckSpan(SpellCheckSpan spellCheckSpan) { for (int i = 0; i < mLength; i++) { if (mSpellCheckSpans[i] == spellCheckSpan) { removeAtIndex(i); mSpellCheckSpans[i].setSpellCheckInProgress(false); mIds[i] = -1; return; } } } private void removeAtIndex(int i) { System.arraycopy(mIds, i + 1, mIds, i, mLength - i - 1); System.arraycopy(mSpellCheckSpans, i + 1, mSpellCheckSpans, i, mLength - i - 1); mLength--; } public void onSelectionChanged() { scheduleSpellCheck(); spellCheck(); } private void scheduleSpellCheck() { if (mLength == 0) return; public void spellCheck() { if (mSpellCheckerSession == null) return; if (mChecker != null) { mTextView.removeCallbacks(mChecker); } if (mChecker == null) { mChecker = new Runnable() { public void run() { spellCheck(); } }; } mTextView.postDelayed(mChecker, DELAY_BEFORE_SPELL_CHECK); } private void spellCheck() { final Editable editable = (Editable) mTextView.getText(); final int selectionStart = Selection.getSelectionStart(editable); final int selectionEnd = Selection.getSelectionEnd(editable); Loading @@ -164,8 +138,7 @@ public class SpellChecker implements SpellCheckerSessionListener { int textInfosCount = 0; for (int i = 0; i < mLength; i++) { SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i]; final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i]; if (spellCheckSpan.isSpellCheckInProgress()) continue; final int start = editable.getSpanStart(spellCheckSpan); Loading @@ -174,7 +147,7 @@ public class SpellChecker implements SpellCheckerSessionListener { // Do not check this word if the user is currently editing it if (start >= 0 && end > start && (selectionEnd < start || selectionStart > end)) { final String word = editable.subSequence(start, end).toString(); spellCheckSpan.setSpellCheckInProgress(); spellCheckSpan.setSpellCheckInProgress(true); textInfos[textInfosCount++] = new TextInfo(word, mCookie, mIds[i]); } } Loading @@ -196,27 +169,18 @@ public class SpellChecker implements SpellCheckerSessionListener { for (int i = 0; i < results.length; i++) { SuggestionsInfo suggestionsInfo = results[i]; if (suggestionsInfo.getCookie() != mCookie) continue; final int sequenceNumber = suggestionsInfo.getSequence(); // Starting from the end, to limit the number of array copy while removing for (int j = mLength - 1; j >= 0; j--) { for (int j = 0; j < mLength; j++) { final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j]; if (sequenceNumber == mIds[j]) { SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j]; final int attributes = suggestionsInfo.getSuggestionsAttributes(); boolean isInDictionary = ((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0); boolean looksLikeTypo = ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0); if (DEBUG_SPELL_CHECK) { final int start = editable.getSpanStart(spellCheckSpan); final int end = editable.getSpanEnd(spellCheckSpan); Log.d(LOG_TAG, "Result sequence=" + suggestionsInfo.getSequence() + " " + editable.subSequence(start, end) + "\t" + (isInDictionary?"IN_DICT" : "NOT_DICT") + "\t" + (looksLikeTypo?"TYPO" : "NOT_TYPO")); } if (!isInDictionary && looksLikeTypo) { String[] suggestions = getSuggestions(suggestionsInfo); if (suggestions.length > 0) { Loading @@ -230,13 +194,6 @@ public class SpellChecker implements SpellCheckerSessionListener { Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // TODO limit to the word rectangle region mTextView.invalidate(); if (DEBUG_SPELL_CHECK) { String suggestionsString = ""; for (String s : suggestions) { suggestionsString += s + "|"; } Log.d(LOG_TAG, " Suggestions for " + sequenceNumber + " " + editable.subSequence(start, end)+ " " + suggestionsString); } } } editable.removeSpan(spellCheckSpan); Loading @@ -246,9 +203,10 @@ public class SpellChecker implements SpellCheckerSessionListener { } 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) { for (int j = 0; j < len; j++) { suggestions[j] = suggestionsInfo.getSuggestionAt(j); } return suggestions; Loading
core/java/android/widget/TextView.java +49 −32 Original line number Diff line number Diff line Loading @@ -7492,9 +7492,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ protected void onSelectionChanged(int selStart, int selEnd) { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED); if (mSpellChecker != null) { mSpellChecker.onSelectionChanged(); } } /** Loading Loading @@ -7553,6 +7550,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener for (int i = 0; i < length; i++) { final int s = text.getSpanStart(spans[i]); final int e = text.getSpanEnd(spans[i]); // Spans that are adjacent to the edited region will be handled in // updateSpellCheckSpans. Result depends on what will be added (space or text) if (e == start || s == end) break; text.removeSpan(spans[i]); } Loading Loading @@ -7735,12 +7734,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } if (what instanceof SpellCheckSpan) { if (newStart < 0) { if (newStart < 0 && what instanceof SpellCheckSpan) { getSpellChecker().removeSpellCheckSpan((SpellCheckSpan) what); } else if (oldStart < 0) { getSpellChecker().addSpellCheckSpan((SpellCheckSpan) what); } } } Loading @@ -7750,8 +7745,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void updateSpellCheckSpans(int start, int end) { if (!isTextEditable() || !isSuggestionsEnabled() || !getSpellChecker().isSessionActive()) return; Editable text = (Editable) mText; Editable text = (Editable) mText; WordIterator wordIterator = getWordIterator(); wordIterator.setCharSequence(text); Loading @@ -7770,57 +7765,75 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } // We need to expand by one character because we want to include the spans that end/start // at position start/end respectively. SpellCheckSpan[] spellCheckSpans = text.getSpans(start - 1, end + 1, SpellCheckSpan.class); SuggestionSpan[] suggestionSpans = text.getSpans(start - 1, end + 1, SuggestionSpan.class); final int numberOfSpellCheckSpans = spellCheckSpans.length; // Iterate over the newly added text and schedule new SpellCheckSpans while (wordStart <= end) { if (wordEnd >= start) { // A word across the interval boundaries must remove boundary edition spans // A new word has been created across the interval boundaries. Remove previous spans if (wordStart < start && wordEnd > start) { removeEditionSpansAt(start, text); removeSpansAt(start, spellCheckSpans, text); removeSpansAt(start, suggestionSpans, text); } if (wordStart < end && wordEnd > end) { removeEditionSpansAt(end, text); removeSpansAt(end, spellCheckSpans, text); removeSpansAt(end, suggestionSpans, text); } // Do not create new boundary spans if they already exist boolean createSpellCheckSpan = true; if (wordEnd == start) { SpellCheckSpan[] spellCheckSpans = text.getSpans(start, start, SpellCheckSpan.class); if (spellCheckSpans.length > 0) createSpellCheckSpan = false; for (int i = 0; i < numberOfSpellCheckSpans; i++) { final int spanEnd = text.getSpanEnd(spellCheckSpans[i]); if (spanEnd == start) { createSpellCheckSpan = false; break; } } } if (wordStart == end) { SpellCheckSpan[] spellCheckSpans = text.getSpans(end, end, SpellCheckSpan.class); if (spellCheckSpans.length > 0) createSpellCheckSpan = false; for (int i = 0; i < numberOfSpellCheckSpans; i++) { final int spanStart = text.getSpanEnd(spellCheckSpans[i]); if (spanStart == end) { createSpellCheckSpan = false; break; } } } if (createSpellCheckSpan) { text.setSpan(new SpellCheckSpan(), wordStart, wordEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mSpellChecker.addSpellCheckSpan(wordStart, wordEnd); } } // iterate word by word wordEnd = wordIterator.following(wordEnd); if (wordEnd == BreakIterator.DONE) return; if (wordEnd == BreakIterator.DONE) break; wordStart = wordIterator.getBeginning(wordEnd); if (wordStart == BreakIterator.DONE) { Log.e(LOG_TAG, "Unable to find word beginning from " + wordEnd + "in " + mText); return; } break; } } private static void removeEditionSpansAt(int offset, Editable text) { SuggestionSpan[] suggestionSpans = text.getSpans(offset, offset, SuggestionSpan.class); for (int i = 0; i < suggestionSpans.length; i++) { text.removeSpan(suggestionSpans[i]); mSpellChecker.spellCheck(); } SpellCheckSpan[] spellCheckSpans = text.getSpans(offset, offset, SpellCheckSpan.class); for (int i = 0; i < spellCheckSpans.length; i++) { text.removeSpan(spellCheckSpans[i]); private static <T> void removeSpansAt(int offset, T[] spans, Editable text) { final int length = spans.length; for (int i = 0; i < length; i++) { final T span = spans[i]; final int start = text.getSpanStart(span); if (start > offset) continue; final int end = text.getSpanEnd(span); if (end < offset) continue; text.removeSpan(span); } } Loading Loading @@ -8381,6 +8394,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean selectAllGotFocus = mSelectAllOnFocus && didTouchFocusSelect(); hideControllers(); if (!selectAllGotFocus && mText.length() > 0) { if (mSpellChecker != null) { // When the cursor moves, the word that was typed may need spell check mSpellChecker.onSelectionChanged(); } if (isCursorInsideEasyCorrectionSpan()) { showSuggestions(); } else if (hasInsertionController()) { Loading