merge in jb-release history after reset to jb-dev
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index e0b32ad..6a9adb1 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -134,7 +134,7 @@
     <string name="save" msgid="7646738597196767214">"Desa"</string>
     <string name="subtype_locale" msgid="8576443440738143764">"Idioma"</string>
     <string name="keyboard_layout_set" msgid="4309233698194565609">"Disseny"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"El teu estil d\'entrada personalitzat ha d\'estar activat perquè puguis començar a fer-lo servir. Vols activar-lo ara?"</string>
+    <string name="custom_input_style_note_message" msgid="8826731320846363423">"El teu estil d\'entrada personalitzat ha d\'estar activat per poder fer-lo servir. Vols activar-lo ara?"</string>
     <string name="enable" msgid="5031294444630523247">"Activa"</string>
     <string name="not_now" msgid="6172462888202790482">"Ara no"</string>
     <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ja existeix aquest estil d\'entrada: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 03a72a9..7ffd368 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -134,10 +134,10 @@
     <string name="save" msgid="7646738597196767214">"Uložit"</string>
     <string name="subtype_locale" msgid="8576443440738143764">"Jazyk"</string>
     <string name="keyboard_layout_set" msgid="4309233698194565609">"Rozvržení"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Vlastní styl zadávání musíte nejdříve povolit. Povolit?"</string>
+    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Vlastní styl vstupu musíte nejdříve povolit. Povolit?"</string>
     <string name="enable" msgid="5031294444630523247">"Povolit"</string>
     <string name="not_now" msgid="6172462888202790482">"Teď ne"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Tento styl zadávání již existuje: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
+    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Stejný styl vstupu již existuje: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Režim studie použitelnosti"</string>
     <string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Délka vibrace při stisku klávesy"</string>
     <string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Hlasitost při stisknutí klávesy"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 3946d43..95d2441 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -134,7 +134,7 @@
     <string name="save" msgid="7646738597196767214">"Αποθήκευση"</string>
     <string name="subtype_locale" msgid="8576443440738143764">"Γλώσσα"</string>
     <string name="keyboard_layout_set" msgid="4309233698194565609">"Διάταξη"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Απαιτείται ενεργοποίηση του προσαρμοσμένου στυλ εισόδου για χρήση. Ενεργοποίηση τώρα;"</string>
+    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Απαιτείται ενεργοποίηση προσαρμ. στυλ εισόδου. Να γίνει τώρα;"</string>
     <string name="enable" msgid="5031294444630523247">"Ενεργοποίηση"</string>
     <string name="not_now" msgid="6172462888202790482">"Όχι τώρα"</string>
     <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Το ίδιο στυλ εισόδου υπάρχει ήδη: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index dd2f247..bae0a6d 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -134,7 +134,7 @@
     <string name="save" msgid="7646738597196767214">"Guardar"</string>
     <string name="subtype_locale" msgid="8576443440738143764">"Idioma"</string>
     <string name="keyboard_layout_set" msgid="4309233698194565609">"Diseño"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Debes activar tu estilo de entrada personalizado para poder utilizarlo. ¿Quieres activarlo ahora?"</string>
+    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Debes activar estilo de entrada personalizado para utilizarlo."</string>
     <string name="enable" msgid="5031294444630523247">"Activar"</string>
     <string name="not_now" msgid="6172462888202790482">"Ahora no"</string>
     <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ya existe el estilo de entrada <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 9192713..452da42 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -134,7 +134,7 @@
     <string name="save" msgid="7646738597196767214">"Mentés"</string>
     <string name="subtype_locale" msgid="8576443440738143764">"Nyelv"</string>
     <string name="keyboard_layout_set" msgid="4309233698194565609">"Elrendezés"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Az egyéni stílust eng. kell haszn. előtt. Engedélyezi most?"</string>
+    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Az egyéni stílust először engedélyezni kell. Engedélyezi most?"</string>
     <string name="enable" msgid="5031294444630523247">"Engedélyezés"</string>
     <string name="not_now" msgid="6172462888202790482">"Most nem"</string>
     <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ugyanez a bemenetstílus már létezik: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
