Merge "Implement Ver4PatriciaTriePolicy::flush()."
diff --git a/java/res/layout/more_keys_keyboard.xml b/java/res/layout/more_keys_keyboard.xml
index 6637117..f3795af 100644
--- a/java/res/layout/more_keys_keyboard.xml
+++ b/java/res/layout/more_keys_keyboard.xml
@@ -22,11 +22,9 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:orientation="horizontal"
-    style="?attr/moreKeysKeyboardContainerStyle"
+    android:orientation="vertical"
 >
     <com.android.inputmethod.keyboard.MoreKeysKeyboardView
-        xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
         android:id="@+id/more_keys_keyboard_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
diff --git a/java/res/layout/more_suggestions.xml b/java/res/layout/more_suggestions.xml
index 8659f07..2643258 100644
--- a/java/res/layout/more_suggestions.xml
+++ b/java/res/layout/more_suggestions.xml
@@ -22,8 +22,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:orientation="horizontal"
-    style="?attr/moreKeysKeyboardContainerStyle"
+    android:orientation="vertical"
 >
     <com.android.inputmethod.latin.suggestions.MoreSuggestionsView
         xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
diff --git a/java/res/values-sw540dp/config.xml b/java/res/values-sw540dp/config.xml
index 9260988..ac992ea 100644
--- a/java/res/values-sw540dp/config.xml
+++ b/java/res/values-sw540dp/config.xml
@@ -19,7 +19,6 @@
 -->
 
 <resources>
-    <bool name="config_enable_show_voice_key_option">false</bool>
     <bool name="config_enable_show_option_of_key_preview_popup">false</bool>
     <!-- Whether or not Popup on key press is enabled by default -->
     <bool name="config_default_key_preview_popup">false</bool>
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml
new file mode 100644
index 0000000..e72e494
--- /dev/null
+++ b/java/res/values-sw600dp/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+    <bool name="config_enable_show_voice_key_option">false</bool>
+</resources>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index a7425f1..f2dc473 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -32,8 +32,6 @@
         <attr name="moreKeysKeyboardStyle" format="reference" />
         <!-- MoreKeysKeyboardView style -->
         <attr name="moreKeysKeyboardViewStyle" format="reference" />
-        <!-- MoreKeysKeyboardView container style -->
-        <attr name="moreKeysKeyboardContainerStyle" format="reference" />
         <!-- Suggestions strip style -->
         <attr name="suggestionStripViewStyle" format="reference" />
         <!-- Suggestion word style -->
diff --git a/java/res/values/themes-gb.xml b/java/res/values/themes-gb.xml
index f52695f..2932143 100644
--- a/java/res/values/themes-gb.xml
+++ b/java/res/values/themes-gb.xml
@@ -26,7 +26,6 @@
         <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.GB</item>
         <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.GB</item>
         <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.GB</item>
-        <item name="moreKeysKeyboardContainerStyle">@style/MoreKeysKeyboardContainer.GB</item>
         <item name="suggestionStripViewStyle">@style/SuggestionStripView.GB</item>
         <item name="suggestionWordStyle">@style/SuggestionWord.GB</item>
     </style>
@@ -118,17 +117,12 @@
         name="MoreKeysKeyboardView.GB"
         parent="KeyboardView.GB"
     >
-        <item name="android:background">@null</item>
+        <item name="android:background">@drawable/keyboard_popup_panel_background_gb</item>
         <item name="keyBackground">@drawable/btn_keyboard_key_popup_gb</item>
         <item name="keyTypeface">normal</item>
         <item name="verticalCorrection">@dimen/more_keys_keyboard_vertical_correction_gb</item>
     </style>
     <style
-        name="MoreKeysKeyboardContainer.GB"
-    >
-        <item name="android:background">@drawable/keyboard_popup_panel_background_gb</item>
-    </style>
-    <style
         name="SuggestionStripView.GB"
         parent="SuggestionStripView"
     >
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index 432ad51..e854cd1 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -26,7 +26,6 @@
         <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.ICS</item>
         <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.ICS</item>
         <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.ICS</item>
