@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();
        }
      }
    }