Merge "Fix InputMethodInfo cache in RichInputMethodManager"
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index ddff769..5efa733 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -491,8 +491,6 @@
     <string name="prefs_key_popup_dismiss_end_scale_settings" translatable="false">Key popup dismiss end scale</string>
     <!-- Title of the settings for reading an external dictionary file -->
     <string name="prefs_read_external_dictionary">Read external dictionary file</string>
-    <!-- Title of the settings for using only personalization dictionary -->
-    <string name="prefs_use_only_personalization_dictionary" translatable="false">Use only personalization dictionary</string>
     <!-- Message to show when there are no files to install as an external dictionary [CHAR LIMIT=100] -->
     <string name="read_external_dictionary_no_files_message">No dictionary files in the Downloads folder</string>
     <!-- Title of the dialog that selects a file to install as an external dictionary [CHAR LIMIT=50] -->
diff --git a/java/res/xml/prefs_for_debug.xml b/java/res/xml/prefs_for_debug.xml
index 81a5d98..7b2b8ea 100644
--- a/java/res/xml/prefs_for_debug.xml
+++ b/java/res/xml/prefs_for_debug.xml
@@ -65,11 +65,6 @@
         android:key="pref_key_preview_dismiss_duration"
         android:title="@string/prefs_key_popup_dismiss_duration_settings"
         latin:maxValue="100" /> <!-- milliseconds -->
-    <CheckBoxPreference
-        android:defaultValue="false"
-        android:key="use_only_personalization_dictionary_for_debug"
-        android:persistent="true"
-        android:title="@string/prefs_use_only_personalization_dictionary" />
     <PreferenceScreen
         android:key="read_external_dictionary"
         android:title="@string/prefs_read_external_dictionary" />
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
index cd18a6b..d6178fc 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
@@ -55,7 +55,6 @@
 
     private final ConcurrentHashMap<String, Dictionary> mDictionaries =
             CollectionUtils.newConcurrentHashMap();
-    private HashSet<String> mDictionarySubsetForDebug = null;
 
     private Dictionary mMainDictionary;
     private ContactsBinaryDictionary mContactsDictionary;
@@ -85,7 +84,6 @@
         mContext = context;
         mLocale = locale;
         mLatchForWaitingLoadingMainDictionary = new CountDownLatch(1);
-        initForDebug(settingsValues);
         loadMainDict(context, locale, listener);
         setUserDictionary(new UserBinaryDictionary(context, locale));
         resetAdditionalDictionaries(oldDictionaryFacilitator, settingsValues);
@@ -101,7 +99,6 @@
             final DictionaryFacilitatorForSuggest oldDictionaryFacilitator) {
         mContext = oldDictionaryFacilitator.mContext;
         mLocale = oldDictionaryFacilitator.mLocale;
-        mDictionarySubsetForDebug = oldDictionaryFacilitator.mDictionarySubsetForDebug;
         mLatchForWaitingLoadingMainDictionary = new CountDownLatch(1);
         loadMainDict(mContext, mLocale, listener);
         // Transfer user dictionary.
@@ -130,7 +127,6 @@
         mContext = oldDictionaryFacilitator.mContext;
         mLocale = oldDictionaryFacilitator.mLocale;
         mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0);
-        initForDebug(settingsValues);
         // Transfer main dictionary.
         setMainDictionary(oldDictionaryFacilitator.mMainDictionary);
         oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_MAIN);
@@ -197,12 +193,12 @@
         }
     }
 