diff --git a/java/res/values-land/keyboard-heights.xml b/java/res/values-land/keyboard-heights.xml
index 12c3e39..4ebeda5 100644
--- a/java/res/values-land/keyboard-heights.xml
+++ b/java/res/values-land/keyboard-heights.xml
@@ -18,10 +18,10 @@
 */
 -->
 
-<!-- Preferable keyboard height in absolute scale: 1.100in -->
 <resources>
     <!-- Build.HARDWARE,keyboard_height_in_dp -->
     <string-array name="keyboard_heights" translatable="false">
+    <!-- Preferable keyboard height in absolute scale: 1.100in -->
         <!-- Droid -->
         <item>sholes,194.3333</item>
         <!-- Nexus One -->
@@ -30,5 +30,8 @@
         <item>herring,171.9385</item>
         <!-- Galaxy Nexus -->
         <item>tuna,173.4207</item>
+    <!-- Preferable keyboard height in absolute scale: 45.0mm -->
+        <!-- Xoom -->
+        <item>stingray,265.4378</item>
     </string-array>
 </resources>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index acb942e..f9aa372 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -134,7 +134,7 @@
     <string name="save" msgid="7646738597196767214">"Uložiť"</string>
     <string name="subtype_locale" msgid="8576443440738143764">"Jazyk"</string>
     <string name="keyboard_layout_set" msgid="4309233698194565609">"Rozloženie"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Pred použitím vlastného štýlu vstupu ho musíte povoliť. Chcete ho povoliť teraz?"</string>
+    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Pred použitím vlastného štýlu vstupu ho musíte povoliť. Chcete ho povoliť?"</string>
     <string name="enable" msgid="5031294444630523247">"Povoliť"</string>
     <string name="not_now" msgid="6172462888202790482">"Teraz nie"</string>
     <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Rovnaký štýl vstupu už existuje: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
diff --git a/java/res/values-sw600dp-land/keyboard-heights.xml b/java/res/values-sw600dp-land/keyboard-heights.xml
deleted file mode 100644
index 93f9824..0000000
--- a/java/res/values-sw600dp-land/keyboard-heights.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<!-- Preferable keyboard height in absolute scale: 45.0mm -->
-<resources>
-    <!-- Build.HARDWARE,keyboard_height_in_dp -->
-    <string-array name="keyboard_heights" translatable="false">
-        <!-- Xoom -->
-        <item>stingray,265.4378</item>
-    </string-array>
-</resources>
diff --git a/java/res/values-sw600dp/keyboard-heights.xml b/java/res/values-sw600dp/keyboard-heights.xml
deleted file mode 100644
index 77e52be..0000000
--- a/java/res/values-sw600dp/keyboard-heights.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<!-- Preferable keyboard height in absolute scale: 48.0mm -->
-<resources>
-    <!-- Build.HARDWARE,keyboard_height_in_dp -->
-    <string-array name="keyboard_heights" translatable="false">
-        <!-- Xoom -->
-        <item>stingray,283.1337</item>
-    </string-array>
-</resources>
diff --git a/java/res/values-sw768dp/keyboard-heights.xml b/java/res/values-sw768dp/keyboard-heights.xml
deleted file mode 100644
index 77e52be..0000000
--- a/java/res/values-sw768dp/keyboard-heights.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<!-- Preferable keyboard height in absolute scale: 48.0mm -->
-<resources>
-    <!-- Build.HARDWARE,keyboard_height_in_dp -->
-    <string-array name="keyboard_heights" translatable="false">
-        <!-- Xoom -->
-        <item>stingray,283.1337</item>
-    </string-array>
-</resources>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index 8b4d5ee..d8980f2 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -134,7 +134,7 @@
     <string name="save" msgid="7646738597196767214">"บันทึก"</string>
     <string name="subtype_locale" msgid="8576443440738143764">"ภาษา"</string>
     <string name="keyboard_layout_set" msgid="4309233698194565609">"การจัดวาง"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"ต้องเปิดใช้รูปแบบการป้อนที่กำหนดเองก่อนเริ่มใช้ เปิดใช้หรือไม่"</string>
