diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/Suggest.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/Suggest.java | 109 |
1 files changed, 100 insertions, 9 deletions
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 4df1d6505..0bf0f687a 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -32,6 +32,9 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + /** * This class loads a dictionary and provides a list of suggestions for a given sequence of * characters. This includes corrections and completions. @@ -62,15 +65,30 @@ public final class Suggest { } private float mAutoCorrectionThreshold; + private float mPlausibilityThreshold; public Suggest(final DictionaryFacilitator dictionaryFacilitator) { mDictionaryFacilitator = dictionaryFacilitator; } + /** + * Set the normalized-score threshold for a suggestion to be considered strong enough that we + * will auto-correct to this. + * @param threshold the threshold + */ public void setAutoCorrectionThreshold(final float threshold) { mAutoCorrectionThreshold = threshold; } + /** + * Set the normalized-score threshold for what we consider a "plausible" suggestion, in + * the same dimension as the auto-correction threshold. + * @param threshold the threshold + */ + public void setPlausibilityThreshold(final float threshold) { + mPlausibilityThreshold = threshold; + } + public interface OnGetSuggestedWordsCallback { public void onGetSuggestedWords(final SuggestedWords suggestedWords); } @@ -115,7 +133,8 @@ public final class Suggest { return suggestionsContainer; } - private static String getWhitelistedWordOrNull(final ArrayList<SuggestedWordInfo> suggestions) { + private static SuggestedWordInfo getWhitelistedWordInfoOrNull( + @Nonnull final ArrayList<SuggestedWordInfo> suggestions) { if (suggestions.isEmpty()) { return null; } @@ -123,9 +142,21 @@ public final class Suggest { if (!firstSuggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) { return null; } - return firstSuggestedWordInfo.mWord; + return firstSuggestedWordInfo; } + // Quality constants for dictionary match + // In increasing order of quality + // This source dictionary does not match the typed word. + private static final int QUALITY_NO_MATCH = 0; + // This source dictionary has a null locale, and the preferred locale is also null. + private static final int QUALITY_MATCH_NULL = 1; + // This source dictionary has a non-null locale different from the preferred locale. The + // preferred locale may be null : this is still better than MATCH_NULL. + private static final int QUALITY_MATCH_OTHER_LOCALE = 2; + // This source dictionary matches the preferred locale. + private static final int QUALITY_MATCH_PREFERRED_LOCALE = 3; + // Retrieves suggestions for non-batch input (typing, recorrection, predictions...) // and calls the callback function with the suggestions. private void getSuggestedWordsForNonBatchInput(final WordComposer wordComposer, @@ -143,22 +174,67 @@ public final class Suggest { final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( wordComposer, ngramContext, proximityInfo.getNativeProximityInfo(), settingsValuesForSuggestion, SESSION_ID_TYPING); + final Locale mostProbableLocale = mDictionaryFacilitator.getMostProbableLocale(); final ArrayList<SuggestedWordInfo> suggestionsContainer = getTransformedSuggestedWordInfoList(wordComposer, suggestionResults, trailingSingleQuotesCount, // For transforming suggestions that don't come for any dictionary, we // use the currently most probable locale as it's our best bet. - mDictionaryFacilitator.getMostProbableLocale()); - final boolean didRemoveTypedWord = - SuggestedWordInfo.removeDups(wordComposer.getTypedWord(), suggestionsContainer); + mostProbableLocale); + + boolean typedWordExistsInAnotherLanguage = false; + int qualityOfFoundSourceDictionary = QUALITY_NO_MATCH; + @Nullable Dictionary sourceDictionaryOfRemovedWord = null; + for (final SuggestedWordInfo info : suggestionsContainer) { + // Search for the best dictionary, defined as the first one with the highest match + // quality we can find. + if (typedWordString.equals(info.mWord)) { + if (mostProbableLocale.equals(info.mSourceDict.mLocale)) { + if (qualityOfFoundSourceDictionary < QUALITY_MATCH_PREFERRED_LOCALE) { + // Use this source if the old match had lower quality than this match + sourceDictionaryOfRemovedWord = info.mSourceDict; + qualityOfFoundSourceDictionary = QUALITY_MATCH_PREFERRED_LOCALE; + } + } else { + final int matchQuality = (null == info.mSourceDict.mLocale) + ? QUALITY_MATCH_NULL : QUALITY_MATCH_OTHER_LOCALE; + if (qualityOfFoundSourceDictionary < matchQuality) { + // Use this source if the old match had lower quality than this match + sourceDictionaryOfRemovedWord = info.mSourceDict; + qualityOfFoundSourceDictionary = matchQuality; + } + typedWordExistsInAnotherLanguage = true; + } + } + } - final String whitelistedWord = getWhitelistedWordOrNull(suggestionsContainer); + SuggestedWordInfo.removeDups(typedWordString, suggestionsContainer); + + final SuggestedWordInfo whitelistedWordInfo = + getWhitelistedWordInfoOrNull(suggestionsContainer); + final String whitelistedWord; + if (null != whitelistedWordInfo && + (mDictionaryFacilitator.isConfidentAboutCurrentLanguageBeing( + whitelistedWordInfo.mSourceDict.mLocale) + || (!typedWordExistsInAnotherLanguage + && !hasPlausibleCandidateInAnyOtherLanguage(suggestionsContainer, + consideredWord, whitelistedWordInfo)))) { + // We'll use the whitelist candidate if we are confident the user is typing in the + // language of the dictionary it's coming from, or if there is no plausible candidate + // coming from another language. + whitelistedWord = whitelistedWordInfo.mWord; + } else { + // If on the contrary we are not confident in the current language and we have + // at least a plausible candidate in any other language, then we don't use this + // whitelist candidate. + whitelistedWord = null; + } final boolean resultsArePredictions = !wordComposer.isComposingWord(); // We allow auto-correction if we have a whitelisted word, or if the word had more than // one char and was not suggested. final boolean allowsToBeAutoCorrected = (null != whitelistedWord) - || (consideredWord.length() > 1 && !didRemoveTypedWord); + || (consideredWord.length() > 1 && (null == sourceDictionaryOfRemovedWord)); final boolean hasAutoCorrection; // If correction is not enabled, we never auto-correct. This is for example for when @@ -194,7 +270,7 @@ public final class Suggest { hasAutoCorrection = false; } else { final SuggestedWordInfo firstSuggestion = suggestionResults.first(); - if (!AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold( + if (!AutoCorrectionUtils.suggestionExceedsThreshold( firstSuggestion, consideredWord, mAutoCorrectionThreshold)) { // Score is too low for autocorrect hasAutoCorrection = false; @@ -209,7 +285,8 @@ public final class Suggest { final SuggestedWordInfo typedWordInfo = new SuggestedWordInfo(typedWordString, SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, - Dictionary.DICTIONARY_USER_TYPED, + null == sourceDictionaryOfRemovedWord ? Dictionary.DICTIONARY_USER_TYPED + : sourceDictionaryOfRemovedWord, SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */); if (!TextUtils.isEmpty(typedWordString)) { @@ -242,6 +319,20 @@ public final class Suggest { false /* isObsoleteSuggestions */, inputStyle, sequenceNumber)); } + private boolean hasPlausibleCandidateInAnyOtherLanguage( + final ArrayList<SuggestedWordInfo> suggestionsContainer, final String consideredWord, + final SuggestedWordInfo whitelistedWordInfo) { + for (final SuggestedWordInfo info : suggestionsContainer) { + if (whitelistedWordInfo.mSourceDict.mLocale.equals(info.mSourceDict.mLocale)) { + continue; + } + return AutoCorrectionUtils.suggestionExceedsThreshold(info, consideredWord, + mPlausibilityThreshold); + } + // No candidate in another language + return false; + } + // Retrieves suggestions for the batch input // and calls the callback function with the suggestions. private void getSuggestedWordsForBatchInput(final WordComposer wordComposer, |