-        <item name="moreKeysKeyboardContainerStyle">@style/MoreKeysKeyboardContainer.ICS</item>
         <item name="suggestionStripViewStyle">@style/SuggestionStripView.ICS</item>
         <item name="suggestionWordStyle">@style/SuggestionWord.ICS</item>
     </style>
@@ -96,17 +95,12 @@
         name="MoreKeysKeyboardView.ICS"
         parent="KeyboardView.ICS"
     >
-        <item name="android:background">@null</item>
+        <item name="android:background">@drawable/keyboard_popup_panel_background_ics</item>
         <item name="keyBackground">@drawable/btn_keyboard_key_popup_ics</item>
         <item name="keyTypeface">normal</item>
         <item name="verticalCorrection">@dimen/more_keys_keyboard_vertical_correction_holo</item>
     </style>
     <style
-        name="MoreKeysKeyboardContainer.ICS"
-    >
-        <item name="android:background">@drawable/keyboard_popup_panel_background_ics</item>
-    </style>
-    <style
         name="SuggestionStripView.ICS"
         parent="SuggestionStripView"
     >
diff --git a/java/res/values/themes-klp.xml b/java/res/values/themes-klp.xml
index a373001..c404b6a 100644
--- a/java/res/values/themes-klp.xml
+++ b/java/res/values/themes-klp.xml
@@ -26,7 +26,6 @@
         <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.KLP</item>
         <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.KLP</item>
         <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.KLP</item>
-        <item name="moreKeysKeyboardContainerStyle">@style/MoreKeysKeyboardContainer.KLP</item>
         <item name="suggestionStripViewStyle">@style/SuggestionStripView.KLP</item>
         <item name="suggestionWordStyle">@style/SuggestionWord.KLP</item>
     </style>
@@ -96,17 +95,12 @@
         name="MoreKeysKeyboardView.KLP"
         parent="KeyboardView.KLP"
     >
-        <item name="android:background">@null</item>
+        <item name="android:background">@drawable/keyboard_popup_panel_background_klp</item>
         <item name="keyBackground">@drawable/btn_keyboard_key_popup_klp</item>
         <item name="keyTypeface">normal</item>
         <item name="verticalCorrection">@dimen/more_keys_keyboard_vertical_correction_holo</item>
     </style>
     <style
-        name="MoreKeysKeyboardContainer.KLP"
-    >
-        <item name="android:background">@drawable/keyboard_popup_panel_background_klp</item>
-    </style>
-    <style
         name="SuggestionStripView.KLP"
         parent="SuggestionStripView"
     >
diff --git a/java/res/xml/prefs_for_debug.xml b/java/res/xml/prefs_for_debug.xml
index 8d9508e..124c443 100644
--- a/java/res/xml/prefs_for_debug.xml
+++ b/java/res/xml/prefs_for_debug.xml
@@ -58,6 +58,12 @@
         android:persistent="true"
         android:title="@string/prefs_use_only_personalization_dictionary" />
 
+    <CheckBoxPreference
+        android:defaultValue="false"
+        android:key="boost_personalization_dictionary_for_debug"
+        android:persistent="true"
+        android:title="@string/prefs_boost_personalization_dictionary" />
+
     <PreferenceScreen
         android:key="read_external_dictionary"
         android:title="@string/prefs_read_external_dictionary" />
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index d059cc8..a0e4e54 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -288,7 +288,7 @@
     private void runGCIfRequiredInternalLocked(final boolean mindsBlockByGC) {
         // Calls to needsToRunGC() need to be serialized.
         if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) {
-            if (setIsRegeneratingIfNotRegenerating()) {
+            if (setProcessingLargeTaskIfNot()) {
                 // Run GC after currently existing time sensitive operations.
                 getExecutor(mFilename).executePrioritized(new Runnable() {
                     @Override
@@ -296,7 +296,7 @@
                         try {
                             mBinaryDictionary.flushWithGC();
                         } finally {
-                            mFilenameDictionaryUpdateController.mIsRegenerating.set(false);
+                            mFilenameDictionaryUpdateController.mProcessingLargeTask.set(false);
                         }
                     }
                 });
@@ -359,13 +359,76 @@
         });
     }
 