-    // initialize a debug flag for the personalization
-    private void initForDebug(final SettingsValues settingsValues) {
-        if (settingsValues.mUseOnlyPersonalizationDictionaryForDebug) {
-            mDictionarySubsetForDebug = new HashSet<String>();
-            mDictionarySubsetForDebug.add(Dictionary.TYPE_PERSONALIZATION);
-        }
+    public boolean needsToBeRecreated(final Locale newLocale,
+            final SettingsValues newSettingsValues) {
+        return !mLocale.equals(newLocale)
+                || (newSettingsValues.mUseContactsDict != (mContactsDictionary != null))
+                || (newSettingsValues.mUsePersonalizedDicts != (mUserHistoryDictionary != null))
+                || (newSettingsValues.mUsePersonalizedDicts != hasPersonalizationDictionary());
     }
 
     public void close() {
@@ -531,10 +527,6 @@
     }
 
     private void addOrReplaceDictionary(final String key, final Dictionary dict) {
-        if (mDictionarySubsetForDebug != null && !mDictionarySubsetForDebug.contains(key)) {
-            Log.w(TAG, "Ignore add " + key + " dictionary for debug.");
-            return;
-        }
         final Dictionary oldDict;
         if (dict == null) {
             oldDict = mDictionaries.remove(key);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index bfc5780..4c2454c 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -530,27 +530,31 @@
         final EditorInfo editorInfo = getCurrentInputEditorInfo();
         final InputAttributes inputAttributes = new InputAttributes(editorInfo, isFullscreenMode());
         mSettings.loadSettings(this, locale, inputAttributes);
-        AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(mSettings.getCurrent());
-        // To load the keyboard we need to load all the settings once, but resetting the
-        // contacts dictionary should be deferred until after the new layout has been displayed
-        // to improve responsivity. In the language switching process, we post a reopenDictionaries
-        // message, then come here to read the settings for the new language before we change
-        // the layout; at this time, we need to skip resetting the contacts dictionary. It will
-        // be done later inside {@see #initSuggest()} when the reopenDictionaries message is
-        // processed.
         final SettingsValues currentSettingsValues = mSettings.getCurrent();
-        final Suggest suggest = mInputLogic.mSuggest;
-        if (!mHandler.hasPendingReopenDictionaries() && suggest != null) {
+        AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(currentSettingsValues);
+        // This method is called on startup and language switch, before the new layout has
+        // been displayed. Opening dictionaries never affects responsivity as dictionaries are
+        // asynchronously loaded.
+        initOrResetSuggestForSettingsValues(mInputLogic.mSuggest, locale, currentSettingsValues);
+    }
+
+    private void initOrResetSuggestForSettingsValues(final Suggest oldSuggest,
+            final Locale locale, final SettingsValues settingsValues) {
+        if (!mHandler.hasPendingReopenDictionaries() && oldSuggest != null) {
             // May need to reset dictionaries depending on the user settings.
             final DictionaryFacilitatorForSuggest oldDictionaryFacilitator =
-                    suggest.mDictionaryFacilitator;
+                    oldSuggest.mDictionaryFacilitator;
+            if (!oldDictionaryFacilitator.needsToBeRecreated(locale, settingsValues)) {
+                // Continue to use the same dictionary facilitator if no configuration has changed.
+                refreshPersonalizationDictionarySession();
+                return;
+            }
             final DictionaryFacilitatorForSuggest dictionaryFacilitator =
-                    new DictionaryFacilitatorForSuggest(currentSettingsValues,
-                            oldDictionaryFacilitator);
+                    new DictionaryFacilitatorForSuggest(settingsValues, oldDictionaryFacilitator);
             // Create Suggest instance with the new dictionary facilitator.
-            resetSuggest(new Suggest(suggest /* oldSuggest */, dictionaryFacilitator));
-        } else if (suggest == null) {
-            initSuggestForLocale(locale);
+            replaceSuggest(new Suggest(oldSuggest, dictionaryFacilitator));
+        } else if (oldSuggest == null) {
+            initSuggestForLocale(oldSuggest, locale);
         }
     }
 
@@ -610,13 +614,13 @@
         } else {
             subtypeLocale = switcherSubtypeLocale;
         }
-        initSuggestForLocale(subtypeLocale);
+        initSuggestForLocale(mInputLogic.mSuggest, subtypeLocale);
     }
 
-    private void initSuggestForLocale(final Locale locale) {
+    private void initSuggestForLocale(final Suggest oldSuggest, final Locale locale) {
         final SettingsValues settingsValues = mSettings.getCurrent();
         final DictionaryFacilitatorForSuggest oldDictionaryFacilitator =
-                (mInputLogic.mSuggest == null) ? null : mInputLogic.mSuggest.mDictionaryFacilitator;
+                (oldSuggest == null) ? null : oldSuggest.mDictionaryFacilitator;
         // Creates new dictionary facilitator for the new locale.
         final DictionaryFacilitatorForSuggest dictionaryFacilitator =
                 new DictionaryFacilitatorForSuggest(this /* context */, locale, settingsValues,
@@ -625,7 +629,7 @@
         if (settingsValues.mCorrectionEnabled) {
             newSuggest.setAutoCorrectionThreshold(settingsValues.mAutoCorrectionThreshold);
         }
-        resetSuggest(newSuggest);
+        replaceSuggest(newSuggest);
     }
 
     /* package private */ void resetSuggestMainDict() {
@@ -633,10 +637,10 @@
                 mInputLogic.mSuggest.mDictionaryFacilitator;
         final DictionaryFacilitatorForSuggest dictionaryFacilitator =
                 new DictionaryFacilitatorForSuggest(this /* listener */, oldDictionaryFacilitator);
-        resetSuggest(new Suggest(mInputLogic.mSuggest /* oldSuggest */, dictionaryFacilitator));
+        replaceSuggest(new Suggest(mInputLogic.mSuggest /* oldSuggest */, dictionaryFacilitator));
     }
 
-    private void resetSuggest(final Suggest newSuggest) {
+    private void replaceSuggest(final Suggest newSuggest) {
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
             ResearchLogger.getInstance().initDictionary(newSuggest.mDictionaryFacilitator);
         }
@@ -1733,7 +1737,7 @@
         final DictionaryFacilitatorForSuggest dictionaryFacilitator =
                 new DictionaryFacilitatorForSuggest(this, locale, mSettings.getCurrent(),
                         this /* listener */, oldDictionaryFacilitator);
-        resetSuggest(new Suggest(locale, dictionaryFacilitator));
+        replaceSuggest(new Suggest(locale, dictionaryFacilitator));
     }
 
     // DO NOT USE THIS for any other purpose than testing.
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index f0e7d2f..1747eee 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -59,6 +59,7 @@
     // Locale used for upper- and title-casing words
     public final Locale mLocale;
 
+    // TODO: Move dictionaryFacilitator constructing logics from LatinIME to Suggest.
     public Suggest(final Locale locale,
             final DictionaryFacilitatorForSuggest dictionaryFacilitator) {
         mLocale = locale;
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
index c87dd15..11d3692 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
@@ -42,8 +42,6 @@
     public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch";
     public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
     public static final String PREF_STATISTICS_LOGGING = "enable_logging";
-    public static final String PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG =
-            "use_only_personalization_dictionary_for_debug";
     public static final String PREF_KEY_PREVIEW_SHOW_UP_START_SCALE =
             "pref_key_preview_show_up_start_scale";
     public static final String PREF_KEY_PREVIEW_DISMISS_END_SCALE =
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 77968f7..50fbbb1 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -84,7 +84,6 @@
     public final float mAutoCorrectionThreshold;
     public final boolean mCorrectionEnabled;
     public final int mSuggestionVisibility;
-    public final boolean mUseOnlyPersonalizationDictionaryForDebug;
     public final int mDisplayOrientation;
     private final AsyncResultHolder<AppWorkaroundsUtils> mAppWorkarounds;
 
@@ -168,8 +167,6 @@
                 prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_SCALE,
                 ResourceUtils.getFloatFromFraction(
                         res, R.fraction.config_key_preview_dismiss_end_scale));
-        mUseOnlyPersonalizationDictionaryForDebug = prefs.getBoolean(
-                DebugSettings.PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG, false);
         mDisplayOrientation = res.getConfiguration().orientation;
         mAppWorkarounds = new AsyncResultHolder<AppWorkaroundsUtils>();
         final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo(
@@ -390,8 +387,6 @@
         sb.append("" + mCorrectionEnabled);
         sb.append("\n   mSuggestionVisibility = ");
         sb.append("" + mSuggestionVisibility);
-        sb.append("\n   mUseOnlyPersonalizationDictionaryForDebug = ");
-        sb.append("" + mUseOnlyPersonalizationDictionaryForDebug);
         sb.append("\n   mDisplayOrientation = ");
         sb.append("" + mDisplayOrientation);
         sb.append("\n   mAppWorkarounds = ");
diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
index ef1d0f4..2bb30a2 100644
--- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
@@ -27,6 +27,7 @@
 import android.util.Log;
 import android.view.inputmethod.InputMethodSubtype;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
 import com.android.inputmethod.latin.R;
 
@@ -42,6 +43,7 @@
         // This utility class is not publicly instantiable.
     }
 
+    @UsedForTesting
     public static boolean isAdditionalSubtype(final InputMethodSubtype subtype) {
         return subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE);
     }
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
index 9939a43..cd97fc9 100644
--- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
@@ -43,6 +43,7 @@
 public class KeyboardLayoutSetTestsBase extends AndroidTestCase {
     private static final int NUMBER_OF_SUBTYPES = 63;
     private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 40;
+    private static final int NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES = 2;
 
     private static final KeyboardTheme DEFAULT_KEYBOARD_THEME =
             KeyboardSwitcher.KEYBOARD_THEMES[KeyboardSwitcher.THEME_INDEX_DEFAULT];
@@ -51,6 +52,8 @@
     private final ArrayList<InputMethodSubtype> mAllSubtypesList = CollectionUtils.newArrayList();
     private final ArrayList<InputMethodSubtype> mAsciiCapableSubtypesList =
             CollectionUtils.newArrayList();
+    private final ArrayList<InputMethodSubtype> mAdditionalSubtypesList =
+            CollectionUtils.newArrayList();
 
     private Context mThemeContext;
     private int mScreenMetrics;
@@ -68,6 +71,10 @@
         final int subtypeCount = imi.getSubtypeCount();
         for (int index = 0; index < subtypeCount; index++) {
             final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+            if (AdditionalSubtypeUtils.isAdditionalSubtype(subtype)) {
+                mAdditionalSubtypesList.add(subtype);
+                continue;
+            }
             mAllSubtypesList.add(subtype);
             if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) {
                 mAsciiCapableSubtypesList.add(subtype);
@@ -84,7 +91,7 @@
         final StringBuilder sb = new StringBuilder();
         for (int index = 0; index < subtypeList.size(); index++) {
             final InputMethodSubtype subtype = subtypeList.get(index);
-            sb.append((index + 1) + ": ");
+            sb.append(index + ": ");
             sb.append(SubtypeLocaleUtils.getSubtypeNameForLogging(subtype));
             sb.append("\n");
         }
@@ -101,6 +108,11 @@
                 NUMBER_OF_ASCII_CAPABLE_SUBTYPES, mAsciiCapableSubtypesList.size());
     }
 
+    public final void testAdditionalSubtypesCount() {
+        assertEquals(toString(mAdditionalSubtypesList),
+                NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES, mAdditionalSubtypesList.size());
+    }
+
     protected final InputMethodSubtype getSubtype(final Locale locale,
             final String keyboardLayout) {
         for (final InputMethodSubtype subtype : mAllSubtypesList) {