diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/Suggest.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/Suggest.java | 235 |
1 files changed, 54 insertions, 181 deletions
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 0a4c7a55d..abf831a28 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -16,18 +16,11 @@ package com.android.inputmethod.latin; -import android.content.Context; -import android.preference.PreferenceManager; import android.text.TextUtils; -import android.util.Log; -import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.personalization.PersonalizationDictionary; -import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary; -import com.android.inputmethod.latin.personalization.UserHistoryDictionary; -import com.android.inputmethod.latin.settings.Settings; +import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.BoundedTreeSet; import com.android.inputmethod.latin.utils.CollectionUtils; @@ -35,9 +28,7 @@ import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Comparator; -import java.util.HashSet; import java.util.Locale; -import java.util.concurrent.ConcurrentHashMap; /** * This class loads a dictionary and provides a list of suggestions for a given sequence of @@ -60,150 +51,26 @@ public final class Suggest { // Close to -2**31 private static final int SUPPRESS_SUGGEST_THRESHOLD = -2000000000; - public static final int MAX_SUGGESTIONS = 18; - - public interface SuggestInitializationListener { - public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable); - } - private static final boolean DBG = LatinImeLogger.sDBG; - - private final ConcurrentHashMap<String, Dictionary> mDictionaries = - CollectionUtils.newConcurrentHashMap(); - private HashSet<String> mOnlyDictionarySetForDebug = null; - private Dictionary mMainDictionary; - private ContactsBinaryDictionary mContactsDict; - @UsedForTesting - private boolean mIsCurrentlyWaitingForMainDictionary = false; + public final DictionaryFacilitatorForSuggest mDictionaryFacilitator; private float mAutoCorrectionThreshold; // Locale used for upper- and title-casing words public final Locale mLocale; - public Suggest(final Context context, final Locale locale, - final SuggestInitializationListener listener) { - initAsynchronously(context, locale, listener); - mLocale = locale; - // initialize a debug flag for the personalization - if (Settings.readUseOnlyPersonalizationDictionaryForDebug( - PreferenceManager.getDefaultSharedPreferences(context))) { - mOnlyDictionarySetForDebug = new HashSet<String>(); - mOnlyDictionarySetForDebug.add(Dictionary.TYPE_PERSONALIZATION); - mOnlyDictionarySetForDebug.add(Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA); - } - } - - @UsedForTesting - Suggest(final AssetFileAddress[] dictionaryList, final Locale locale) { - final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(dictionaryList, - false /* useFullEditDistance */, locale); + public Suggest(final Locale locale, + final DictionaryFacilitatorForSuggest dictionaryFacilitator) { mLocale = locale; - mMainDictionary = mainDict; - addOrReplaceDictionaryInternal(Dictionary.TYPE_MAIN, mainDict); - } - - private void initAsynchronously(final Context context, final Locale locale, - final SuggestInitializationListener listener) { - resetMainDict(context, locale, listener); - } - - private void addOrReplaceDictionaryInternal(final String key, final Dictionary dict) { - if (mOnlyDictionarySetForDebug != null && !mOnlyDictionarySetForDebug.contains(key)) { - Log.w(TAG, "Ignore add " + key + " dictionary for debug."); - return; - } - addOrReplaceDictionary(mDictionaries, key, dict); - } - - private static void addOrReplaceDictionary( - final ConcurrentHashMap<String, Dictionary> dictionaries, - final String key, final Dictionary dict) { - final Dictionary oldDict = (dict == null) - ? dictionaries.remove(key) - : dictionaries.put(key, dict); - if (oldDict != null && dict != oldDict) { - oldDict.close(); - } - } - - public void resetMainDict(final Context context, final Locale locale, - final SuggestInitializationListener listener) { - mIsCurrentlyWaitingForMainDictionary = true; - mMainDictionary = null; - if (listener != null) { - listener.onUpdateMainDictionaryAvailability(hasMainDictionary()); - } - new Thread("InitializeBinaryDictionary") { - @Override - public void run() { - final DictionaryCollection newMainDict = - DictionaryFactory.createMainDictionaryFromManager(context, locale); - addOrReplaceDictionaryInternal(Dictionary.TYPE_MAIN, newMainDict); - mMainDictionary = newMainDict; - if (listener != null) { - listener.onUpdateMainDictionaryAvailability(hasMainDictionary()); - } - mIsCurrentlyWaitingForMainDictionary = false; - } - }.start(); - } - - // The main dictionary could have been loaded asynchronously. Don't cache the return value - // of this method. - public boolean hasMainDictionary() { - return null != mMainDictionary && mMainDictionary.isInitialized(); - } - - @UsedForTesting - public boolean isCurrentlyWaitingForMainDictionary() { - return mIsCurrentlyWaitingForMainDictionary; - } - - public Dictionary getMainDictionary() { - return mMainDictionary; - } - - public ContactsBinaryDictionary getContactsDictionary() { - return mContactsDict; - } - - public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() { - return mDictionaries; - } - - /** - * Sets an optional user dictionary resource to be loaded. The user dictionary is consulted - * before the main dictionary, if set. This refers to the system-managed user dictionary. - */ - public void setUserDictionary(final UserBinaryDictionary userDictionary) { - addOrReplaceDictionaryInternal(Dictionary.TYPE_USER, userDictionary); + mDictionaryFacilitator = dictionaryFacilitator; } - /** - * Sets an optional contacts dictionary resource to be loaded. It is also possible to remove - * the contacts dictionary by passing null to this method. In this case no contacts dictionary - * won't be used. - */ - public void setContactsDictionary(final ContactsBinaryDictionary contactsDictionary) { - mContactsDict = contactsDictionary; - addOrReplaceDictionaryInternal(Dictionary.TYPE_CONTACTS, contactsDictionary); - } - - public void setUserHistoryDictionary(final UserHistoryDictionary userHistoryDictionary) { - addOrReplaceDictionaryInternal(Dictionary.TYPE_USER_HISTORY, userHistoryDictionary); - } - - public void setPersonalizationPredictionDictionary( - final PersonalizationPredictionDictionary personalizationPredictionDictionary) { - addOrReplaceDictionaryInternal(Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA, - personalizationPredictionDictionary); - } - - public void setPersonalizationDictionary( - final PersonalizationDictionary personalizationDictionary) { - addOrReplaceDictionaryInternal(Dictionary.TYPE_PERSONALIZATION, - personalizationDictionary); + // Creates instance with new dictionary facilitator. + public Suggest(final Suggest oldSuggst, + final DictionaryFacilitatorForSuggest dictionaryFacilitator) { + mLocale = oldSuggst.mLocale; + mAutoCorrectionThreshold = oldSuggst.mAutoCorrectionThreshold; + mDictionaryFacilitator = dictionaryFacilitator; } public void setAutoCorrectionThreshold(float threshold) { @@ -240,7 +107,7 @@ public final class Suggest { final OnGetSuggestedWordsCallback callback) { final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, - MAX_SUGGESTIONS); + SuggestedWords.MAX_SUGGESTIONS); final String typedWord = wordComposer.getTypedWord(); final String consideredWord = trailingSingleQuotesCount > 0 @@ -257,29 +124,40 @@ public final class Suggest { } else { wordComposerForLookup = wordComposer; } - - for (final String key : mDictionaries.keySet()) { - final Dictionary dictionary = mDictionaries.get(key); - suggestionsSet.addAll(dictionary.getSuggestions(wordComposerForLookup, - prevWordForBigram, proximityInfo, blockOffensiveWords, - additionalFeaturesOptions)); + final ArrayList<SuggestedWordInfo> rawSuggestions; + if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) { + rawSuggestions = CollectionUtils.newArrayList(); + } else { + rawSuggestions = null; } - + mDictionaryFacilitator.getSuggestions(wordComposerForLookup, prevWordForBigram, + proximityInfo, blockOffensiveWords, additionalFeaturesOptions, SESSION_TYPING, + suggestionsSet, rawSuggestions); + final String firstSuggestion; final String whitelistedWord; if (suggestionsSet.isEmpty()) { - whitelistedWord = null; - } else if (SuggestedWordInfo.KIND_WHITELIST != suggestionsSet.first().mKind) { - whitelistedWord = null; + whitelistedWord = firstSuggestion = null; } else { - whitelistedWord = suggestionsSet.first().mWord; + final SuggestedWordInfo firstSuggestedWordInfo = suggestionsSet.first(); + firstSuggestion = firstSuggestedWordInfo.mWord; + if (SuggestedWordInfo.KIND_WHITELIST != firstSuggestedWordInfo.mKind) { + whitelistedWord = null; + } else { + whitelistedWord = firstSuggestion; + } } - // The word can be auto-corrected if it has a whitelist entry that is not itself, - // or if it's a 2+ characters non-word (i.e. it's not in the dictionary). + // We allow auto-correction if we have a whitelisted word, or if the word is not a valid + // word of more than 1 char, except if the first suggestion is the same as the typed string + // because in this case if it's strong enough to auto-correct that will mistakenly designate + // the second candidate for auto-correction. + // TODO: stop relying on indices to find where is the auto-correction in the suggested + // words, and correct this test. final boolean allowsToBeAutoCorrected = (null != whitelistedWord && !whitelistedWord.equals(consideredWord)) - || (consideredWord.length() > 1 && !AutoCorrectionUtils.isValidWord(this, - consideredWord, wordComposer.isFirstCharCapitalized())); + || (consideredWord.length() > 1 && !mDictionaryFacilitator.isValidWord( + consideredWord, wordComposer.isFirstCharCapitalized()) + && !consideredWord.equals(firstSuggestion)); final boolean hasAutoCorrection; // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because @@ -289,7 +167,8 @@ public final class Suggest { // the word *would* have been auto-corrected. if (!isCorrectionEnabled || !allowsToBeAutoCorrected || !wordComposer.isComposingWord() || suggestionsSet.isEmpty() || wordComposer.hasDigits() - || wordComposer.isMostlyCaps() || wordComposer.isResumed() || !hasMainDictionary() + || wordComposer.isMostlyCaps() || wordComposer.isResumed() + || !mDictionaryFacilitator.hasMainDictionary() || SuggestedWordInfo.KIND_SHORTCUT == suggestionsSet.first().mKind) { // If we don't have a main dictionary, we never want to auto-correct. The reason for // this is, the user may have a contact whose name happens to match a valid word in @@ -342,13 +221,12 @@ public final class Suggest { suggestionsList = suggestionsContainer; } - callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, + callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, rawSuggestions, // TODO: this first argument is lying. If this is a whitelisted word which is an // actual word, it says typedWordValid = false, which looks wrong. We should either // rename the attribute or change the value. !allowsToBeAutoCorrected /* typedWordValid */, hasAutoCorrection, /* willAutoCorrect */ - false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, !wordComposer.isComposingWord() /* isPrediction */, sequenceNumber)); } @@ -361,16 +239,16 @@ public final class Suggest { final int sessionId, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, - MAX_SUGGESTIONS); - - // At second character typed, search the unigrams (scores being affected by bigrams) - for (final String key : mDictionaries.keySet()) { - final Dictionary dictionary = mDictionaries.get(key); - suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId(wordComposer, - prevWordForBigram, proximityInfo, blockOffensiveWords, - additionalFeaturesOptions, sessionId)); + SuggestedWords.MAX_SUGGESTIONS); + final ArrayList<SuggestedWordInfo> rawSuggestions; + if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) { + rawSuggestions = CollectionUtils.newArrayList(); + } else { + rawSuggestions = null; } - + mDictionaryFacilitator.getSuggestions(wordComposer, prevWordForBigram, proximityInfo, + blockOffensiveWords, additionalFeaturesOptions, sessionId, suggestionsSet, + rawSuggestions); for (SuggestedWordInfo wordInfo : suggestionsSet) { LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict.mDictType); } @@ -407,10 +285,9 @@ public final class Suggest { // In the batch input mode, the most relevant suggested word should act as a "typed word" // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false). - callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, + callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, rawSuggestions, true /* typedWordValid */, false /* willAutoCorrect */, - false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, false /* isPrediction */, sequenceNumber)); } @@ -432,7 +309,8 @@ public final class Suggest { final String scoreInfoString; if (normalizedScore > 0) { scoreInfoString = String.format( - Locale.ROOT, "%d (%4.2f)", cur.mScore, normalizedScore); + Locale.ROOT, "%d (%4.2f), %s", cur.mScore, normalizedScore, + cur.mSourceDict.mDictType); } else { scoreInfoString = Integer.toString(cur.mScore); } @@ -483,11 +361,6 @@ public final class Suggest { } public void close() { - final HashSet<Dictionary> dictionaries = CollectionUtils.newHashSet(); - dictionaries.addAll(mDictionaries.values()); - for (final Dictionary dictionary : dictionaries) { - dictionary.close(); - } - mMainDictionary = null; + mDictionaryFacilitator.close(); } } |