+    public interface AddMultipleDictionaryEntriesCallback {
+        public void onFinished();
+    }
+
+    public static class LanguageModelParam {
+        public final String mWord0;
+        public final String mWord1;
+        public final boolean mIsValid;
+        public final int mFrequency;
+        public final int mBigramFrequency;
+        public LanguageModelParam(final String word0, final String word1, final boolean isValid,
+                final int frequency, final int bigramFrequency) {
+            mWord0 = word0;
+            mWord1 = word1;
+            mIsValid = isValid;
+            mFrequency = frequency;
+            mBigramFrequency = bigramFrequency;
+        }
+    }
+
+    /**
+     * Dynamically add multiple entries to the dictionary.
+     */
+    protected void addMultipleDictionaryEntriesDynamically(
+            final ArrayList<LanguageModelParam> languageModelParams,
+            final AddMultipleDictionaryEntriesCallback callback) {
+        if (!mIsUpdatable) {
+            Log.w(TAG, "addMultipleDictionaryEntriesDynamically is called for non-updatable " +
+                    "dictionary: " + mFilename);
+            return;
+        }
+        getExecutor(mFilename).execute(new Runnable() {
+            @Override
+            public void run() {
+                final boolean locked = setProcessingLargeTaskIfNot();
+                try {
+                    for (final LanguageModelParam languageModelParam : languageModelParams) {
+                        if (languageModelParam.mWord1 == null) {
+                            continue;
+                        }
+                        if (mBinaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+                            mBinaryDictionary.flushWithGC();
+                        }
+                        mBinaryDictionary.addUnigramWord(languageModelParam.mWord1,
+                                languageModelParam.mFrequency);
+                        if (languageModelParam.mWord0 != null
+                                && !languageModelParam.mWord0.equals(languageModelParam.mWord1)) {
+                            mBinaryDictionary.addBigramWords(languageModelParam.mWord0,
+                                    languageModelParam.mWord1, languageModelParam.mBigramFrequency);
+                        }
+                    }
+                } finally {
+                    if (callback != null) {
+                        callback.onFinished();
+                    }
+                    if (locked) {
+                        mFilenameDictionaryUpdateController.mProcessingLargeTask.set(false);
+                    }
+                }
+            }
+        });
+    }
+
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
             final String prevWord, final ProximityInfo proximityInfo,
             final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
             final int sessionId) {
         reloadDictionaryIfRequired();
-        if (isRegenerating()) {
+        if (processingLargeTask()) {
             return null;
         }
         final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder =
@@ -402,7 +465,7 @@
     }
 
     protected boolean isValidWordInner(final String word) {
-        if (isRegenerating()) {
+        if (processingLargeTask()) {
             return false;
         }
         final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
@@ -524,7 +587,7 @@
      */
     public final void reloadDictionaryIfRequired() {
         if (!isReloadRequired()) return;
-        if (setIsRegeneratingIfNotRegenerating()) {
+        if (setProcessingLargeTaskIfNot()) {
             reloadDictionary();
         }
     }
@@ -536,13 +599,14 @@
         return mBinaryDictionary == null || mPerInstanceDictionaryUpdateController.isOutOfDate();
     }
 
-    private boolean isRegenerating() {
-        return mFilenameDictionaryUpdateController.mIsRegenerating.get();
+    private boolean processingLargeTask() {
+        return mFilenameDictionaryUpdateController.mProcessingLargeTask.get();
     }
 
-    // Returns whether the dictionary can be regenerated.
-    private boolean setIsRegeneratingIfNotRegenerating() {
-        return mFilenameDictionaryUpdateController.mIsRegenerating.compareAndSet(
+    // Returns whether the dictionary is being used for a large task. If true, we should not use
+    // this dictionary for latency sensitive operations.
+    private boolean setProcessingLargeTaskIfNot() {
+        return mFilenameDictionaryUpdateController.mProcessingLargeTask.compareAndSet(
                 false /* expect */ , true /* update */);
     }
 
@@ -592,7 +656,7 @@
                     }
                     mPerInstanceDictionaryUpdateController.mLastUpdateTime = time;
                 } finally {
-                    mFilenameDictionaryUpdateController.mIsRegenerating.set(false);
+                    mFilenameDictionaryUpdateController.mProcessingLargeTask.set(false);
                 }
             }
         });
@@ -619,13 +683,13 @@
     }
 
     /**
-     * For tracking whether the dictionary is out of date and the dictionary is regenerating.
-     * Can be shared across multiple dictionary instances that access the same filename.
+     * For tracking whether the dictionary is out of date and the dictionary is used in a large
+     * task. Can be shared across multiple dictionary instances that access the same filename.
      */
     private static class DictionaryUpdateController {
         public volatile long mLastUpdateTime = 0;
         public volatile long mLastUpdateRequestTime = 0;
-        public volatile AtomicBoolean mIsRegenerating = new AtomicBoolean();
+        public volatile AtomicBoolean mProcessingLargeTask = new AtomicBoolean();
 
         public boolean isOutOfDate() {
             return (mLastUpdateRequestTime > mLastUpdateTime);
@@ -666,7 +730,7 @@
                     mBinaryDictionary.flushWithGC();
                     r.run();
                 } finally {
-                    mFilenameDictionaryUpdateController.mIsRegenerating.set(false);
+                    mFilenameDictionaryUpdateController.mProcessingLargeTask.set(false);
                 }
             }
         });
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 0a4c7a5..53f04b1 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -432,7 +432,8 @@
             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);
             }
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index e7a25d2..42bd760 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -113,6 +113,18 @@
         return false;
     }
 