+    <string name="custom_input_style_note_message" msgid="8826731320846363423">"ต้องเปิดใช้รูปแบบอินพุตที่กำหนดเองก่อน เปิดใช้เลยไหม"</string>
     <string name="enable" msgid="5031294444630523247">"เปิดใช้งาน"</string>
     <string name="not_now" msgid="6172462888202790482">"ข้ามไปก่อน"</string>
     <string name="custom_input_style_already_exists" msgid="8008728952215449707">"รูปแบบการป้อนข้อมูลเดียวกันนี้มีอยู่แล้ว: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index a6bb0c9..06728cc 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -112,7 +112,7 @@
     <string name="language_selection_title" msgid="1651299598555326750">"输入语言"</string>
     <string name="select_language" msgid="3693815588777926848">"输入语言"</string>
     <string name="hint_add_to_dictionary" msgid="573678656946085380">"再次触摸即可保存"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"提供字典"</string>
+    <string name="has_dictionary" msgid="6071847973466625007">"有可用词典"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"启用用户反馈"</string>
     <string name="prefs_description_log" msgid="5827825607258246003">"自动向 Google 发送使用情况统计信息和崩溃报告,帮助改进该输入法编辑器。"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"键盘主题"</string>
diff --git a/java/res/values/keyboard-heights.xml b/java/res/values/keyboard-heights.xml
index 7d5b583..7d85994 100644
--- a/java/res/values/keyboard-heights.xml
+++ b/java/res/values/keyboard-heights.xml
@@ -18,10 +18,10 @@
 */
 -->
 
-<!-- Preferable keyboard height in absolute scale: 1.285in -->
 <resources>
     <!-- Build.HARDWARE,keyboard_height_in_dp -->
     <string-array name="keyboard_heights" translatable="false">
+    <!-- Preferable keyboard height in absolute scale: 1.285in -->
         <!-- Droid -->
         <item>sholes,227.0167</item>
         <!-- Nexus One -->
@@ -30,5 +30,8 @@
         <item>herring,200.8554</item>
         <!-- Galaxy Nexus -->
         <item>tuna,202.5869</item>
+    <!-- Preferable keyboard height in absolute scale: 48.0mm -->
+        <!-- Xoom -->
+        <item>stingray,283.1337</item>
     </string-array>
 </resources>
diff --git a/java/res/values-sw768dp-land/keyboard-heights.xml b/java/res/values/phantom_sudden_move_event_device_list.xml
similarity index 70%
rename from java/res/values-sw768dp-land/keyboard-heights.xml
rename to java/res/values/phantom_sudden_move_event_device_list.xml
index 692c5a0..63d12e9 100644
--- a/java/res/values-sw768dp-land/keyboard-heights.xml
+++ b/java/res/values/phantom_sudden_move_event_device_list.xml
@@ -17,12 +17,10 @@
 ** limitations under the License.
 */
 -->
-
-<!-- Preferable keyboard height in absolute scale: 58.0mm -->
 <resources>
-    <!-- Build.HARDWARE,keyboard_height_in_dp -->
-    <string-array name="keyboard_heights" translatable="false">
-        <!-- Xoom -->
-        <item>stingray,342.1198</item>
+    <string-array name="phantom_sudden_move_event_device_list" translatable="false">
+        <!-- "Build.HARDWARE,true" that needs "phantom sudden move event" hack.
+             See {@link com.android.inputmethod.keyboard.PointerTracker}. -->
+        <item>stingray,true</item> <!-- Xoom -->
     </string-array>
 </resources>
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index b159993..e917a81 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -338,8 +338,10 @@
 
         mHasDistinctMultitouch = context.getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
