@Override public boolean onUnbind(final Intent intent) { final Map<String, DictionaryPool> oldPools = mDictionaryPools; mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>()); final Map<String, Dictionary> oldUserDictionaries = mUserDictionaries; mUserDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>()); final Map<String, Dictionary> oldWhitelistDictionaries = mWhitelistDictionaries; mWhitelistDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>()); for (DictionaryPool pool : oldPools.values()) { pool.close(); } for (Dictionary dict : oldUserDictionaries.values()) { dict.close(); } for (Dictionary dict : oldWhitelistDictionaries.values()) { dict.close(); } if (null != mContactsDictionary) { // The synchronously loaded contacts dictionary should have been in one // or several pools, but it is shielded against multiple closing and it's // safe to call it several times. final SynchronouslyLoadedContactsDictionary dictToClose = mContactsDictionary; mContactsDictionary = null; dictToClose.close(); } return false; }
/** * Gets a list of suggestions for a specific string. This returns a list of possible corrections * for the text passed as an argument. It may split or group words, and even perform grammatical * analysis. */ @Override public SuggestionsInfo onGetSuggestions(final TextInfo textInfo, final int suggestionsLimit) { try { final String text = textInfo.getText(); if (shouldFilterOut(text)) { DictAndProximity dictInfo = null; try { dictInfo = mDictionaryPool.takeOrGetNull(); if (null == dictInfo) return getNotInDictEmptySuggestions(); return dictInfo.mDictionary.isValidWord(text) ? getInDictEmptySuggestions() : getNotInDictEmptySuggestions(); } finally { if (null != dictInfo) { if (!mDictionaryPool.offer(dictInfo)) { Log.e(TAG, "Can't re-insert a dictionary into its pool"); } } } } // TODO: Don't gather suggestions if the limit is <= 0 unless necessary final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer( text, mService.mSuggestionThreshold, mService.mLikelyThreshold, suggestionsLimit); final WordComposer composer = new WordComposer(); final int length = text.length(); for (int i = 0; i < length; ++i) { final int character = text.codePointAt(i); final int proximityIndex = SpellCheckerProximityInfo.getIndexOf(character); final int[] proximities; if (-1 == proximityIndex) { proximities = new int[] {character}; } else { proximities = Arrays.copyOfRange( SpellCheckerProximityInfo.PROXIMITY, proximityIndex, proximityIndex + SpellCheckerProximityInfo.ROW_SIZE); } composer.add( character, proximities, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); } final int capitalizeType = getCapitalizationType(text); boolean isInDict = true; DictAndProximity dictInfo = null; try { dictInfo = mDictionaryPool.takeOrGetNull(); if (null == dictInfo) return getNotInDictEmptySuggestions(); dictInfo.mDictionary.getWords(composer, suggestionsGatherer, dictInfo.mProximityInfo); isInDict = dictInfo.mDictionary.isValidWord(text); if (!isInDict && CAPITALIZE_NONE != capitalizeType) { // We want to test the word again if it's all caps or first caps only. // If it's fully down, we already tested it, if it's mixed case, we don't // want to test a lowercase version of it. isInDict = dictInfo.mDictionary.isValidWord(text.toLowerCase(mLocale)); } } finally { if (null != dictInfo) { if (!mDictionaryPool.offer(dictInfo)) { Log.e(TAG, "Can't re-insert a dictionary into its pool"); } } } final SuggestionsGatherer.Result result = suggestionsGatherer.getResults(capitalizeType, mLocale); if (DBG) { Log.i( TAG, "Spell checking results for " + text + " with suggestion limit " + suggestionsLimit); Log.i(TAG, "IsInDict = " + isInDict); Log.i(TAG, "LooksLikeTypo = " + (!isInDict)); Log.i(TAG, "HasLikelySuggestions = " + result.mHasLikelySuggestions); if (null != result.mSuggestions) { for (String suggestion : result.mSuggestions) { Log.i(TAG, suggestion); } } } // TODO: actually use result.mHasLikelySuggestions final int flags = (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY : SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO); return new SuggestionsInfo(flags, result.mSuggestions); } catch (RuntimeException e) { // Don't kill the keyboard if there is a bug in the spell checker if (DBG) { throw e; } else { Log.e(TAG, "Exception while spellcheking: " + e); return getNotInDictEmptySuggestions(); } } }