+    public void addMultipleDictionaryEntriesToDictionary(
+            final ArrayList<LanguageModelParam> languageModelParams,
+            final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) {
+        if (languageModelParams == null || languageModelParams.isEmpty()) {
+            if (callback != null) {
+                callback.onFinished();
+            }
+            return;
+        }
+        addMultipleDictionaryEntriesDynamically(languageModelParams, callback);
+    }
+
     /**
      * Pair will be added to the decaying dictionary.
      *
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
index a86f6e5..1f46f5b 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 
+import com.android.inputmethod.latin.ExpandableBinaryDictionary;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
@@ -113,6 +115,20 @@
         dictionary.addToDictionary(word0, word1, isValid);
     }
 
+    // TODO: Support multi locale.
+    public void addMultipleDictionaryEntriesToPersonalizationDictionary(
+            final ArrayList<ExpandableBinaryDictionary.LanguageModelParam> languageModelParams,
+            final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) {
+        final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary();
+        if (dictionary == null) {
+            if (callback != null) {
+                callback.onFinished();
+            }
+            return;
+        }
+        dictionary.addMultipleDictionaryEntriesToDictionary(languageModelParams, callback);
+    }
+
     // Bulk import
     // TODO: Support multi locale to add bigram
     public void addBigramsToPersonalizationDictionary(
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
index da1fb73..d060485 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
@@ -112,8 +112,7 @@
                 updateDebugMode();
                 mServiceNeedsRestart = true;
             }
-        } else if (key.equals(PREF_FORCE_NON_DISTINCT_MULTITOUCH)
-                || key.equals(PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG)) {
+        } else if (key.equals(PREF_FORCE_NON_DISTINCT_MULTITOUCH)) {
             mServiceNeedsRestart = true;
         }
     }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_policy.cpp
index 6b58f5f..b25589c 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_policy.cpp
@@ -42,7 +42,7 @@
 const char *const DynamicPatriciaTriePolicy::MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT";
 const char *const DynamicPatriciaTriePolicy::SET_NEEDS_TO_DECAY_FOR_TESTING_QUERY =
         "SET_NEEDS_TO_DECAY_FOR_TESTING";
-const int DynamicPatriciaTriePolicy::MAX_DICT_EXTENDED_REGION_SIZE = 1024 * 1024;
+const int DynamicPatriciaTriePolicy::MAX_DICT_EXTENDED_REGION_SIZE = 32 * 1024;
 const int DynamicPatriciaTriePolicy::MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS =
         DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE - 1024;