Merge "Annotate the typed word with its source dictionary"
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 4df1d65..2d0ec42 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -32,6 +32,8 @@
 import java.util.HashMap;
 import java.util.Locale;
 
+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.
@@ -143,14 +145,16 @@
         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);
+        @Nullable final Dictionary sourceDictionaryOfRemovedWord =
+                SuggestedWordInfo.removeDupsAndReturnSourceOfTypedWord(wordComposer.getTypedWord(),
+                        mostProbableLocale /* preferredLocale */, suggestionsContainer);
 
         final String whitelistedWord = getWhitelistedWordOrNull(suggestionsContainer);
         final boolean resultsArePredictions = !wordComposer.isComposingWord();
@@ -158,7 +162,7 @@
         // 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
@@ -209,7 +213,8 @@
 
         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)) {
@@ -275,7 +280,8 @@
             final SuggestedWordInfo rejected = suggestionsContainer.remove(0);
             suggestionsContainer.add(1, rejected);
         }
-        SuggestedWordInfo.removeDups(null /* typedWord */, suggestionsContainer);
+        SuggestedWordInfo.removeDupsAndReturnSourceOfTypedWord(null /* typedWord */,
+                null /* preferredLocale */, suggestionsContainer);
 
         // For some reason some suggestions with MIN_VALUE are making their way here.
         // TODO: Find a more robust way to detect distracters.
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 49153d2..cbf48f0 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -27,6 +27,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.Locale;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -355,37 +356,53 @@
         }
 
         // This will always remove the higher index if a duplicate is found.
-        public static boolean removeDups(final String typedWord,
-                ArrayList<SuggestedWordInfo> candidates) {
+        // Returns null if the typed word is not found. Always return the dictionary for the
+        // highest suggestion matching the locale if found, otherwise return the dictionary for
+        // the highest suggestion.
+        @Nullable
+        public static Dictionary removeDupsAndReturnSourceOfTypedWord(
+                @Nullable final String typedWord,
+                @Nullable final Locale preferredLocale,
+                @Nonnull ArrayList<SuggestedWordInfo> candidates) {
             if (candidates.isEmpty()) {
-                return false;
+                return null;
             }
-            final boolean didRemoveTypedWord;
+            final Dictionary sourceDictionaryOfTypedWord;
             if (!TextUtils.isEmpty(typedWord)) {
-                didRemoveTypedWord = removeSuggestedWordInfoFrom(typedWord, candidates,
-                        -1 /* startIndexExclusive */);
+                sourceDictionaryOfTypedWord =
+                        removeSuggestedWordInfoFromListAndReturnSourceDictionary(typedWord,
+                                preferredLocale, candidates, -1 /* startIndexExclusive */);
             } else {
-                didRemoveTypedWord = false;
+                sourceDictionaryOfTypedWord = null;
             }
             for (int i = 0; i < candidates.size(); ++i) {
-                removeSuggestedWordInfoFrom(candidates.get(i).mWord, candidates,
-                        i /* startIndexExclusive */);
+                removeSuggestedWordInfoFromListAndReturnSourceDictionary(candidates.get(i).mWord,
+                        null /* preferredLocale */, candidates, i /* startIndexExclusive */);
             }
-            return didRemoveTypedWord;
+            return sourceDictionaryOfTypedWord;
         }
 
-        private static boolean removeSuggestedWordInfoFrom(final String word,
-                final ArrayList<SuggestedWordInfo> candidates, final int startIndexExclusive) {
-            boolean didRemove = false;
+        @Nullable
+        private static Dictionary removeSuggestedWordInfoFromListAndReturnSourceDictionary(
+                @Nonnull final String word, @Nullable final Locale preferredLocale,
+                @Nonnull final ArrayList<SuggestedWordInfo> candidates,
+                final int startIndexExclusive) {
+            Dictionary sourceDictionaryOfTypedWord = null;
             for (int i = startIndexExclusive + 1; i < candidates.size(); ++i) {
                 final SuggestedWordInfo previous = candidates.get(i);
                 if (word.equals(previous.mWord)) {
-                    didRemove = true;
+                    if (null == sourceDictionaryOfTypedWord
+                            || (null != preferredLocale
+                                    && preferredLocale.equals(previous.mSourceDict.mLocale))) {
+                        if (Dictionary.TYPE_USER_HISTORY != previous.mSourceDict.mDictType) {
+                            sourceDictionaryOfTypedWord = previous.mSourceDict;
+                        }
+                    }
                     candidates.remove(i);
                     --i;
                 }
             }
-            return didRemove;
+            return sourceDictionaryOfTypedWord;
         }
     }