Merge "Apply distracter filter for UserHistoryDictionary."
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 80f0cea..4cb920f 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -168,7 +168,7 @@
     }
 
     public DictionaryFacilitator() {
-        mDistracterFilter = new DistracterFilter.EmptyDistracterFilter();
+        mDistracterFilter = DistracterFilter.EMPTY_DISTRACTER_FILTER;
     }
 
     public DictionaryFacilitator(final DistracterFilter distracterFilter) {
@@ -448,7 +448,7 @@
         // We don't add words with 0-frequency (assuming they would be profanity etc.).
         final boolean isValid = maxFreq > 0;
         UserHistoryDictionary.addToDictionary(userHistoryDictionary, prevWordsInfo, secondWord,
-                isValid, timeStampInSeconds);
+                isValid, timeStampInSeconds, mDistracterFilter);
     }
 
     public void cancelAddingUserHistory(final PrevWordsInfo prevWordsInfo,
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index b92f042..95ff8c6 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -27,6 +27,7 @@
 import com.android.inputmethod.latin.makedict.WordProperty;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.utils.CombinedFormatUtils;
+import com.android.inputmethod.latin.utils.DistracterFilter;
 import com.android.inputmethod.latin.utils.ExecutorUtils;
 import com.android.inputmethod.latin.utils.FileUtils;
 import com.android.inputmethod.latin.utils.LanguageModelParam;
@@ -271,9 +272,10 @@
     /**
      * Adds unigram information of a word to the dictionary. May overwrite an existing entry.
      */
-    public void addUnigramEntry(final String word, final int frequency,
+    public void addUnigramEntryWithCheckingDistracter(final String word, final int frequency,
             final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
-            final boolean isBlacklisted, final int timestamp) {
+            final boolean isBlacklisted, final int timestamp,
+            final DistracterFilter distracterFilter) {
         reloadDictionaryIfRequired();
         asyncExecuteTaskWithWriteLock(new Runnable() {
             @Override
@@ -281,6 +283,11 @@
                 if (mBinaryDictionary == null) {
                     return;
                 }
+                if (distracterFilter.isDistracterToWordsInDictionaries(
+                        PrevWordsInfo.EMPTY_PREV_WORDS_INFO, word, mLocale)) {
+                    // The word is a distracter.
+                    return;
+                }
                 runGCIfRequiredLocked(true /* mindsBlockByGC */);
                 addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq,
                         isNotAWord, isBlacklisted, timestamp);
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
index f89caf9..67ad54f 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
@@ -23,6 +23,7 @@
 import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.ExpandableBinaryDictionary;
 import com.android.inputmethod.latin.PrevWordsInfo;
+import com.android.inputmethod.latin.utils.DistracterFilter;
 
 import java.io.File;
 import java.util.Locale;
@@ -60,10 +61,11 @@
      * @param word the word the user inputted
      * @param isValid whether the word is valid or not
      * @param timestamp the timestamp when the word has been inputted
+     * @param distracterFilter the filter to check whether the word is a distracter
      */
     public static void addToDictionary(final ExpandableBinaryDictionary userHistoryDictionary,
             final PrevWordsInfo prevWordsInfo, final String word, final boolean isValid,
-            final int timestamp) {
+            final int timestamp, final DistracterFilter distracterFilter) {
         final String prevWord = prevWordsInfo.mPrevWord;
         if (word.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH ||
                 (prevWord != null && prevWord.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) {
@@ -71,8 +73,9 @@
         }
         final int frequency = isValid ?
                 FREQUENCY_FOR_WORDS_IN_DICTS : FREQUENCY_FOR_WORDS_NOT_IN_DICTS;
-        userHistoryDictionary.addUnigramEntry(word, frequency, null /* shortcutTarget */,
-                0 /* shortcutFreq */, false /* isNotAWord */, false /* isBlacklisted */, timestamp);
+        userHistoryDictionary.addUnigramEntryWithCheckingDistracter(word, frequency,
+                null /* shortcutTarget */, 0 /* shortcutFreq */, false /* isNotAWord */,
+                false /* isBlacklisted */, timestamp, distracterFilter);
         // Do not insert a word as a bigram of itself
         if (word.equals(prevWord)) {
             return;
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
index 6e0fab3..787e4a5 100644
--- a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
@@ -40,7 +40,7 @@
 
     public void close();
 
-    public static final class EmptyDistracterFilter implements DistracterFilter {
+    public static final DistracterFilter EMPTY_DISTRACTER_FILTER = new DistracterFilter() {
         @Override
         public boolean isDistracterToWordsInDictionaries(PrevWordsInfo prevWordsInfo,
                 String testedWord, Locale locale) {
@@ -54,5 +54,5 @@
         @Override
         public void updateEnabledSubtypes(List<InputMethodSubtype> enabledSubtypes) {
         }
-    }
+    };
 }
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java
index 3fc8418..1c93a91 100644
--- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java
@@ -32,6 +32,7 @@
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.KeyboardLayoutSet;
+import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.DictionaryFacilitator;
 import com.android.inputmethod.latin.PrevWordsInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -205,14 +206,25 @@
         final String consideredWord = trailingSingleQuotesCount > 0 ?
                 testedWord.substring(0, testedWord.length() - trailingSingleQuotesCount) :
                 testedWord;
-
-        final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults(
-                composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, mKeyboard.getProximityInfo(),
-                true /* blockOffensiveWords */, null /* additionalFeaturesOptions */,
-                0 /* sessionId */, null /* rawSuggestions */);
-        if (suggestionResults.isEmpty()) {
-            return false;
-        }
-        return isDistracter(suggestionResults, consideredWord);
+        final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<>();
+        ExecutorUtils.getExecutor("check distracters").execute(new Runnable() {
+            @Override
+            public void run() {
+                final SuggestionResults suggestionResults =
+                        mDictionaryFacilitator.getSuggestionResults(
+                                composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO,
+                                mKeyboard.getProximityInfo(), true /* blockOffensiveWords */,
+                                null /* additionalFeaturesOptions */, 0 /* sessionId */,
+                                null /* rawSuggestions */);
+                if (suggestionResults.isEmpty()) {
+                    holder.set(false);
+                    return;
+                }
+                holder.set(isDistracter(suggestionResults, consideredWord));
+            }
+        });
+        // It's OK to block the distracter filtering, but the dictionary lookup should be done
+        // sequentially using ExecutorUtils.
+        return holder.get(false /* defaultValue */, Constants.GET_SUGGESTED_WORDS_TIMEOUT);
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index 8bcf14a..c67d1fa 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -23,6 +23,7 @@
 import com.android.inputmethod.latin.ExpandableBinaryDictionary;
 import com.android.inputmethod.latin.PrevWordsInfo;
 import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
+import com.android.inputmethod.latin.utils.DistracterFilter;
 import com.android.inputmethod.latin.utils.FileUtils;
 
 import java.io.File;
@@ -112,7 +113,8 @@
         PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
         for (String word : words) {
             UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true,
-                    (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
+                    (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()),
+                    DistracterFilter.EMPTY_DISTRACTER_FILTER);
             prevWordsInfo = new PrevWordsInfo(word);
         }
     }
@@ -262,7 +264,8 @@
         dict.waitAllTasksForTests();
         PrevWordsInfo prevWordsInfo = new PrevWordsInfo(null);
         for (final String word : words) {
-            UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, mCurrentTime);
+            UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, mCurrentTime,
+                    DistracterFilter.EMPTY_DISTRACTER_FILTER);
             prevWordsInfo = new PrevWordsInfo(word);
             dict.waitAllTasksForTests();
             assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word));