SpellChecker.java revision d6e3494421dff2a091f1011e5266b280b2109843
16435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne// Copyright 2011 Google Inc. All Rights Reserved. 26435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 36435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunnepackage android.widget; 46435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 56435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport android.content.Context; 66435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport android.text.Editable; 76435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport android.text.Selection; 86435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport android.text.Spanned; 96435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport android.text.style.SpellCheckSpan; 106435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport android.text.style.SuggestionSpan; 116435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport android.util.Log; 126435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport android.view.textservice.SpellCheckerSession; 136435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener; 146435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport android.view.textservice.SuggestionsInfo; 156435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport android.view.textservice.TextInfo; 166435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport android.view.textservice.TextServicesManager; 176435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 186435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport com.android.internal.util.ArrayUtils; 196435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 206435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunneimport java.util.Locale; 216435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 226435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 236435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne/** 246435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne * Helper class for TextView. Bridge between the TextView and the Dictionnary service. 256435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne * 266435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne * @hide 276435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne */ 286435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunnepublic class SpellChecker implements SpellCheckerSessionListener { 296435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private static final String LOG_TAG = "SpellChecker"; 306435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private static final boolean DEBUG_SPELL_CHECK = false; 316435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private static final int DELAY_BEFORE_SPELL_CHECK = 400; // milliseconds 326435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 336435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private final TextView mTextView; 346435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 356435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final SpellCheckerSession spellCheckerSession; 366435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int mCookie; 376435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 386435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne // Paired arrays for the (id, spellCheckSpan) pair. mIndex is the next available position 396435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private int[] mIds; 406435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private SpellCheckSpan[] mSpellCheckSpans; 416435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne // The actual current number of used slots in the above arrays 426435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private int mLength; 436435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 446435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private int mSpanSequenceCounter = 0; 456435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private Runnable mChecker; 466435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 476435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne public SpellChecker(TextView textView) { 486435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mTextView = textView; 496435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 506435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final TextServicesManager textServicesManager = (TextServicesManager) textView.getContext(). 516435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); 526435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne spellCheckerSession = textServicesManager.newSpellCheckerSession( 536435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne null /* not currently used by the textServicesManager */, Locale.getDefault(), 546435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne this, true /* means use the languages defined in Settings */); 556435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mCookie = hashCode(); 566435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 576435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne // Arbitrary: 4 simultaneous spell check spans. Will automatically double size on demand 586435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int size = ArrayUtils.idealObjectArraySize(4); 596435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mIds = new int[size]; 606435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mSpellCheckSpans = new SpellCheckSpan[size]; 616435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mLength = 0; 626435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 636435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 646435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne public void addSpellCheckSpan(SpellCheckSpan spellCheckSpan) { 656435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne int length = mIds.length; 666435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (mLength >= length) { 676435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int newSize = length * 2; 686435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne int[] newIds = new int[newSize]; 696435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne SpellCheckSpan[] newSpellCheckSpans = new SpellCheckSpan[newSize]; 706435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne System.arraycopy(mIds, 0, newIds, 0, length); 716435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne System.arraycopy(mSpellCheckSpans, 0, newSpellCheckSpans, 0, length); 726435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mIds = newIds; 736435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mSpellCheckSpans = newSpellCheckSpans; 746435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 756435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 766435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mIds[mLength] = mSpanSequenceCounter++; 776435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mSpellCheckSpans[mLength] = spellCheckSpan; 786435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mLength++; 796435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 806435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (DEBUG_SPELL_CHECK) { 816435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final Editable mText = (Editable) mTextView.getText(); 826435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne int start = mText.getSpanStart(spellCheckSpan); 836435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne int end = mText.getSpanEnd(spellCheckSpan); 846435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (start >= 0 && end >= 0) { 856435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne Log.d(LOG_TAG, "Schedule check " + mText.subSequence(start, end)); 866435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } else { 876435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne Log.d(LOG_TAG, "Schedule check EMPTY!"); 886435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 896435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 906435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 916435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne scheduleSpellCheck(); 926435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 936435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 946435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne public void removeSpellCheckSpan(SpellCheckSpan spellCheckSpan) { 956435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne for (int i = 0; i < mLength; i++) { 966435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (mSpellCheckSpans[i] == spellCheckSpan) { 976435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne removeAtIndex(i); 986435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne return; 996435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1006435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1016435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1026435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1036435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private void removeAtIndex(int i) { 1046435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne System.arraycopy(mIds, i + 1, mIds, i, mLength - i - 1); 1056435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne System.arraycopy(mSpellCheckSpans, i + 1, mSpellCheckSpans, i, mLength - i - 1); 1066435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mLength--; 1076435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1086435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1096435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne public void onSelectionChanged() { 1106435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne scheduleSpellCheck(); 1116435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1126435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1136435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private void scheduleSpellCheck() { 1146435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (mLength == 0) return; 1156435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (mChecker != null) { 1166435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mTextView.removeCallbacks(mChecker); 1176435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1186435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (mChecker == null) { 1196435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mChecker = new Runnable() { 1206435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne public void run() { 1216435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne spellCheck(); 1226435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1236435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne }; 1246435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1256435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mTextView.postDelayed(mChecker, DELAY_BEFORE_SPELL_CHECK); 1266435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1276435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1286435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private void spellCheck() { 1296435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final Editable editable = (Editable) mTextView.getText(); 1306435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int selectionStart = Selection.getSelectionStart(editable); 1316435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int selectionEnd = Selection.getSelectionEnd(editable); 1326435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1336435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne TextInfo[] textInfos = new TextInfo[mLength]; 1346435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne int textInfosCount = 0; 1356435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1366435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne for (int i = 0; i < mLength; i++) { 1376435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i]; 1386435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1396435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (spellCheckSpan.isSpellCheckInProgress()) continue; 1406435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1416435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int start = editable.getSpanStart(spellCheckSpan); 1426435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int end = editable.getSpanEnd(spellCheckSpan); 1436435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1446435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne // Do not check this word if the user is currently editing it 145d6e3494421dff2a091f1011e5266b280b2109843Gilles Debunne if (start >= 0 && end > start && (selectionEnd < start || selectionStart > end)) { 1466435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final String word = editable.subSequence(start, end).toString(); 1476435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne spellCheckSpan.setSpellCheckInProgress(); 1486435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne textInfos[textInfosCount++] = new TextInfo(word, mCookie, mIds[i]); 1496435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1506435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1516435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1526435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (textInfosCount > 0) { 1536435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (textInfosCount < mLength) { 1546435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne TextInfo[] textInfosCopy = new TextInfo[textInfosCount]; 1556435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne System.arraycopy(textInfos, 0, textInfosCopy, 0, textInfosCount); 1566435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne textInfos = textInfosCopy; 1576435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1586435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne spellCheckerSession.getSuggestions(textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE, 1596435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne false /* TODO Set sequentialWords to true for initial spell check */); 1606435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1616435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1626435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1636435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne @Override 1646435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne public void onGetSuggestions(SuggestionsInfo[] results) { 1656435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final Editable editable = (Editable) mTextView.getText(); 1666435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne for (int i = 0; i < results.length; i++) { 1676435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne SuggestionsInfo suggestionsInfo = results[i]; 1686435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (suggestionsInfo.getCookie() != mCookie) continue; 1696435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1706435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int sequenceNumber = suggestionsInfo.getSequence(); 1716435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne // Starting from the end, to limit the number of array copy while removing 1726435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne for (int j = mLength - 1; j >= 0; j--) { 1736435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (sequenceNumber == mIds[j]) { 1746435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j]; 1756435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int attributes = suggestionsInfo.getSuggestionsAttributes(); 1766435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne boolean isInDictionary = 1776435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne ((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0); 1786435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne boolean looksLikeTypo = 1796435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0); 1806435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1816435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (DEBUG_SPELL_CHECK) { 1826435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int start = editable.getSpanStart(spellCheckSpan); 1836435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int end = editable.getSpanEnd(spellCheckSpan); 1846435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne Log.d(LOG_TAG, "Result sequence=" + suggestionsInfo.getSequence() + " " + 1856435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne editable.subSequence(start, end) + 1866435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne "\t" + (isInDictionary?"IN_DICT" : "NOT_DICT") + 1876435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne "\t" + (looksLikeTypo?"TYPO" : "NOT_TYPO")); 1886435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 1896435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 1906435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (!isInDictionary && looksLikeTypo) { 1916435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne String[] suggestions = getSuggestions(suggestionsInfo); 1926435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (suggestions.length > 0) { 1936435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne SuggestionSpan suggestionSpan = new SuggestionSpan( 1946435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mTextView.getContext(), suggestions, 1956435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne SuggestionSpan.FLAG_EASY_CORRECT | 1966435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne SuggestionSpan.FLAG_MISSPELLED); 1976435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int start = editable.getSpanStart(spellCheckSpan); 1986435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int end = editable.getSpanEnd(spellCheckSpan); 1996435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne editable.setSpan(suggestionSpan, start, end, 2006435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 2016435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne // TODO limit to the word rectangle region 2026435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne mTextView.invalidate(); 2036435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 2046435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne if (DEBUG_SPELL_CHECK) { 2056435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne String suggestionsString = ""; 2066435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne for (String s : suggestions) { suggestionsString += s + "|"; } 2076435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne Log.d(LOG_TAG, " Suggestions for " + sequenceNumber + " " + 2086435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne editable.subSequence(start, end)+ " " + suggestionsString); 2096435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 2106435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 2116435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 2126435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne editable.removeSpan(spellCheckSpan); 2136435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 2146435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 2156435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 2166435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 2176435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne 2186435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne private static String[] getSuggestions(SuggestionsInfo suggestionsInfo) { 2196435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne final int len = Math.max(0, suggestionsInfo.getSuggestionsCount()); 2206435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne String[] suggestions = new String[len]; 2216435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne for (int j = 0; j < len; ++j) { 2226435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne suggestions[j] = suggestionsInfo.getSuggestionAt(j); 2236435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 2246435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne return suggestions; 2256435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne } 2266435a56a8c02de98befcc8cd743b2b638cffb327Gilles Debunne} 227