-
-        PointerTracker.init(mHasDistinctMultitouch);
+        final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
+                Utils.getDeviceOverrideValue(context.getResources(),
+                        R.array.phantom_sudden_move_event_device_list, "false"));
+        PointerTracker.init(mHasDistinctMultitouch, needsPhantomSuddenMoveEventHack);
 
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.LatinKeyboardView, defStyle, R.style.LatinKeyboardView);
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 59f53fc..0f8f6cd 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -108,6 +108,7 @@
     // Parameters for pointer handling.
     private static LatinKeyboardView.PointerTrackerParams sParams;
     private static int sTouchNoiseThresholdDistanceSquared;
+    private static boolean sNeedsPhantomSuddenMoveEventHack;
 
     private static final ArrayList<PointerTracker> sTrackers = new ArrayList<PointerTracker>();
     private static PointerTrackerQueue sPointerTrackerQueue;
@@ -162,12 +163,14 @@
     private static final KeyboardActionListener EMPTY_LISTENER =
             new KeyboardActionListener.Adapter();
 
-    public static void init(boolean hasDistinctMultitouch) {
+    public static void init(boolean hasDistinctMultitouch,
+            boolean needsPhantomSuddenMoveEventHack) {
         if (hasDistinctMultitouch) {
             sPointerTrackerQueue = new PointerTrackerQueue();
         } else {
             sPointerTrackerQueue = null;
         }
+        sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
 
         setParameters(LatinKeyboardView.PointerTrackerParams.DEFAULT);
     }
@@ -593,10 +596,13 @@
                     final int dx = x - lastX;
                     final int dy = y - lastY;
                     final int lastMoveSquared = dx * dx + dy * dy;
-                    if (lastMoveSquared >= mKeyQuarterWidthSquared) {
-                        if (DEBUG_MODE)
-                            Log.w(TAG, String.format("onMoveEvent: sudden move is translated to "
+                    if (sNeedsPhantomSuddenMoveEventHack
+                            && lastMoveSquared >= mKeyQuarterWidthSquared) {
+                        if (DEBUG_MODE) {
+                            Log.w(TAG, String.format("onMoveEvent:"
+                                    + " phantom sudden move event is translated to "
                                     + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y));
+                        }
                         if (ProductionFlag.IS_EXPERIMENTAL) {
                             ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
                         }
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index 0a09c84..34308df 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -214,6 +214,10 @@
             return false;
         }
         if (contactCount != sContactCountAtLastRebuild) {
+            if (DEBUG) {
+                Log.d(TAG, "Contact count changed: " + sContactCountAtLastRebuild + " to "
+                        + contactCount);
+            }
             return true;
         }
         // Check all contacts since it's not possible to find out which names have changed.
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 08f5854..c65404c 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -294,7 +294,7 @@
      */
     protected void loadBinaryDictionary() {
         if (DEBUG) {
-            Log.d(TAG, "Loading binary dictionary: request="
+            Log.d(TAG, "Loading binary dictionary: " + mFilename + " request="
                     + mSharedDictionaryController.mLastUpdateRequestTime + " update="
                     + mSharedDictionaryController.mLastUpdateTime);
         }
@@ -326,7 +326,7 @@
      */
     private void generateBinaryDictionary() {
         if (DEBUG) {
-            Log.d(TAG, "Generating binary dictionary: request="
+            Log.d(TAG, "Generating binary dictionary: " + mFilename + " request="
                     + mSharedDictionaryController.mLastUpdateRequestTime + " update="
                     + mSharedDictionaryController.mLastUpdateTime);
         }
@@ -371,7 +371,7 @@
         mLocalDictionaryController.mLastUpdateRequestTime = time;
         mSharedDictionaryController.mLastUpdateRequestTime = time;
         if (DEBUG) {
-            Log.d(TAG, "Reload request: request=" + time + " update="
+            Log.d(TAG, "Reload request: " + mFilename + ": request=" + time + " update="
                     + mSharedDictionaryController.mLastUpdateTime);
         }
     }
@@ -380,28 +380,46 @@
      * Reloads the dictionary if required. Reload will occur asynchronously in a separate thread.
      */
     void asyncReloadDictionaryIfRequired() {
+        if (!isReloadRequired()) return;
+        if (DEBUG) {
+            Log.d(TAG, "Starting AsyncReloadDictionaryTask: " + mFilename);
+        }
         new AsyncReloadDictionaryTask().start();
     }
 
     /**
-     * Reloads the dictionary if required. Access is controlled on a per dictionary file basis and
-     * supports concurrent calls from multiple instances that share the same dictionary file.
+     * Reloads the dictionary if required.
      */
     protected final void syncReloadDictionaryIfRequired() {
-        if (mBinaryDictionary != null && !mLocalDictionaryController.isOutOfDate()) {
-            return;
-        }
+        if (!isReloadRequired()) return;
+        syncReloadDictionaryInternal();
+    }
 
+    /**
+     * Returns whether a dictionary reload is required.
+     */
+    private boolean isReloadRequired() {
+        return mBinaryDictionary == null || mLocalDictionaryController.isOutOfDate();
+    }
+
+    /**
+     * Reloads the dictionary. Access is controlled on a per dictionary file basis and supports
+     * concurrent calls from multiple instances that share the same dictionary file.
+     */
+    private final void syncReloadDictionaryInternal() {
         // Ensure that only one thread attempts to read or write to the shared binary dictionary
         // file at the same time.
         mSharedDictionaryController.lock();
         try {
             final long time = SystemClock.uptimeMillis();
-            if (mSharedDictionaryController.isOutOfDate() || !dictionaryFileExists()) {
+            final boolean dictionaryFileExists = dictionaryFileExists();
+            if (mSharedDictionaryController.isOutOfDate() || !dictionaryFileExists) {
                 // If the shared dictionary file does not exist or is out of date, the first
                 // instance that acquires the lock will generate a new one.
-                if (hasContentChanged()) {
-                    // If the source content has changed, rebuild the binary dictionary.
+                if (hasContentChanged() || !dictionaryFileExists) {
+                    // If the source content has changed or the dictionary does not exist, rebuild
+                    // the binary dictionary. Empty dictionaries are supported (in the case where
+                    // loadDictionaryAsync() adds nothing) in order to provide a uniform framework.
                     mSharedDictionaryController.mLastUpdateTime = time;
                     generateBinaryDictionary();
                     loadBinaryDictionary();
@@ -435,7 +453,7 @@
     private class AsyncReloadDictionaryTask extends Thread {
         @Override
         public void run() {
-            syncReloadDictionaryIfRequired();
+            syncReloadDictionaryInternal();
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 08f3e84..c9ff0a5 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -151,6 +151,11 @@
 
         if (!VibratorUtils.getInstance(context).hasVibrator()) {
             generalSettings.removePreference(findPreference(PREF_VIBRATE_ON));
+            final PreferenceGroup advancedSettings =
+                    (PreferenceGroup) findPreference(PREF_ADVANCED_SETTINGS);
+            if (null != advancedSettings) { // Theoretically advancedSettings cannot be null
+                advancedSettings.removePreference(findPreference(PREF_VIBRATION_DURATION_SETTINGS));
+            }
         }
 
         final boolean showPopupOption = res.getBoolean(
diff --git a/native/jni/src/bigram_dictionary.cpp b/native/jni/src/bigram_dictionary.cpp
index eb4bf8d1..9ef024d 100644
--- a/native/jni/src/bigram_dictionary.cpp
+++ b/native/jni/src/bigram_dictionary.cpp
@@ -117,7 +117,7 @@
     do {
         bigramFlags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
         uint16_t bigramBuffer[MAX_WORD_LENGTH];
-        int unigramFreq;
+        int unigramFreq = 0;
         const int bigramPos = BinaryFormat::getAttributeAddressAndForwardPointer(root, bigramFlags,
                 &pos);
         const int length = BinaryFormat::getWordAtAddress(root, bigramPos, MAX_WORD_LENGTH,
diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp
index fe3f292..d56938d 100644
--- a/native/jni/src/correction.cpp
+++ b/native/jni/src/correction.cpp
@@ -109,6 +109,10 @@
     initEditDistance(mEditDistanceTable);
 }
 
+void Correction::resetCorrection() {
+    mTotalTraverseCount = 0;
+}
+
 void Correction::initCorrection(const ProximityInfo *pi, const int inputLength,
         const int maxDepth) {
     mProximityInfo = pi;
diff --git a/native/jni/src/correction.h b/native/jni/src/correction.h
index 1ac4b87..3300a84 100644
--- a/native/jni/src/correction.h
+++ b/native/jni/src/correction.h
@@ -94,6 +94,7 @@
     }
 
     Correction(const int typedLetterMultiplier, const int fullWordMultiplier);
+    void resetCorrection();
     void initCorrection(
             const ProximityInfo *pi, const int inputLength, const int maxWordLength);
     void initCorrectionState(const int rootPos, const int childCount, const bool traverseAll);
@@ -129,6 +130,10 @@
 
     bool needsToPrune() const;
 
+    int pushAndGetTotalTraverseCount() {
+        return ++mTotalTraverseCount;
+    }
+
     int getFreqForSplitMultipleWords(
             const int *freqArray, const int *wordLengthArray, const int wordCount,
             const bool isSpaceProximity, const unsigned short *word);
@@ -200,6 +205,8 @@
     int mTerminalOutputIndex;
     int mMaxErrors;
 
+    uint8_t mTotalTraverseCount;
+
     // The following arrays are state buffer.
     unsigned short mWord[MAX_WORD_LENGTH_INTERNAL];
     int mDistances[MAX_WORD_LENGTH_INTERNAL];
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 19f8434..b61ebd2 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -24,6 +24,7 @@
 #define AKLOGI ALOGI
 
 #define DUMP_WORD(word, length) do { dumpWord(word, length); } while(0)
+#define DUMP_WORD_INT(word, length) do { dumpWordInt(word, length); } while(0)
 
 static inline void dumpWord(const unsigned short* word, const int length) {
     static char charBuf[50];
@@ -35,10 +36,21 @@
     AKLOGI("[ %s ]", charBuf);
 }
 
+static inline void dumpWordInt(const int* word, const int length) {
+    static char charBuf[50];
+
+    for (int i = 0; i < length; ++i) {
+        charBuf[i] = word[i];
+    }
+    charBuf[length] = 0;
+    AKLOGI("i[ %s ]", charBuf);
+}
+
 #else
 #define AKLOGE(fmt, ...)
 #define AKLOGI(fmt, ...)
 #define DUMP_WORD(word, length)
+#define DUMP_WORD_INT(word, length)
 #endif
 
 #ifdef FLAG_DO_PROFILE
@@ -223,6 +235,10 @@
 #define SUB_QUEUE_MAX_COUNT 10
 #define SUB_QUEUE_MIN_WORD_LENGTH 4
 #define MULTIPLE_WORDS_SUGGESTION_MAX_WORDS 10
+// TODO: Remove this limitation
+#define MULTIPLE_WORDS_SUGGESTION_MAX_WORD_LENGTH 12
+// TODO: Remove this limitation
+#define MULTIPLE_WORDS_SUGGESTION_MAX_TOTAL_TRAVERSE_COUNT 110
 #define MULTIPLE_WORDS_DEMOTION_RATE 80
 #define MIN_INPUT_LENGTH_FOR_THREE_OR_MORE_WORDS_CORRECTION 6
 
diff --git a/native/jni/src/unigram_dictionary.cpp b/native/jni/src/unigram_dictionary.cpp
index efe9c4f..690d8dc 100644
--- a/native/jni/src/unigram_dictionary.cpp
+++ b/native/jni/src/unigram_dictionary.cpp
@@ -177,6 +177,7 @@
 
     queuePool->clearAll();
     Correction* masterCorrection = correction;
+    correction->resetCorrection();
     if (BinaryFormat::REQUIRES_GERMAN_UMLAUT_PROCESSING & FLAGS)
     { // Incrementally tune the word and try all possibilities
         int codesBuffer[getCodesBufferSize(codes, codesSize)];
@@ -301,6 +302,7 @@
         const int *yCoordinates, const int *codes, const int inputLength, Correction *correction) {
     if (DEBUG_DICT) {
         AKLOGI("initSuggest");
+        DUMP_WORD_INT(codes, inputLength);
     }
     proximityInfo->setInputParams(codes, inputLength, xCoordinates, yCoordinates);
     const int maxDepth = min(inputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH);
@@ -324,6 +326,16 @@
         const int inputLength, const std::map<int, int> *bigramMap, const uint8_t *bigramFilter,
         Correction *correction, WordsPriorityQueuePool *queuePool,
         const bool doAutoCompletion, const int maxErrors, const int currentWordIndex) {
+    uint8_t totalTraverseCount = correction->pushAndGetTotalTraverseCount();
+    if (DEBUG_DICT) {
+        AKLOGI("Traverse count %d", totalTraverseCount);
+    }
+    if (totalTraverseCount > MULTIPLE_WORDS_SUGGESTION_MAX_TOTAL_TRAVERSE_COUNT) {
+        if (DEBUG_DICT) {
+            AKLOGI("Abort traversing %d", totalTraverseCount);
+        }
+        return;
+    }
     // TODO: Remove setCorrectionParams
     correction->setCorrectionParams(0, 0, 0,
             -1 /* spaceProximityPos */, -1 /* missingSpacePos */, useFullEditDistance,
@@ -410,7 +422,7 @@
     }
 }
 
-bool UnigramDictionary::getSubStringSuggestion(
+int UnigramDictionary::getSubStringSuggestion(
         ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates,
         const int *codes, const bool useFullEditDistance, Correction *correction,
         WordsPriorityQueuePool* queuePool, const int inputLength,
@@ -449,8 +461,9 @@
             }
         }
         WordsPriorityQueue* queue = queuePool->getSubQueue(currentWordIndex, inputWordLength);
-        if (!queue || queue->size() < 1) {
-            return false;
+        // TODO: Return the correct value depending on doAutoCompletion
+        if (!queue || queue->size() <= 0) {
+            return FLAG_MULTIPLE_SUGGEST_ABORT;
         }
         int score = 0;
         const float ns = queue->getHighestNormalizedScore(
@@ -463,7 +476,7 @@
         // threshold.
         if (ns < TWO_WORDS_CORRECTION_WITH_OTHER_ERROR_THRESHOLD
                 || nextWordLength < SUB_QUEUE_MIN_WORD_LENGTH) {
-            return false;
+            return FLAG_MULTIPLE_SUGGEST_SKIP;
         }
         freq = score >> (nextWordLength + TWO_WORDS_PLUS_OTHER_ERROR_CORRECTION_DEMOTION_DIVIDER);
     }
@@ -474,7 +487,7 @@
     }
     if (freq <= 0 || nextWordLength <= 0
             || MAX_WORD_LENGTH <= (outputWordStartPos + nextWordLength)) {
-        return false;
+        return FLAG_MULTIPLE_SUGGEST_SKIP;
     }
     for (int i = 0; i < nextWordLength; ++i) {
         outputWord[outputWordStartPos + i] = tempOutputWord[i];
@@ -491,7 +504,7 @@
 
     if ((inputWordStartPos + inputWordLength) < inputLength) {
         if (outputWordStartPos + nextWordLength >= MAX_WORD_LENGTH) {
-            return false;
+            return FLAG_MULTIPLE_SUGGEST_SKIP;
         }
         outputWord[tempOutputWordLength] = SPACE;
         if (outputWordLength) {
@@ -512,7 +525,7 @@
         }
         addWord(outputWord, tempOutputWordLength, pairFreq, queuePool->getMasterQueue());
     }
-    return true;
+    return FLAG_MULTIPLE_SUGGEST_CONTINUE;
 }
 
 void UnigramDictionary::getMultiWordsSuggestionRec(ProximityInfo *proximityInfo,
@@ -542,11 +555,18 @@
         // Current word
         int inputWordStartPos = startInputPos;
         int inputWordLength = i - startInputPos;
-        if (!getSubStringSuggestion(proximityInfo, xcoordinates, ycoordinates, codes,
-                useFullEditDistance, correction, queuePool, inputLength, hasAutoCorrectionCandidate,
-                startWordIndex, inputWordStartPos, inputWordLength, outputWordLength,
-                true /* not used */, freqArray, wordLengthArray, outputWord,
-                &tempOutputWordLength)) {
+        if (inputWordLength > MULTIPLE_WORDS_SUGGESTION_MAX_WORD_LENGTH) {
+            break;
+        }
+        const int suggestionFlag = getSubStringSuggestion(proximityInfo, xcoordinates, ycoordinates,
+                codes, useFullEditDistance, correction, queuePool, inputLength,
+                hasAutoCorrectionCandidate, startWordIndex, inputWordStartPos, inputWordLength,
+                outputWordLength, true /* not used */, freqArray, wordLengthArray, outputWord,
+                &tempOutputWordLength);
+        if (suggestionFlag == FLAG_MULTIPLE_SUGGEST_ABORT) {
+            // TODO: break here
+            continue;
+        } else if (suggestionFlag == FLAG_MULTIPLE_SUGGEST_SKIP) {
             continue;
         }
 
@@ -557,10 +577,11 @@
         // Missing space
         inputWordStartPos = i;
         inputWordLength = inputLength - i;
-        if(!getSubStringSuggestion(proximityInfo, xcoordinates, ycoordinates, codes,
+        if(getSubStringSuggestion(proximityInfo, xcoordinates, ycoordinates, codes,
                 useFullEditDistance, correction, queuePool, inputLength, hasAutoCorrectionCandidate,
                 startWordIndex + 1, inputWordStartPos, inputWordLength, tempOutputWordLength,
-                false /* missing space */, freqArray, wordLengthArray, outputWord, 0)) {
+                false /* missing space */, freqArray, wordLengthArray, outputWord, 0)
+                        != FLAG_MULTIPLE_SUGGEST_CONTINUE) {
             getMultiWordsSuggestionRec(proximityInfo, xcoordinates, ycoordinates, codes,
                     useFullEditDistance, inputLength, correction, queuePool,
                     hasAutoCorrectionCandidate, inputWordStartPos, startWordIndex + 1,
diff --git a/native/jni/src/unigram_dictionary.h b/native/jni/src/unigram_dictionary.h
index b708940..a1a8299 100644
--- a/native/jni/src/unigram_dictionary.h
+++ b/native/jni/src/unigram_dictionary.h
@@ -70,6 +70,9 @@
     static const int DEFAULT_MAX_ERRORS = 2;
     static const int MAX_ERRORS_FOR_TWO_WORDS = 1;
 
+    static const int FLAG_MULTIPLE_SUGGEST_ABORT = 0;
+    static const int FLAG_MULTIPLE_SUGGEST_SKIP = 1;
+    static const int FLAG_MULTIPLE_SUGGEST_CONTINUE = 2;
     UnigramDictionary(const uint8_t* const streamStart, int typedLetterMultipler,
             int fullWordMultiplier, int maxWordLength, int maxWords, const unsigned int flags);
     int getFrequency(const int32_t* const inWord, const int length) const;
@@ -127,7 +130,7 @@
             ProximityInfo *proximityInfo, unsigned short *word);
     int getMostFrequentWordLikeInner(const uint16_t* const inWord, const int length,
             short unsigned int *outWord);
-    bool getSubStringSuggestion(
+    int getSubStringSuggestion(
             ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates,
             const int *codes, const bool useFullEditDistance, Correction *correction,
             WordsPriorityQueuePool* queuePool, const int inputLength,