Merge "Add DamerauLevenshteinEditDistancePolicyTest"
diff --git a/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java
index 95df202..9cfcc3d 100644
--- a/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java
+++ b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java
@@ -16,22 +16,12 @@
 
 package com.android.inputmethod.latin.utils;
 
-import android.content.Context;
-
 import com.android.inputmethod.latin.RichInputMethodManager;
 import com.android.inputmethod.latin.settings.SettingsValues;
 
 public final class StatsUtils {
-    public static void init(final Context context) {
-    }
 
     public static void onCreate(final SettingsValues settingsValues,
             RichInputMethodManager richImm) {
     }
-
-    public static void onLoadSettings(final SettingsValues settingsValues) {
-    }
-
-    public static void onDestroy() {
-    }
 }
diff --git a/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtilsManager.java b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtilsManager.java
new file mode 100644
index 0000000..120b105
--- /dev/null
+++ b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtilsManager.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+
+package com.android.inputmethod.latin.utils;
+
+import android.content.Context;
+
+import com.android.inputmethod.latin.settings.SettingsValues;
+
+public class StatsUtilsManager {
+
+    private static final StatsUtilsManager sInstance = new StatsUtilsManager();
+
+    /**
+     * @return the singleton instance of {@link StatsUtilsManager}.
+     */
+    public static StatsUtilsManager getInstance() {
+        return sInstance;
+    }
+
+    public void onCreate(final Context context) {
+    }
+
+    public void onLoadSettings(final SettingsValues settingsValues) {
+    }
+
+    public void onDestroy() {
+    }
+}
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index f37f6cc..a1ffe5a 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -82,6 +82,7 @@
                 <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
                 <action android:name="android.intent.action.USER_INITIALIZE" />
+                <action android:name="android.intent.action.LOCALE_CHANGED" />
             </intent-filter>
         </receiver>
 
diff --git a/java/res/anim/key_preview_dismiss_holo.xml b/java/res/anim/key_preview_dismiss_holo.xml
new file mode 100644
index 0000000..0bf7254
--- /dev/null
+++ b/java/res/anim/key_preview_dismiss_holo.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2014, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="scaleX"
+        android:duration="53"
+        android:valueFrom="1.00"
+        android:valueTo="0.94" />
+    <objectAnimator
+        android:propertyName="scaleY"
+        android:duration="53"
+        android:valueFrom="1.00"
+        android:valueTo="0.94" />
+</set>
diff --git a/java/res/anim/key_preview_dismiss_lxx.xml b/java/res/anim/key_preview_dismiss_lxx.xml
new file mode 100644
index 0000000..326e534
--- /dev/null
+++ b/java/res/anim/key_preview_dismiss_lxx.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2014, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="scaleX"
+        android:duration="53"
+        android:valueFrom="1.00"
+        android:valueTo="1.00" />
+    <objectAnimator
+        android:propertyName="scaleY"
+        android:duration="53"
+        android:valueFrom="1.00"
+        android:valueTo="0.94" />
+</set>
diff --git a/java/res/anim/key_preview_show_up_holo.xml b/java/res/anim/key_preview_show_up_holo.xml
new file mode 100644
index 0000000..ad2e413
--- /dev/null
+++ b/java/res/anim/key_preview_show_up_holo.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2014, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="scaleX"
+        android:duration="17"
+        android:valueFrom="0.98"
+        android:valueTo="1.00" />
+    <objectAnimator
+        android:propertyName="scaleY"
+        android:duration="17"
+        android:valueFrom="0.98"
+        android:valueTo="1.00" />
+</set>
diff --git a/java/res/anim/key_preview_show_up_lxx.xml b/java/res/anim/key_preview_show_up_lxx.xml
new file mode 100644
index 0000000..f500349
--- /dev/null
+++ b/java/res/anim/key_preview_show_up_lxx.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2014, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:propertyName="scaleX"
+        android:duration="17"
+        android:valueFrom="1.00"
+        android:valueTo="1.00" />
+    <objectAnimator
+        android:propertyName="scaleY"
+        android:duration="17"
+        android:valueFrom="0.98"
+        android:valueTo="1.00" />
+</set>
diff --git a/java/res/drawable/btn_keyboard_key_popup_lxx_light.xml b/java/res/drawable/btn_keyboard_key_popup_lxx_light.xml
new file mode 100644
index 0000000..d6cd2b8
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key_popup_lxx_light.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_popup_selected_lxx_light" />
+    <item android:drawable="@android:color/transparent" />
+</selector>
diff --git a/java/res/layout/additional_subtype_dialog.xml b/java/res/layout/additional_subtype_dialog.xml
index f97c006..b7804f5 100644
--- a/java/res/layout/additional_subtype_dialog.xml
+++ b/java/res/layout/additional_subtype_dialog.xml
@@ -18,39 +18,60 @@
 */
 -->
 
-<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:columnCount="2"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_marginLeft="8dip"
-    android:layout_marginRight="8dip"
-    android:padding="8dip">
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="left|center_vertical"
-        style="?android:attr/textAppearanceSmall"
-        android:text="@string/subtype_locale" />
-    <Spinner
-        android:id="@+id/subtype_locale_spinner"
-        android:layout_width="wrap_content"
-        android:layout_marginLeft="8dip"
-        android:layout_marginBottom="8dip"
-        android:layout_marginTop="8dip"
-        android:layout_gravity="fill_horizontal|center_vertical"
-        android:prompt="@string/subtype_locale" />
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="left|center_vertical"
-        style="?android:attr/textAppearanceSmall"
-        android:text="@string/keyboard_layout_set" />
-    <Spinner
-        android:id="@+id/keyboard_layout_set_spinner"
-        android:layout_width="wrap_content"
-        android:layout_marginLeft="8dip"
-        android:layout_marginBottom="8dip"
-        android:layout_marginTop="8dip"
-        android:layout_gravity="fill_horizontal|center_vertical"
-        android:prompt="@string/keyboard_layout_set" />
-</GridLayout>
+    android:padding="16dip">
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:layout_width="0dp"
+            android:layout_weight="30"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start|center_vertical"
+            android:gravity="start|left"
+            android:textAlignment="viewStart"
+            style="?android:attr/textAppearanceSmall"
+            android:text="@string/subtype_locale" />
+        <Spinner
+            android:id="@+id/subtype_locale_spinner"
+            android:spinnerMode="dialog"
+            android:layout_width="0dp"
+            android:layout_weight="70"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="8dip"
+            android:layout_marginBottom="8dip"
+            android:layout_marginTop="8dip"
+            android:layout_gravity="fill_horizontal|center_vertical"
+            android:gravity="start|left"
+            android:prompt="@string/subtype_locale" />
+        </LinearLayout>
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:layout_width="0dp"
+            android:layout_weight="30"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start|center_vertical"
+            android:textAlignment="viewStart"
+            style="?android:attr/textAppearanceSmall"
+            android:text="@string/keyboard_layout_set" />
+        <Spinner
+            android:id="@+id/keyboard_layout_set_spinner"
+            android:spinnerMode="dialog"
+            android:layout_width="0dp"
+            android:layout_weight="70"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="8dip"
+            android:layout_marginBottom="8dip"
+            android:layout_marginTop="8dip"
+            android:layout_gravity="fill_horizontal|center_vertical"
+            android:gravity="start|left"
+            android:prompt="@string/keyboard_layout_set" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/java/res/values-ne-rNP/strings.xml b/java/res/values-ne-rNP/strings.xml
index c7c95ae..1764e76 100644
--- a/java/res/values-ne-rNP/strings.xml
+++ b/java/res/values-ne-rNP/strings.xml
@@ -80,7 +80,7 @@
     <string name="help_and_feedback" msgid="5328219371839879161">"मद्दत र प्रतिक्रिया"</string>
     <string name="select_language" msgid="3693815588777926848">"इनपुट भाषाहरू"</string>
     <string name="hint_add_to_dictionary" msgid="573678656946085380">"बचत गर्न पुनः छुनुहोस्"</string>
-    <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"बचत गर्न यहाँ छुनुहोस्"</string>
+    <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"सुरक्षित गर्न यहाँ छुनुहोस्"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"उपलब्ध शब्दकोश"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"किबोर्ड थिम"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"अंग्रेजी (युके)"</string>
diff --git a/java/res/values-ta-rIN/strings-talkback-descriptions.xml b/java/res/values-ta-rIN/strings-talkback-descriptions.xml
index 911e1a5..0ef0720 100644
--- a/java/res/values-ta-rIN/strings-talkback-descriptions.xml
+++ b/java/res/values-ta-rIN/strings-talkback-descriptions.xml
@@ -58,7 +58,7 @@
     <string name="keyboard_mode_date" msgid="6597407244976713364">"தேதி"</string>
     <string name="keyboard_mode_date_time" msgid="3642804408726668808">"தேதி மற்றும் நேரம்"</string>
     <string name="keyboard_mode_email" msgid="1239682082047693644">"மின்னஞ்சல்"</string>
-    <string name="keyboard_mode_im" msgid="3812086215529493501">"செய்தியிடல்"</string>
+    <string name="keyboard_mode_im" msgid="3812086215529493501">"மெசேஜ்"</string>
     <string name="keyboard_mode_number" msgid="5395042245837996809">"எண்"</string>
     <string name="keyboard_mode_phone" msgid="2486230278064523665">"ஃபோன்"</string>
     <string name="keyboard_mode_text" msgid="9138789594969187494">"உரை"</string>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index e89912a..c756f8c 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -113,6 +113,10 @@
         <!-- TODO: consolidate key preview linger timeout with the key preview animation parameters. -->
         <!-- Delay after key releasing and key press feedback dismissing in millisecond -->
         <attr name="keyPreviewLingerTimeout" format="integer" />
+        <!-- Key preview show up animator -->
+        <attr name="keyPreviewShowUpAnimator" format="reference" />
+        <!-- Key preview dismiss animator -->
+        <attr name="keyPreviewDismissAnimator" format="reference" />
         <!-- Layout resource for more keys keyboard -->
         <attr name="moreKeysKeyboardLayout" format="reference" />
         <attr name="backgroundDimAlpha" format="integer" />
diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml
index d42b4e9..5453d51 100644
--- a/java/res/values/colors.xml
+++ b/java/res/values/colors.xml
@@ -82,4 +82,6 @@
     <color name="setup_text_action">@android:color/holo_blue_light</color>
     <color name="setup_step_background">@android:color/background_light</color>
     <color name="setup_welcome_video_margin_color">#FFCCCCCC</color>
+    <!-- Accent color for the notification. We need to match this to the OS build -->
+    <color name="notification_accent_color">#FF607D8B</color>
 </resources>
diff --git a/java/res/values/donottranslate-text-decorator.xml b/java/res/values/donottranslate-text-decorator.xml
index 9c39a46..a200349 100644
--- a/java/res/values/donottranslate-text-decorator.xml
+++ b/java/res/values/donottranslate-text-decorator.xml
@@ -31,7 +31,7 @@
 
     <!-- If true, the commit/add-to-text indicator will be suppressed when the word isn't going to
          trigger auto-correction. -->
-    <bool name="text_decorator_only_for_auto_correction">false</bool>
+    <bool name="text_decorator_only_for_auto_correction">true</bool>
 
     <!-- If true, the commit/add-to-text indicator will be suppressed when the word is already in
          the dictionary. -->
@@ -61,18 +61,24 @@
     <!-- Coordinates of the closed path to be used to render the commit indicator.
          The format is:  X[0], Y[0], X[1], Y[1], ..., X[N-1], Y[N-1] -->
     <integer-array name="text_decorator_commit_indicator_path">
-        <item>180</item>
-        <item>323</item>
-        <item>97</item>
         <item>240</item>
-        <item>68</item>
-        <item>268</item>
-        <item>180</item>
-        <item>380</item>
-        <item>420</item>
-        <item>140</item>
-        <item>392</item>
-        <item>112</item>
+        <item>80</item>
+        <item>212</item>
+        <item>108</item>
+        <item>323</item>
+        <item>220</item>
+        <item>80</item>
+        <item>220</item>
+        <item>80</item>
+        <item>260</item>
+        <item>323</item>
+        <item>260</item>
+        <item>212</item>
+        <item>372</item>
+        <item>240</item>
+        <item>400</item>
+        <item>400</item>
+        <item>240</item>
     </integer-array>
 
     <!-- Background color to be used to highlight the target text when the add-to-dictionary
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 2a6495a..414820b 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -336,14 +336,20 @@
     <string name="prefs_keypress_vibration_duration_settings">Keypress vibration duration</string>
     <!-- Title of the settings for keypress sound volume [CHAR LIMIT=35] -->
     <string name="prefs_keypress_sound_volume_settings">Keypress sound volume</string>
+    <!-- Title of the settings for customize key popup animation parameters [CHAR LIMIT=35] -->
+    <string name="prefs_customize_key_preview_animation">Customize key preview animation</string>
     <!-- Title of the settings for key popup show up animation duration (in milliseconds) [CHAR LIMIT=35] -->
     <string name="prefs_key_popup_show_up_duration_settings" translatable="false">Key popup show up duration</string>
     <!-- Title of the settings for key popup dismiss animation duration (in milliseconds) [CHAR LIMIT=35] -->
     <string name="prefs_key_popup_dismiss_duration_settings" translatable="false">Key popup dismiss duration</string>
-    <!-- Title of the settings for key popup show up animation start scale (in percentile) [CHAR LIMIT=35] -->
-    <string name="prefs_key_popup_show_up_start_scale_settings" translatable="false">Key popup show up start scale</string>
-    <!-- Title of the settings for key popup dismiss animation end scale (in percentile) [CHAR LIMIT=35] -->
-    <string name="prefs_key_popup_dismiss_end_scale_settings" translatable="false">Key popup dismiss end scale</string>
+    <!-- Title of the settings for key popup show up animation start X-scale (in percentile) [CHAR LIMIT=35] -->
+    <string name="prefs_key_popup_show_up_start_x_scale_settings" translatable="false">Key popup show up start X scale</string>
+    <!-- Title of the settings for key popup show up animation start Y-scale (in percentile) [CHAR LIMIT=35] -->
+    <string name="prefs_key_popup_show_up_start_y_scale_settings" translatable="false">Key popup show up start Y scale</string>
+    <!-- Title of the settings for key popup dismiss animation end X-scale (in percentile) [CHAR LIMIT=35] -->
+    <string name="prefs_key_popup_dismiss_end_x_scale_settings" translatable="false">Key popup dismiss end X scale</string>
+    <!-- Title of the settings for key popup dismiss animation end Y-scale (in percentile) [CHAR LIMIT=35] -->
+    <string name="prefs_key_popup_dismiss_end_y_scale_settings" translatable="false">Key popup dismiss end Y scale</string>
     <!-- Title of the settings for reading an external dictionary file -->
     <string name="prefs_read_external_dictionary">Read external dictionary file</string>
     <!-- Message to show when there are no files to install as an external dictionary [CHAR LIMIT=100] -->
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index 6fddcb9..051489e 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -59,6 +59,8 @@
     >
         <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_ics</item>
         <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item>
+        <item name="keyPreviewShowUpAnimator">@anim/key_preview_show_up_holo</item>
+        <item name="keyPreviewDismissAnimator">@anim/key_preview_dismiss_holo</item>
         <item name="gestureFloatingPreviewTextColor">@color/highlight_color_ics</item>
         <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_holo</item>
         <item name="gestureTrailColor">@color/highlight_color_ics</item>
diff --git a/java/res/values/themes-klp.xml b/java/res/values/themes-klp.xml
index c9b8331..a853ed9 100644
--- a/java/res/values/themes-klp.xml
+++ b/java/res/values/themes-klp.xml
@@ -59,6 +59,8 @@
     >
         <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_klp</item>
         <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item>
+        <item name="keyPreviewShowUpAnimator">@anim/key_preview_show_up_holo</item>
+        <item name="keyPreviewDismissAnimator">@anim/key_preview_dismiss_holo</item>
         <item name="gestureFloatingPreviewTextColor">@color/highlight_color_klp</item>
         <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_holo</item>
         <item name="gestureTrailColor">@color/highlight_color_klp</item>
diff --git a/java/res/values/themes-lxx-dark.xml b/java/res/values/themes-lxx-dark.xml
index 6afbd9b..2aaee13 100644
--- a/java/res/values/themes-lxx-dark.xml
+++ b/java/res/values/themes-lxx-dark.xml
@@ -59,6 +59,8 @@
     >
         <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_lxx_dark</item>
         <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item>
+        <item name="keyPreviewShowUpAnimator">@anim/key_preview_show_up_lxx</item>
+        <item name="keyPreviewDismissAnimator">@anim/key_preview_dismiss_lxx</item>
         <item name="gestureFloatingPreviewTextColor">@color/auto_correct_color_lxx_dark</item>
         <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_lxx_dark</item>
         <item name="gestureTrailColor">@color/gesture_trail_color_lxx_dark</item>
diff --git a/java/res/values/themes-lxx-light.xml b/java/res/values/themes-lxx-light.xml
index b3ced80..e7a6f58 100644
--- a/java/res/values/themes-lxx-light.xml
+++ b/java/res/values/themes-lxx-light.xml
@@ -59,6 +59,8 @@
     >
         <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_lxx_light</item>
         <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item>
+        <item name="keyPreviewShowUpAnimator">@anim/key_preview_show_up_lxx</item>
+        <item name="keyPreviewDismissAnimator">@anim/key_preview_dismiss_lxx</item>
         <item name="gestureFloatingPreviewTextColor">@color/auto_correct_color_lxx_light</item>
         <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_lxx_light</item>
         <item name="gestureTrailColor">@color/gesture_trail_color_lxx_light</item>
@@ -98,8 +100,7 @@
         parent="KeyboardView.LXX_Light"
     >
         <item name="android:background">@drawable/keyboard_popup_panel_background_lxx_light</item>
-        <!-- Reuse KLP key background -->
-        <item name="keyBackground">@drawable/btn_keyboard_key_popup_klp</item>
+        <item name="keyBackground">@drawable/btn_keyboard_key_popup_lxx_light</item>
         <item name="keyTypeface">normal</item>
         <item name="verticalCorrection">@dimen/config_more_keys_keyboard_vertical_correction_holo</item>
     </style>
diff --git a/java/res/xml/prefs_screen_debug.xml b/java/res/xml/prefs_screen_debug.xml
index 965369a..e0f3501 100644
--- a/java/res/xml/prefs_screen_debug.xml
+++ b/java/res/xml/prefs_screen_debug.xml
@@ -52,19 +52,38 @@
         latin:minValue="@integer/config_min_longpress_timeout"
         latin:maxValue="@integer/config_max_longpress_timeout"
         latin:stepValue="@integer/config_longpress_timeout_step" />
+    <CheckBoxPreference
+        android:key="pref_has_custom_key_preview_animation_params"
+        android:title="@string/prefs_customize_key_preview_animation"
+        android:defaultValue="false"
+        android:persistent="true" />
     <com.android.inputmethod.latin.settings.SeekBarDialogPreference
-        android:key="pref_key_preview_show_up_start_scale"
-        android:title="@string/prefs_key_popup_show_up_start_scale_settings"
+        android:dependency="pref_customize_key_preview_animation"
+        android:key="pref_key_preview_show_up_start_x_scale"
+        android:title="@string/prefs_key_popup_show_up_start_x_scale_settings"
         latin:maxValue="100" /> <!-- percent -->
     <com.android.inputmethod.latin.settings.SeekBarDialogPreference
-        android:key="pref_key_preview_dismiss_end_scale"
-        android:title="@string/prefs_key_popup_dismiss_end_scale_settings"
+        android:dependency="pref_customize_key_preview_animation"
+        android:key="pref_key_preview_show_up_start_y_scale"
+        android:title="@string/prefs_key_popup_show_up_start_y_scale_settings"
         latin:maxValue="100" /> <!-- percent -->
     <com.android.inputmethod.latin.settings.SeekBarDialogPreference
+        android:dependency="pref_customize_key_preview_animation"
+        android:key="pref_key_preview_dismiss_end_x_scale"
+        android:title="@string/prefs_key_popup_dismiss_end_x_scale_settings"
+        latin:maxValue="100" /> <!-- percent -->
+    <com.android.inputmethod.latin.settings.SeekBarDialogPreference
+        android:dependency="pref_customize_key_preview_animation"
+        android:key="pref_key_preview_dismiss_end_y_scale"
+        android:title="@string/prefs_key_popup_dismiss_end_y_scale_settings"
+        latin:maxValue="100" /> <!-- percent -->
+    <com.android.inputmethod.latin.settings.SeekBarDialogPreference
+        android:dependency="pref_customize_key_preview_animation"
         android:key="pref_key_preview_show_up_duration"
         android:title="@string/prefs_key_popup_show_up_duration_settings"
         latin:maxValue="100" /> <!-- milliseconds -->
     <com.android.inputmethod.latin.settings.SeekBarDialogPreference
+        android:dependency="pref_customize_key_preview_animation"
         android:key="pref_key_preview_dismiss_duration"
         android:title="@string/prefs_key_popup_dismiss_duration_settings"
         latin:maxValue="100" /> <!-- milliseconds -->
diff --git a/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java b/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java
new file mode 100644
index 0000000..eb18007
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+package com.android.inputmethod.compat;
+
+import android.app.Notification;
+import android.os.Build;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class NotificationCompatUtils {
+    // Note that TextInfo.getCharSequence() is supposed to be available in API level 21 and later.
+    private static final Method METHOD_setColor =
+            CompatUtils.getMethod(Notification.Builder.class, "setColor", int.class);
+    private static final Method METHOD_setVisibility =
+            CompatUtils.getMethod(Notification.Builder.class, "setVisibility", int.class);
+    private static final Method METHOD_setCategory =
+            CompatUtils.getMethod(Notification.Builder.class, "setCategory", String.class);
+    private static final Method METHOD_setPriority =
+            CompatUtils.getMethod(Notification.Builder.class, "setPriority", int.class);
+    private static final Method METHOD_build =
+            CompatUtils.getMethod(Notification.Builder.class, "build");
+    private static final Field FIELD_VISIBILITY_SECRET =
+            CompatUtils.getField(Notification.class, "VISIBILITY_SECRET");
+    private static final int VISIBILITY_SECRET = null == FIELD_VISIBILITY_SECRET ? 0
+            : (Integer) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */,
+                    FIELD_VISIBILITY_SECRET);
+    private static final Field FIELD_CATEGORY_RECOMMENDATION =
+            CompatUtils.getField(Notification.class, "CATEGORY_RECOMMENDATION");
+    private static final String CATEGORY_RECOMMENDATION = null == FIELD_CATEGORY_RECOMMENDATION ? ""
+            : (String) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */,
+                    FIELD_CATEGORY_RECOMMENDATION);
+    private static final Field FIELD_PRIORITY_LOW =
+            CompatUtils.getField(Notification.class, "PRIORITY_LOW");
+    private static final int PRIORITY_LOW = null == FIELD_PRIORITY_LOW ? 0
+            : (Integer) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */,
+                    FIELD_PRIORITY_LOW);
+
+    private NotificationCompatUtils() {
+        // This class is non-instantiable.
+    }
+
+    // Sets the accent color
+    public static void setColor(final Notification.Builder builder, final int color) {
+        CompatUtils.invoke(builder, null, METHOD_setColor, color);
+    }
+
+    public static void setVisibilityToSecret(final Notification.Builder builder) {
+        CompatUtils.invoke(builder, null, METHOD_setVisibility, VISIBILITY_SECRET);
+    }
+
+    public static void setCategoryToRecommendation(final Notification.Builder builder) {
+        CompatUtils.invoke(builder, null, METHOD_setCategory, CATEGORY_RECOMMENDATION);
+    }
+
+    public static void setPriorityToLow(final Notification.Builder builder) {
+        CompatUtils.invoke(builder, null, METHOD_setPriority, PRIORITY_LOW);
+    }
+
+    public static Notification build(final Notification.Builder builder) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            // #build was added in API level 16, JELLY_BEAN
+            return (Notification) CompatUtils.invoke(builder, null, METHOD_build);
+        } else {
+            // #getNotification was deprecated in API level 16, JELLY_BEAN
+            return builder.getNotification();
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
index afbe8c8..0f00be1 100644
--- a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java
@@ -34,6 +34,9 @@
     // Note that View.setElevation(float) has been introduced in API level 21.
     private static final Method METHOD_setElevation = CompatUtils.getMethod(
             View.class, "setElevation", float.class);
+    // Note that View.setTextAlignment(int) has been introduced in API level 17.
+    private static final Method METHOD_setTextAlignment = CompatUtils.getMethod(
+            View.class, "setTextAlignment", int.class);
 
     private ViewCompatUtils() {
         // This utility class is not publicly instantiable.
@@ -56,9 +59,19 @@
     }
 
     public static void setElevation(final View view, final float elevation) {
-        if (METHOD_setElevation == null) {
-            return;
-        }
         CompatUtils.invoke(view, null, METHOD_setElevation, elevation);
     }
+
+    // These TEXT_ALIGNMENT_* constants have been introduced in API 17.
+    public static final int TEXT_ALIGNMENT_INHERIT = 0;
+    public static final int TEXT_ALIGNMENT_GRAVITY = 1;
+    public static final int TEXT_ALIGNMENT_TEXT_START = 2;
+    public static final int TEXT_ALIGNMENT_TEXT_END = 3;
+    public static final int TEXT_ALIGNMENT_CENTER = 4;
+    public static final int TEXT_ALIGNMENT_VIEW_START = 5;
+    public static final int TEXT_ALIGNMENT_VIEW_END = 6;
+
+    public static void setTextAlignment(final View view, final int textAlignment) {
+        CompatUtils.invoke(view, null, METHOD_setTextAlignment, textAlignment);
+    }
 }
diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
index 95a0942..6fbca44 100644
--- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
+++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
@@ -31,12 +31,14 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.net.ConnectivityManager;
 import android.net.Uri;
+import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.inputmethod.compat.ConnectivityManagerCompatUtils;
 import com.android.inputmethod.compat.DownloadManagerCompatUtils;
+import com.android.inputmethod.compat.NotificationCompatUtils;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.utils.ApplicationUtils;
 import com.android.inputmethod.latin.utils.DebugLogUtils;
@@ -858,7 +860,7 @@
         final String language = (null == locale ? "" : locale.getDisplayLanguage());
         final String titleFormat = context.getString(R.string.dict_available_notification_title);
         final String notificationTitle = String.format(titleFormat, language);
-        final Notification notification = new Notification.Builder(context)
+        final Notification.Builder builder = new Notification.Builder(context)
                 .setAutoCancel(true)
                 .setContentIntent(notificationIntent)
                 .setContentTitle(notificationTitle)
@@ -866,8 +868,13 @@
                 .setTicker(notificationTitle)
                 .setOngoing(false)
                 .setOnlyAlertOnce(true)
-                .setSmallIcon(R.drawable.ic_notify_dictionary)
-                .getNotification();
+                .setSmallIcon(R.drawable.ic_notify_dictionary);
+        NotificationCompatUtils.setColor(builder,
+                context.getResources().getColor(R.color.notification_accent_color));
+        NotificationCompatUtils.setPriorityToLow(builder);
+        NotificationCompatUtils.setVisibilityToSecret(builder);
+        NotificationCompatUtils.setCategoryToRecommendation(builder);
+        final Notification notification = NotificationCompatUtils.build(builder);
         notificationManager.notify(DICT_AVAILABLE_NOTIFICATION_ID, notification);
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 3743d26..05334c7 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -108,11 +108,23 @@
     private final MoreKeySpec[] mMoreKeys;
     /** More keys column number and flags */
     private final int mMoreKeysColumnAndFlags;
-    private static final int MORE_KEYS_COLUMN_MASK = 0x000000ff;
-    private static final int MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER = 0x80000000;
+    private static final int MORE_KEYS_COLUMN_NUMBER_MASK = 0x000000ff;
+    // If this flag is specified, more keys keyboard should have the specified number of columns.
+    // Otherwise more keys keyboard should have less than or equal to the specified maximum number
+    // of columns.
+    private static final int MORE_KEYS_FLAGS_FIXED_COLUMN = 0x00000100;
+    // If this flag is specified, the order of more keys is determined by the order in the more
+    // keys' specification. Otherwise the order of more keys is automatically determined.
+    private static final int MORE_KEYS_FLAGS_FIXED_ORDER = 0x00000200;
+    private static final int MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER = 0;
+    private static final int MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER =
+            MORE_KEYS_FLAGS_FIXED_COLUMN;
+    private static final int MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER =
+            (MORE_KEYS_FLAGS_FIXED_COLUMN | MORE_KEYS_FLAGS_FIXED_ORDER);
     private static final int MORE_KEYS_FLAGS_HAS_LABELS = 0x40000000;
     private static final int MORE_KEYS_FLAGS_NEEDS_DIVIDERS = 0x20000000;
     private static final int MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY = 0x10000000;
+    // TODO: Rename these specifiers to !autoOrder! and !fixedOrder! respectively.
     private static final String MORE_KEYS_AUTO_COLUMN_ORDER = "!autoColumnOrder!";
     private static final String MORE_KEYS_FIXED_COLUMN_ORDER = "!fixedColumnOrder!";
     private static final String MORE_KEYS_HAS_LABELS = "!hasLabels!";
@@ -255,25 +267,31 @@
         int actionFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
         String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
 
-        int moreKeysColumn = style.getInt(keyAttr,
-                R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMoreKeysKeyboardColumn);
+        // Get maximum column order number and set a relevant mode value.
+        int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER
+                | style.getInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn,
+                        params.mMaxMoreKeysKeyboardColumn);
         int value;
         if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) {
-            moreKeysColumn = value & MORE_KEYS_COLUMN_MASK;
+            // Override with fixed column order number and set a relevant mode value.
+            moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER
+                    | (value & MORE_KEYS_COLUMN_NUMBER_MASK);
         }
         if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) {
-            moreKeysColumn = MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER | (value & MORE_KEYS_COLUMN_MASK);
+            // Override with fixed column order number and set a relevant mode value.
+            moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER
+                    | (value & MORE_KEYS_COLUMN_NUMBER_MASK);
         }
         if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) {
-            moreKeysColumn |= MORE_KEYS_FLAGS_HAS_LABELS;
+            moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_HAS_LABELS;
         }
         if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) {
-            moreKeysColumn |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS;
+            moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS;
         }
         if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NO_PANEL_AUTO_MORE_KEY)) {
-            moreKeysColumn |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY;
+            moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY;
         }
-        mMoreKeysColumnAndFlags = moreKeysColumn;
+        mMoreKeysColumnAndFlags = moreKeysColumnAndFlags;
 
         final String[] additionalMoreKeys;
         if ((mLabelFlags & LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS) != 0) {
@@ -680,12 +698,16 @@
                 && !TextUtils.isEmpty(mHintLabel);
     }
 
-    public final int getMoreKeysColumn() {
-        return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_MASK;
+    public final int getMoreKeysColumnNumber() {
+        return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_NUMBER_MASK;
     }
 
-    public final boolean isFixedColumnOrderMoreKeys() {
-        return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER) != 0;
+    public final boolean isMoreKeysFixedColumn() {
+        return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN) != 0;
+    }
+
+    public final boolean isMoreKeysFixedOrder() {
+        return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_ORDER) != 0;
     }
 
     public final boolean hasLabelsInMoreKeys() {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 0804ceb..3f43673 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -119,7 +119,15 @@
                 new SparseArray<>();
     }
 
-    public static void clearKeyboardCache() {
+    public static void onSystemLocaleChanged() {
+        clearKeyboardCache();
+    }
+
+    public static void onKeyboardThemeChanged() {
+        clearKeyboardCache();
+    }
+
+    private static void clearKeyboardCache() {
         sKeyboardCache.clear();
         sKeysCache.clear();
     }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 30bd1df..91d7033 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -103,7 +103,7 @@
         if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)) {
             mKeyboardTheme = keyboardTheme;
             mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
-            KeyboardLayoutSet.clearKeyboardCache();
+            KeyboardLayoutSet.onKeyboardThemeChanged();
             return true;
         }
         return false;
@@ -154,9 +154,12 @@
                 mCurrentSettingsValues.mKeyPreviewPopupOn,
                 mCurrentSettingsValues.mKeyPreviewPopupDismissDelay);
         keyboardView.setKeyPreviewAnimationParams(
-                mCurrentSettingsValues.mKeyPreviewShowUpStartScale,
+                mCurrentSettingsValues.mHasCustomKeyPreviewAnimationParams,
+                mCurrentSettingsValues.mKeyPreviewShowUpStartXScale,
+                mCurrentSettingsValues.mKeyPreviewShowUpStartYScale,
                 mCurrentSettingsValues.mKeyPreviewShowUpDuration,
-                mCurrentSettingsValues.mKeyPreviewDismissEndScale,
+                mCurrentSettingsValues.mKeyPreviewDismissEndXScale,
+                mCurrentSettingsValues.mKeyPreviewDismissEndYScale,
                 mCurrentSettingsValues.mKeyPreviewDismissDuration);
         keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
         final boolean subtypeChanged = (oldKeyboard == null)
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 847d907..d2f3e97 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -85,6 +85,8 @@
  * @attr ref R.styleable#MainKeyboardView_keyPreviewOffset
  * @attr ref R.styleable#MainKeyboardView_keyPreviewHeight
  * @attr ref R.styleable#MainKeyboardView_keyPreviewLingerTimeout
+ * @attr ref R.styleable#MainKeyboardView_keyPreviewShowUpAnimator
+ * @attr ref R.styleable#MainKeyboardView_keyPreviewDismissAnimator
  * @attr ref R.styleable#MainKeyboardView_moreKeysKeyboardLayout
  * @attr ref R.styleable#MainKeyboardView_backgroundDimAlpha
  * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
@@ -390,20 +392,34 @@
     }
 
     /**
-     * Enables or disables the key feedback popup. This is a popup that shows a magnified
+     * Enables or disables the key preview popup. This is a popup that shows a magnified
      * version of the depressed key. By default the preview is enabled.
      * @param previewEnabled whether or not to enable the key feedback preview
      * @param delay the delay after which the preview is dismissed
-     * @see #isKeyPreviewPopupEnabled()
      */
     public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
         mKeyPreviewDrawParams.setPopupEnabled(previewEnabled, delay);
     }
 
-    public void setKeyPreviewAnimationParams(final float showUpStartScale, final int showUpDuration,
-            final float dismissEndScale, final int dismissDuration) {
-        mKeyPreviewDrawParams.setAnimationParams(
-                showUpStartScale, showUpDuration, dismissEndScale, dismissDuration);
+    /**
+     * Enables or disables the key preview popup animations and set animations' parameters.
+     *
+     * @param hasCustomAnimationParams false to use the default key preview popup animations
+     *   specified by keyPreviewShowUpAnimator and keyPreviewDismissAnimator attributes.
+     *   true to override the default animations with the specified parameters.
+     * @param showUpStartXScale from this x-scale the show up animation will start.
+     * @param showUpStartYScale from this y-scale the show up animation will start.
+     * @param showUpDuration the duration of the show up animation in milliseconds.
+     * @param dismissEndXScale to this x-scale the dismiss animation will end.
+     * @param dismissEndYScale to this y-scale the dismiss animation will end.
+     * @param dismissDuration the duration of the dismiss animation in milliseconds.
+     */
+    public void setKeyPreviewAnimationParams(final boolean hasCustomAnimationParams,
+            final float showUpStartXScale, final float showUpStartYScale, final int showUpDuration,
+            final float dismissEndXScale, final float dismissEndYScale, final int dismissDuration) {
+        mKeyPreviewDrawParams.setAnimationParams(hasCustomAnimationParams,
+                showUpStartXScale, showUpStartYScale, showUpDuration,
+                dismissEndXScale, dismissEndYScale, dismissDuration);
     }
 
     private void locatePreviewPlacerView() {
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
index e0184d7..52e2e85 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
@@ -43,7 +43,7 @@
 
     @UsedForTesting
     static class MoreKeysKeyboardParams extends KeyboardParams {
-        public boolean mIsFixedOrder;
+        public boolean mIsMoreKeysFixedOrder;
         /* package */int mTopRowAdjustment;
         public int mNumRows;
         public int mNumColumns;
@@ -61,29 +61,35 @@
          * Set keyboard parameters of more keys keyboard.
          *
          * @param numKeys number of keys in this more keys keyboard.
-         * @param maxColumns number of maximum columns of this more keys keyboard.
+         * @param numColumn number of columns of this more keys keyboard.
          * @param keyWidth more keys keyboard key width in pixel, including horizontal gap.
          * @param rowHeight more keys keyboard row height in pixel, including vertical gap.
          * @param coordXInParent coordinate x of the key preview in parent keyboard.
          * @param parentKeyboardWidth parent keyboard width in pixel.
-         * @param isFixedColumnOrder if true, more keys should be laid out in fixed order.
+         * @param isMoreKeysFixedColumn true if more keys keyboard should have
+         *   <code>numColumn</code> columns. Otherwise more keys keyboard should have
+         *   <code>numColumn</code> columns at most.
+         * @param isMoreKeysFixedOrder true if the order of more keys is determined by the order in
+         *   the more keys' specification. Otherwise the order of more keys is automatically
+         *   determined.
          * @param dividerWidth width of divider, zero for no dividers.
          */
-        public void setParameters(final int numKeys, final int maxColumns, final int keyWidth,
+        public void setParameters(final int numKeys, final int numColumn, final int keyWidth,
                 final int rowHeight, final int coordXInParent, final int parentKeyboardWidth,
-                final boolean isFixedColumnOrder, final int dividerWidth) {
-            mIsFixedOrder = isFixedColumnOrder;
-            if (parentKeyboardWidth / keyWidth < Math.min(numKeys, maxColumns)) {
+                final boolean isMoreKeysFixedColumn, final boolean isMoreKeysFixedOrder,
+                final int dividerWidth) {
+            mIsMoreKeysFixedOrder = isMoreKeysFixedOrder;
+            if (parentKeyboardWidth / keyWidth < Math.min(numKeys, numColumn)) {
                 throw new IllegalArgumentException("Keyboard is too small to hold more keys: "
-                        + parentKeyboardWidth + " " + keyWidth + " " + numKeys + " " + maxColumns);
+                        + parentKeyboardWidth + " " + keyWidth + " " + numKeys + " " + numColumn);
             }
             mDefaultKeyWidth = keyWidth;
             mDefaultRowHeight = rowHeight;
 
-            final int numRows = (numKeys + maxColumns - 1) / maxColumns;
+            final int numRows = (numKeys + numColumn - 1) / numColumn;
             mNumRows = numRows;
-            final int numColumns = mIsFixedOrder ? Math.min(numKeys, maxColumns)
-                    : getOptimizedColumns(numKeys, maxColumns);
+            final int numColumns = isMoreKeysFixedColumn ? Math.min(numKeys, numColumn)
+                    : getOptimizedColumns(numKeys, numColumn);
             mNumColumns = numColumns;
             final int topKeys = numKeys % numColumns;
             mTopKeys = topKeys == 0 ? numColumns : topKeys;
@@ -120,7 +126,7 @@
             mRightKeys = rightKeys;
 
             // Adjustment of the top row.
-            mTopRowAdjustment = mIsFixedOrder ? getFixedOrderTopRowAdjustment()
+            mTopRowAdjustment = isMoreKeysFixedOrder ? getFixedOrderTopRowAdjustment()
                     : getAutoOrderTopRowAdjustment();
             mDividerWidth = dividerWidth;
             mColumnWidth = mDefaultKeyWidth + mDividerWidth;
@@ -148,7 +154,7 @@
 
         // Return key position according to column count (0 is default).
         /* package */int getColumnPos(final int n) {
-            return mIsFixedOrder ? getFixedOrderColumnPos(n) : getAutomaticColumnPos(n);
+            return mIsMoreKeysFixedOrder ? getFixedOrderColumnPos(n) : getAutomaticColumnPos(n);
         }
 
         private int getFixedOrderColumnPos(final int n) {
@@ -263,7 +269,8 @@
          * @param keyboard the {@link Keyboard} that contains the parentKey.
          * @param isSingleMoreKeyWithPreview true if the <code>key</code> has just a single
          *        "more key" and its key popup preview is enabled.
-         * @param keyPreviewDrawParams the parameter to place key preview.
+         * @param keyPreviewVisibleWidth the width of visible part of key popup preview.
+         * @param keyPreviewVisibleHeight the height of visible part of key popup preview
          * @param paintToMeasure the {@link Paint} object to measure a "more key" width
          */
         public Builder(final Context context, final Key key, final Keyboard keyboard,
@@ -306,9 +313,9 @@
                 dividerWidth = 0;
             }
             final MoreKeySpec[] moreKeys = key.getMoreKeys();
-            mParams.setParameters(moreKeys.length, key.getMoreKeysColumn(), keyWidth, rowHeight,
+            mParams.setParameters(moreKeys.length, key.getMoreKeysColumnNumber(), keyWidth, rowHeight,
                     key.getX() + key.getWidth() / 2, keyboard.mId.mWidth,
-                    key.isFixedColumnOrderMoreKeys(), dividerWidth);
+                    key.isMoreKeysFixedColumn(), key.isMoreKeysFixedOrder(), dividerWidth);
         }
 
         private static int getMaxKeyWidth(final Key parentKey, final int minKeyWidth,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
index cd29c8d..5005b7d 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
@@ -18,13 +18,9 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
 
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.latin.utils.CoordinateUtils;
@@ -89,9 +85,9 @@
         }
         final Object tag = keyPreviewView.getTag();
         if (withAnimation) {
-            if (tag instanceof KeyPreviewAnimations) {
-                final KeyPreviewAnimations animation = (KeyPreviewAnimations)tag;
-                animation.startDismiss();
+            if (tag instanceof KeyPreviewAnimators) {
+                final KeyPreviewAnimators animators = (KeyPreviewAnimators)tag;
+                animators.startDismiss();
                 return;
             }
         }
@@ -161,87 +157,60 @@
         }
 
         // Show preview with animation.
-        final Animator showUpAnimation = createShowUpAniation(key, keyPreviewView);
-        final Animator dismissAnimation = createDismissAnimation(key, keyPreviewView);
-        final KeyPreviewAnimations animation = new KeyPreviewAnimations(
-                showUpAnimation, dismissAnimation);
-        keyPreviewView.setTag(animation);
-        animation.startShowUp();
+        final Animator showUpAnimator = createShowUpAnimator(key, keyPreviewView);
+        final Animator dismissAnimator = createDismissAnimator(key, keyPreviewView);
+        final KeyPreviewAnimators animators = new KeyPreviewAnimators(
+                showUpAnimator, dismissAnimator);
+        keyPreviewView.setTag(animators);
+        animators.startShowUp();
     }
 
-    private static final float KEY_PREVIEW_SHOW_UP_END_SCALE = 1.0f;
-    private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR =
-            new AccelerateInterpolator();
-    private static final DecelerateInterpolator DECELERATE_INTERPOLATOR =
-            new DecelerateInterpolator();
-
-    private Animator createShowUpAniation(final Key key, final KeyPreviewView keyPreviewView) {
-        // TODO: Optimization for no scale animation and no duration.
-        final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat(
-                keyPreviewView, View.SCALE_X, mParams.getShowUpStartScale(),
-                KEY_PREVIEW_SHOW_UP_END_SCALE);
-        final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat(
-                keyPreviewView, View.SCALE_Y, mParams.getShowUpStartScale(),
-                KEY_PREVIEW_SHOW_UP_END_SCALE);
-        final AnimatorSet showUpAnimation = new AnimatorSet();
-        showUpAnimation.play(scaleXAnimation).with(scaleYAnimation);
-        showUpAnimation.setDuration(mParams.getShowUpDuration());
-        showUpAnimation.setInterpolator(DECELERATE_INTERPOLATOR);
-        showUpAnimation.addListener(new AnimatorListenerAdapter() {
+    public Animator createShowUpAnimator(final Key key, final KeyPreviewView keyPreviewView) {
+        final Animator animator = mParams.createShowUpAnimator(keyPreviewView);
+        animator.addListener(new AnimatorListenerAdapter() {
             @Override
-            public void onAnimationStart(final Animator animation) {
+            public void onAnimationStart(final Animator animator) {
                 showKeyPreview(key, keyPreviewView, false /* withAnimation */);
             }
         });
-        return showUpAnimation;
+        return animator;
     }
 
-    private Animator createDismissAnimation(final Key key, final KeyPreviewView keyPreviewView) {
-        // TODO: Optimization for no scale animation and no duration.
-        final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat(
-                keyPreviewView, View.SCALE_X, mParams.getDismissEndScale());
-        final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat(
-                keyPreviewView, View.SCALE_Y, mParams.getDismissEndScale());
-        final AnimatorSet dismissAnimation = new AnimatorSet();
-        dismissAnimation.play(scaleXAnimation).with(scaleYAnimation);
-        final int dismissDuration = Math.min(
-                mParams.getDismissDuration(), mParams.getLingerTimeout());
-        dismissAnimation.setDuration(dismissDuration);
-        dismissAnimation.setInterpolator(ACCELERATE_INTERPOLATOR);
-        dismissAnimation.addListener(new AnimatorListenerAdapter() {
+    private Animator createDismissAnimator(final Key key, final KeyPreviewView keyPreviewView) {
+        final Animator animator = mParams.createDismissAnimator(keyPreviewView);
+        animator.addListener(new AnimatorListenerAdapter() {
             @Override
-            public void onAnimationEnd(final Animator animation) {
+            public void onAnimationEnd(final Animator animator) {
                 dismissKeyPreview(key, false /* withAnimation */);
             }
         });
-        return dismissAnimation;
+        return animator;
     }
 
-    private static class KeyPreviewAnimations extends AnimatorListenerAdapter {
-        private final Animator mShowUpAnimation;
-        private final Animator mDismissAnimation;
+    private static class KeyPreviewAnimators extends AnimatorListenerAdapter {
+        private final Animator mShowUpAnimator;
+        private final Animator mDismissAnimator;
 
-        public KeyPreviewAnimations(final Animator showUpAnimation,
-                final Animator dismissAnimation) {
-            mShowUpAnimation = showUpAnimation;
-            mDismissAnimation = dismissAnimation;
+        public KeyPreviewAnimators(final Animator showUpAnimator, final Animator dismissAnimator) {
+            mShowUpAnimator = showUpAnimator;
+            mDismissAnimator = dismissAnimator;
         }
 
         public void startShowUp() {
-            mShowUpAnimation.start();
+            mShowUpAnimator.start();
         }
 
         public void startDismiss() {
-            if (mShowUpAnimation.isRunning()) {
-                mShowUpAnimation.addListener(this);
+            if (mShowUpAnimator.isRunning()) {
+                mShowUpAnimator.addListener(this);
                 return;
             }
-            mDismissAnimation.start();
+            mDismissAnimator.start();
         }
 
         @Override
-        public void onAnimationEnd(final Animator animation) {
-            mDismissAnimation.start();
+        public void onAnimationEnd(final Animator animator) {
+            mDismissAnimator.start();
         }
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
index 68c9831..5ed39f9 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
@@ -16,8 +16,14 @@
 
 package com.android.inputmethod.keyboard.internal;
 
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.content.res.TypedArray;
 import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
 
 import com.android.inputmethod.latin.R;
 
@@ -26,10 +32,15 @@
     public final int mPreviewOffset;
     public final int mPreviewHeight;
     public final int mPreviewBackgroundResId;
+    private final int mShowUpAnimatorResId;
+    private final int mDismissAnimatorResId;
+    private boolean mHasCustomAnimationParams;
     private int mShowUpDuration;
     private int mDismissDuration;
-    private float mShowUpStartScale;
-    private float mDismissEndScale;
+    private float mShowUpStartXScale;
+    private float mShowUpStartYScale;
+    private float mDismissEndXScale;
+    private float mDismissEndYScale;
     private int mLingerTimeout;
     private boolean mShowPopup = true;
 
@@ -67,6 +78,10 @@
                 R.styleable.MainKeyboardView_keyPreviewBackground, 0);
         mLingerTimeout = mainKeyboardViewAttr.getInt(
                 R.styleable.MainKeyboardView_keyPreviewLingerTimeout, 0);
+        mShowUpAnimatorResId = mainKeyboardViewAttr.getResourceId(
+                R.styleable.MainKeyboardView_keyPreviewShowUpAnimator, 0);
+        mDismissAnimatorResId = mainKeyboardViewAttr.getResourceId(
+                R.styleable.MainKeyboardView_keyPreviewDismissAnimator, 0);
     }
 
     public void setVisibleOffset(final int previewVisibleOffset) {
@@ -112,27 +127,62 @@
         return mLingerTimeout;
     }
 
-    public void setAnimationParams(final float showUpStartScale, final int showUpDuration,
-            final float dismissEndScale, final int dismissDuration) {
-        mShowUpStartScale = showUpStartScale;
+    public void setAnimationParams(final boolean hasCustomAnimationParams,
+            final float showUpStartXScale, final float showUpStartYScale, final int showUpDuration,
+            final float dismissEndXScale, final float dismissEndYScale, final int dismissDuration) {
+        mHasCustomAnimationParams = hasCustomAnimationParams;
+        mShowUpStartXScale = showUpStartXScale;
+        mShowUpStartYScale = showUpStartYScale;
         mShowUpDuration = showUpDuration;
-        mDismissEndScale = dismissEndScale;
+        mDismissEndXScale = dismissEndXScale;
+        mDismissEndYScale = dismissEndYScale;
         mDismissDuration = dismissDuration;
     }
 
-    public float getShowUpStartScale() {
-        return mShowUpStartScale;
+    private static final float KEY_PREVIEW_SHOW_UP_END_SCALE = 1.0f;
+    private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR =
+            new AccelerateInterpolator();
+    private static final DecelerateInterpolator DECELERATE_INTERPOLATOR =
+            new DecelerateInterpolator();
+
+    public Animator createShowUpAnimator(final View target) {
+        if (mHasCustomAnimationParams) {
+            final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(
+                    target, View.SCALE_X, mShowUpStartXScale,
+                    KEY_PREVIEW_SHOW_UP_END_SCALE);
+            final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(
+                    target, View.SCALE_Y, mShowUpStartYScale,
+                    KEY_PREVIEW_SHOW_UP_END_SCALE);
+            final AnimatorSet showUpAnimator = new AnimatorSet();
+            showUpAnimator.play(scaleXAnimator).with(scaleYAnimator);
+            showUpAnimator.setDuration(mShowUpDuration);
+            showUpAnimator.setInterpolator(DECELERATE_INTERPOLATOR);
+            return showUpAnimator;
+        }
+        final Animator animator = AnimatorInflater.loadAnimator(
+                target.getContext(), mShowUpAnimatorResId);
+        animator.setTarget(target);
+        animator.setInterpolator(DECELERATE_INTERPOLATOR);
+        return animator;
     }
 
-    public int getShowUpDuration() {
-        return mShowUpDuration;
-    }
-
-    public float getDismissEndScale() {
-        return mDismissEndScale;
-    }
-
-    public int getDismissDuration() {
-        return mDismissDuration;
+    public Animator createDismissAnimator(final View target) {
+        if (mHasCustomAnimationParams) {
+            final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(
+                    target, View.SCALE_X, mDismissEndXScale);
+            final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(
+                    target, View.SCALE_Y, mDismissEndYScale);
+            final AnimatorSet dismissAnimator = new AnimatorSet();
+            dismissAnimator.play(scaleXAnimator).with(scaleYAnimator);
+            final int dismissDuration = Math.min(mDismissDuration, mLingerTimeout);
+            dismissAnimator.setDuration(dismissDuration);
+            dismissAnimator.setInterpolator(ACCELERATE_INTERPOLATOR);
+            return dismissAnimator;
+        }
+        final Animator animator = AnimatorInflater.loadAnimator(
+                target.getContext(), mDismissAnimatorResId);
+        animator.setTarget(target);
+        animator.setInterpolator(ACCELERATE_INTERPOLATOR);
+        return animator;
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 693e1cd..2e10875 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -83,7 +83,6 @@
     public static final String DIR_NAME_SUFFIX_FOR_RECORD_MIGRATION = ".migrating";
 
     private long mNativeDict;
-    private final Locale mLocale;
     private final long mDictSize;
     private final String mDictFilePath;
     private final boolean mUseFullEditDistance;
@@ -117,8 +116,7 @@
     public BinaryDictionary(final String filename, final long offset, final long length,
             final boolean useFullEditDistance, final Locale locale, final String dictType,
             final boolean isUpdatable) {
-        super(dictType);
-        mLocale = locale;
+        super(dictType, locale);
         mDictSize = length;
         mDictFilePath = filename;
         mIsUpdatable = isUpdatable;
@@ -138,8 +136,7 @@
     public BinaryDictionary(final String filename, final boolean useFullEditDistance,
             final Locale locale, final String dictType, final long formatVersion,
             final Map<String, String> attributeMap) {
-        super(dictType);
-        mLocale = locale;
+        super(dictType, locale);
         mDictSize = 0;
         mDictFilePath = filename;
         // On memory dictionary is always updatable.
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 7294813..02d5edd 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -57,6 +57,13 @@
         @SuppressWarnings("dep-ann")
         public static final String FORCE_ASCII = "forceAscii";
 
+        /**
+         * The private IME option used to suppress the floating gesture preview for a given text
+         * field. This overrides the corresponding keyboard settings preference.
+         * {@link com.android.inputmethod.latin.settings.SettingsValues#mGestureFloatingPreviewTextEnabled}
+         */
+        public static final String NO_FLOATING_GESTURE_PREVIEW = "noGestureFloatingPreview";
+
         private ImeOption() {
             // This utility class is not publicly instantiable.
         }
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 560ced9..2f79c76 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -16,12 +16,12 @@
 
 package com.android.inputmethod.latin;
 
-import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
 
 import java.util.ArrayList;
+import java.util.Locale;
 
 /**
  * Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key
@@ -62,9 +62,12 @@
     // Contextual dictionary.
     public static final String TYPE_CONTEXTUAL = "contextual";
     public final String mDictType;
+    // The locale for this dictionary. May be null if unknown (phony dictionary for example).
+    public final Locale mLocale;
 
-    public Dictionary(final String dictType) {
+    public Dictionary(final String dictType, final Locale locale) {
         mDictType = dictType;
+        mLocale = locale;
     }
 
     /**
@@ -162,7 +165,7 @@
     private static class PhonyDictionary extends Dictionary {
         // This class is not publicly instantiable.
         private PhonyDictionary(final String type) {
-            super(type);
+            super(type, null);
         }
 
         @Override
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index 2b4c54d..ca5e937 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -25,6 +25,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Locale;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -34,13 +35,14 @@
     private final String TAG = DictionaryCollection.class.getSimpleName();
     protected final CopyOnWriteArrayList<Dictionary> mDictionaries;
 
-    public DictionaryCollection(final String dictType) {
-        super(dictType);
+    public DictionaryCollection(final String dictType, final Locale locale) {
+        super(dictType, locale);
         mDictionaries = new CopyOnWriteArrayList<>();
     }
 
-    public DictionaryCollection(final String dictType, final Dictionary... dictionaries) {
-        super(dictType);
+    public DictionaryCollection(final String dictType, final Locale locale,
+            final Dictionary... dictionaries) {
+        super(dictType, locale);
         if (null == dictionaries) {
             mDictionaries = new CopyOnWriteArrayList<>();
         } else {
@@ -49,8 +51,9 @@
         }
     }
 
-    public DictionaryCollection(final String dictType, final Collection<Dictionary> dictionaries) {
-        super(dictType);
+    public DictionaryCollection(final String dictType, final Locale locale,
+            final Collection<Dictionary> dictionaries) {
+        super(dictType, locale);
         mDictionaries = new CopyOnWriteArrayList<>(dictionaries);
         mDictionaries.removeAll(Collections.singleton(null));
     }
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 60d6bc3..480bd1f 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -33,6 +33,7 @@
 import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
 import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
 import com.android.inputmethod.latin.utils.DistracterFilter;
+import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatchesAndSuggestions;
 import com.android.inputmethod.latin.utils.DistracterFilterCheckingIsInDictionary;
 import com.android.inputmethod.latin.utils.ExecutorUtils;
 import com.android.inputmethod.latin.utils.LanguageModelParam;
@@ -59,6 +60,7 @@
     // HACK: This threshold is being used when adding a capitalized entry in the User History
     // dictionary.
     private static final int CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT = 140;
+    private static final int MAX_DICTIONARY_FACILITATOR_CACHE_SIZE = 3;
 
     private Dictionaries mDictionaries = new Dictionaries();
     private boolean mIsUserDictEnabled = false;
@@ -66,6 +68,7 @@
     // To synchronize assigning mDictionaries to ensure closing dictionaries.
     private final Object mLock = new Object();
     private final DistracterFilter mDistracterFilter;
+    private final DictionaryFacilitatorLruCache mFacilitatorCacheForPersonalization;
 
     private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS =
             new String[] {
@@ -173,10 +176,14 @@
 
     public DictionaryFacilitator() {
         mDistracterFilter = DistracterFilter.EMPTY_DISTRACTER_FILTER;
+        mFacilitatorCacheForPersonalization = null;
     }
 
-    public DictionaryFacilitator(final DistracterFilter distracterFilter) {
-        mDistracterFilter = distracterFilter;
+    public DictionaryFacilitator(final Context context) {
+        mFacilitatorCacheForPersonalization = new DictionaryFacilitatorLruCache(context,
+                MAX_DICTIONARY_FACILITATOR_CACHE_SIZE, "" /* dictionaryNamePrefix */);
+        mDistracterFilter = new DistracterFilterCheckingExactMatchesAndSuggestions(context,
+                mFacilitatorCacheForPersonalization);
     }
 
     public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) {
@@ -351,6 +358,9 @@
         for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
             dictionaries.closeDict(dictType);
         }
+        if (mFacilitatorCacheForPersonalization != null) {
+            mFacilitatorCacheForPersonalization.evictAll();
+        }
         mDistracterFilter.close();
     }
 
@@ -493,7 +503,7 @@
             final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) {
         final Dictionaries dictionaries = mDictionaries;
         final SuggestionResults suggestionResults =
-                new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS);
+                new SuggestionResults(SuggestedWords.MAX_SUGGESTIONS);
         final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT };
         for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
             final Dictionary dictionary = dictionaries.getDict(dictType);
@@ -597,11 +607,15 @@
             }
             return;
         }
+        // TODO: Get locale from personalizationDataChunk.mDetectedLanguage.
+        final Locale dataChunkLocale = getLocale();
+        final DictionaryFacilitator dictionaryFacilitatorForLocale =
+                mFacilitatorCacheForPersonalization.get(dataChunkLocale);
         final ArrayList<LanguageModelParam> languageModelParams =
                 LanguageModelParam.createLanguageModelParamsFrom(
                         personalizationDataChunk.mTokens,
                         personalizationDataChunk.mTimestampInSeconds,
-                        this /* dictionaryFacilitator */, spacingAndPunctuations,
+                        dictionaryFacilitatorForLocale, spacingAndPunctuations,
                         new DistracterFilterCheckingIsInDictionary(
                                 mDistracterFilter, personalizationDict));
         if (languageModelParams == null || languageModelParams.isEmpty()) {
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
index 59de4f8..3459b42 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -50,7 +50,7 @@
             final Locale locale, final boolean useFullEditDistance) {
         if (null == locale) {
             Log.e(TAG, "No locale defined for dictionary");
-            return new DictionaryCollection(Dictionary.TYPE_MAIN,
+            return new DictionaryCollection(Dictionary.TYPE_MAIN, locale,
                     createReadOnlyBinaryDictionary(context, locale));
         }
 
@@ -75,7 +75,7 @@
         // If the list is empty, that means we should not use any dictionary (for example, the user
         // explicitly disabled the main dictionary), so the following is okay. dictList is never
         // null, but if for some reason it is, DictionaryCollection handles it gracefully.
-        return new DictionaryCollection(Dictionary.TYPE_MAIN, dictList);
+        return new DictionaryCollection(Dictionary.TYPE_MAIN, locale, dictList);
     }
 
     /**
@@ -188,7 +188,7 @@
     public static Dictionary createDictionaryForTest(final AssetFileAddress[] dictionaryList,
             final boolean useFullEditDistance, Locale locale) {
         final DictionaryCollection dictionaryCollection =
-                new DictionaryCollection(Dictionary.TYPE_MAIN);
+                new DictionaryCollection(Dictionary.TYPE_MAIN, locale);
         for (final AssetFileAddress address : dictionaryList) {
             final ReadOnlyBinaryDictionary readOnlyBinaryDictionary = new ReadOnlyBinaryDictionary(
                     address.mFilename, address.mOffset, address.mLength, useFullEditDistance,
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index de38403..a1dd67f 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -86,9 +86,6 @@
      */
     private final String mDictName;
 
-    /** Dictionary locale */
-    private final Locale mLocale;
-
     /** Dictionary file */
     private final File mDictFile;
 
@@ -137,10 +134,9 @@
      */
     public ExpandableBinaryDictionary(final Context context, final String dictName,
             final Locale locale, final String dictType, final File dictFile) {
-        super(dictType);
+        super(dictType, locale);
         mDictName = dictName;
         mContext = context;
-        mLocale = locale;
         mDictFile = getDictFile(context, dictName, dictFile);
         mBinaryDictionary = null;
         mIsReloading = new AtomicBoolean();
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index ebe4361..782e182 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.latin;
 
+import static com.android.inputmethod.latin.Constants.ImeOption.NO_FLOATING_GESTURE_PREVIEW;
 import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE;
 import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT;
 
@@ -42,6 +43,12 @@
     final public boolean mApplicationSpecifiedCompletionOn;
     final public boolean mShouldInsertSpacesAutomatically;
     final public boolean mShouldShowVoiceInputKey;
+    /**
+     * Whether the floating gesture preview should be disabled. If true, this should override the
+     * corresponding keyboard settings preference, always suppressing the floating preview text.
+     * {@link com.android.inputmethod.latin.settings.SettingsValues#mGestureFloatingPreviewTextEnabled}
+     */
+    final public boolean mDisableGestureFloatingPreviewText;
     final private int mInputType;
     final private EditorInfo mEditorInfo;
     final private String mPackageNameForPrivateImeOptions;
@@ -76,6 +83,7 @@
             mApplicationSpecifiedCompletionOn = false;
             mShouldInsertSpacesAutomatically = false;
             mShouldShowVoiceInputKey = false;
+            mDisableGestureFloatingPreviewText = false;
             return;
         }
         // inputClass == InputType.TYPE_CLASS_TEXT
@@ -107,6 +115,9 @@
                 || hasNoMicrophoneKeyOption();
         mShouldShowVoiceInputKey = !noMicrophone;
 
+        mDisableGestureFloatingPreviewText = InputAttributes.inPrivateImeOptions(
+                mPackageNameForPrivateImeOptions, NO_FLOATING_GESTURE_PREVIEW, editorInfo);
+
         // If it's a browser edit field and auto correct is not ON explicitly, then
         // disable auto correction, but keep suggestions on.
         // If NO_SUGGESTIONS is set, don't do prediction.
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index ee0ff5c..c55acd4 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -93,6 +93,7 @@
 import com.android.inputmethod.latin.utils.JniUtils;
 import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
 import com.android.inputmethod.latin.utils.StatsUtils;
+import com.android.inputmethod.latin.utils.StatsUtilsManager;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 import com.android.inputmethod.latin.utils.ViewLayoutUtils;
 
@@ -130,8 +131,7 @@
 
     private final Settings mSettings;
     private final DictionaryFacilitator mDictionaryFacilitator =
-            new DictionaryFacilitator(
-                    new DistracterFilterCheckingExactMatchesAndSuggestions(this /* context */));
+            new DictionaryFacilitator(this /* context */);
     // TODO: Move from LatinIME.
     private final PersonalizationDictionaryUpdater mPersonalizationDictionaryUpdater =
             new PersonalizationDictionaryUpdater(this /* context */, mDictionaryFacilitator);
@@ -158,6 +158,7 @@
     private final SubtypeSwitcher mSubtypeSwitcher;
     private final SubtypeState mSubtypeState = new SubtypeState();
     private final SpecialKeyDetector mSpecialKeyDetector;
+    private StatsUtilsManager mStatsUtilsManager;
 
     // Object for reacting to adding/removing a dictionary pack.
     private final BroadcastReceiver mDictionaryPackInstallReceiver =
@@ -538,6 +539,7 @@
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         mKeyboardSwitcher = KeyboardSwitcher.getInstance();
         mSpecialKeyDetector = new SpecialKeyDetector(this);
+        mStatsUtilsManager = StatsUtilsManager.getInstance();
         mIsHardwareAcceleratedDrawingEnabled =
                 InputMethodServiceCompatUtils.enableHardwareAcceleration(this);
         Log.i(TAG, "Hardware accelerated drawing: " + mIsHardwareAcceleratedDrawingEnabled);
@@ -553,8 +555,7 @@
         KeyboardSwitcher.init(this);
         AudioAndHapticFeedbackManager.init(this);
         AccessibilityUtils.init(this);
-        StatsUtils.init(this);
-
+        mStatsUtilsManager.onCreate(this /* context */);
         super.onCreate();
 
         mHandler.onCreate();
@@ -586,7 +587,6 @@
         registerReceiver(mDictionaryDumpBroadcastReceiver, dictDumpFilter);
 
         DictionaryDecayBroadcastReciever.setUpIntervalAlarmForDictionaryDecaying(this);
-
         StatsUtils.onCreate(mSettings.getCurrent(), mRichImm);
     }
 
@@ -609,7 +609,7 @@
         mDictionaryFacilitator.updateEnabledSubtypes(mRichImm.getMyEnabledInputMethodSubtypeList(
                 true /* allowsImplicitlySelectedSubtypes */));
         refreshPersonalizationDictionarySession(currentSettingsValues);
-        StatsUtils.onLoadSettings(currentSettingsValues);
+        mStatsUtilsManager.onLoadSettings(currentSettingsValues);
     }
 
     private void refreshPersonalizationDictionarySession(
@@ -699,7 +699,7 @@
         unregisterReceiver(mConnectivityAndRingerModeChangeReceiver);
         unregisterReceiver(mDictionaryPackInstallReceiver);
         unregisterReceiver(mDictionaryDumpBroadcastReceiver);
-        StatsUtils.onDestroy();
+        mStatsUtilsManager.onDestroy();
         super.onDestroy();
     }
 
@@ -1179,10 +1179,6 @@
         return mInputLogic.getCurrentRecapitalizeState();
     }
 
-    public Locale getCurrentSubtypeLocale() {
-        return mSubtypeSwitcher.getCurrentSubtypeLocale();
-    }
-
     /**
      * @param codePoints code points to get coordinates for.
      * @return x,y coordinates for this keyboard, as a flattened array.
@@ -1496,7 +1492,7 @@
         }
         final String wordToShow;
         if (CapsModeUtils.isAutoCapsMode(mInputLogic.mLastComposedWord.mCapitalizedMode)) {
-            wordToShow = word.toLowerCase(getCurrentSubtypeLocale());
+            wordToShow = word.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
         } else {
             wordToShow = word;
         }
diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
index 5d4fc58..ecf25c2 100644
--- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
@@ -40,7 +40,7 @@
 
     public ReadOnlyBinaryDictionary(final String filename, final long offset, final long length,
             final boolean useFullEditDistance, final Locale locale, final String dictType) {
-        super(dictType);
+        super(dictType, locale);
         mBinaryDictionary = new BinaryDictionary(filename, offset, length, useFullEditDistance,
                 locale, dictType, false /* isUpdatable */);
     }
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 6779351..9e4aa40 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -84,7 +84,7 @@
 
     private static ArrayList<SuggestedWordInfo> getTransformedSuggestedWordInfoList(
             final WordComposer wordComposer, final SuggestionResults results,
-            final int trailingSingleQuotesCount) {
+            final int trailingSingleQuotesCount, final Locale defaultLocale) {
         final boolean shouldMakeSuggestionsAllUpperCase = wordComposer.isAllUpperCase()
                 && !wordComposer.isResumed();
         final boolean isOnlyFirstCharCapitalized =
@@ -96,9 +96,11 @@
                 || 0 != trailingSingleQuotesCount) {
             for (int i = 0; i < suggestionsCount; ++i) {
                 final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
+                final Locale wordLocale = wordInfo.mSourceDict.mLocale;
                 final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
-                        wordInfo, results.mLocale, shouldMakeSuggestionsAllUpperCase,
-                        isOnlyFirstCharCapitalized, trailingSingleQuotesCount);
+                        wordInfo, null == wordLocale ? defaultLocale : wordLocale,
+                        shouldMakeSuggestionsAllUpperCase, isOnlyFirstCharCapitalized,
+                        trailingSingleQuotesCount);
                 suggestionsContainer.set(i, transformedWordInfo);
             }
         }
@@ -134,7 +136,7 @@
                 SESSION_ID_TYPING);
         final ArrayList<SuggestedWordInfo> suggestionsContainer =
                 getTransformedSuggestedWordInfoList(wordComposer, suggestionResults,
-                        trailingSingleQuotesCount);
+                        trailingSingleQuotesCount, mDictionaryFacilitator.getLocale());
         final boolean didRemoveTypedWord =
                 SuggestedWordInfo.removeDups(wordComposer.getTypedWord(), suggestionsContainer);
 
@@ -208,6 +210,7 @@
         final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults(
                 wordComposer, prevWordsInfo, proximityInfo, settingsValuesForSuggestion,
                 SESSION_ID_GESTURE);
+        final Locale defaultLocale = mDictionaryFacilitator.getLocale();
         final ArrayList<SuggestedWordInfo> suggestionsContainer =
                 new ArrayList<>(suggestionResults);
         final int suggestionsCount = suggestionsContainer.size();
@@ -216,9 +219,10 @@
         if (isFirstCharCapitalized || isAllUpperCase) {
             for (int i = 0; i < suggestionsCount; ++i) {
                 final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
+                final Locale wordlocale = wordInfo.mSourceDict.mLocale;
                 final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
-                        wordInfo, suggestionResults.mLocale, isAllUpperCase, isFirstCharCapitalized,
-                        0 /* trailingSingleQuotesCount */);
+                        wordInfo, null == wordlocale ? defaultLocale : wordlocale, isAllUpperCase,
+                        isFirstCharCapitalized, 0 /* trailingSingleQuotesCount */);
                 suggestionsContainer.set(i, transformedWordInfo);
             }
         }
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 1eebabe..6bc3da8 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -429,7 +429,7 @@
      */
     @UsedForTesting
     public SuggestedWordInfo getTypedWordInfoOrNull() {
-        if (this == EMPTY) {
+        if (SuggestedWords.INDEX_OF_TYPED_WORD >= size()) {
             return null;
         }
         final SuggestedWordInfo info = getInfo(SuggestedWords.INDEX_OF_TYPED_WORD);
diff --git a/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java
index e4ee426..123ab20 100644
--- a/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java
+++ b/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java
@@ -17,21 +17,16 @@
 package com.android.inputmethod.latin;
 
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
 import android.os.Process;
-import android.preference.PreferenceManager;
 import android.util.Log;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.compat.IntentCompatUtils;
-import com.android.inputmethod.latin.settings.Settings;
+import com.android.inputmethod.keyboard.KeyboardLayoutSet;
 import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager;
-import com.android.inputmethod.latin.setup.SetupActivity;
 import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils;
 
 /**
@@ -58,6 +53,9 @@
  * When a multiuser account has been created, {@link Intent#ACTION_USER_INITIALIZE} is received
  * by this receiver and it checks the whether the setup wizard's icon should be appeared or not on
  * the launcher depending on which partition this IME is installed.
+ *
+ * When the system locale has been changed, {@link Intent#ACTION_LOCALE_CHANGED} is received by
+ * this receiver and the {@link KeyboardLayoutSet}'s cache is cleared.
  */
 public final class SystemBroadcastReceiver extends BroadcastReceiver {
     private static final String TAG = SystemBroadcastReceiver.class.getSimpleName();
@@ -67,21 +65,22 @@
         final String intentAction = intent.getAction();
         if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intentAction)) {
             Log.i(TAG, "Package has been replaced: " + context.getPackageName());
-        } else if (Intent.ACTION_BOOT_COMPLETED.equals(intentAction)) {
-            Log.i(TAG, "Boot has been completed");
-        } else if (IntentCompatUtils.is_ACTION_USER_INITIALIZE(intentAction)) {
-            Log.i(TAG, "User initialize");
-        }
-
-        LauncherIconVisibilityManager.onReceiveGlobalIntent(intentAction, context);
-
-        if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intentAction)) {
             // Need to restore additional subtypes because system always clears additional
             // subtypes when the package is replaced.
             RichInputMethodManager.init(context);
             final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
             final InputMethodSubtype[] additionalSubtypes = richImm.getAdditionalSubtypes(context);
             richImm.setAdditionalInputMethodSubtypes(additionalSubtypes);
+            LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context);
+        } else if (Intent.ACTION_BOOT_COMPLETED.equals(intentAction)) {
+            Log.i(TAG, "Boot has been completed");
+            LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context);
+        } else if (IntentCompatUtils.is_ACTION_USER_INITIALIZE(intentAction)) {
+            Log.i(TAG, "User initialize");
+            LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context);
+        } else if (Intent.ACTION_LOCALE_CHANGED.equals(intentAction)) {
+            Log.i(TAG, "System locale changed");
+            KeyboardLayoutSet.onSystemLocaleChanged();
         }
 
         // The process that hosts this broadcast receiver is invoked and remains alive even after
diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
index 6d7f53c..d53a616 100644
--- a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
@@ -30,11 +30,14 @@
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
 import android.preference.PreferenceGroup;
+import android.support.v4.view.ViewCompat;
 import android.util.Pair;
+import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 import android.widget.ArrayAdapter;
@@ -43,6 +46,7 @@
 import android.widget.Toast;
 
 import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
+import com.android.inputmethod.compat.ViewCompatUtils;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.RichInputMethodManager;
 import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
@@ -233,6 +237,12 @@
             mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter());
             mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner);
             mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter());
+            // All keyboard layout names are in the Latin script and thus left to right. That means
+            // the view would align them to the left even if the system locale is RTL, but that
+            // would look strange. To fix this, we align them to the view's start, which will be
+            // natural for any direction.
+            ViewCompatUtils.setTextAlignment(
+                    mKeyboardLayoutSetSpinner, ViewCompatUtils.TEXT_ALIGNMENT_VIEW_START);
             return v;
         }
 
@@ -398,6 +408,16 @@
     }
 
     @Override
+    public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
+            final Bundle savedInstanceState) {
+        final View view = super.onCreateView(inflater, container, savedInstanceState);
+        // For correct display in RTL locales, we need to set the layout direction of the
+        // fragment's top view.
+        ViewCompat.setLayoutDirection(view, ViewCompat.LAYOUT_DIRECTION_LOCALE);
+        return view;
+    }
+
+    @Override
     public void onActivityCreated(final Bundle savedInstanceState) {
         final Context context = getActivity();
         mSubtypeLocaleAdapter = new SubtypeLocaleAdapter(context);
@@ -422,7 +442,7 @@
                     KEY_SUBTYPE_FOR_SUBTYPE_ENABLER);
             final SubtypePreference subtypePref = (SubtypePreference)findPreference(
                     mSubtypePreferenceKeyForSubtypeEnabler);
-            mSubtypeEnablerNotificationDialog = createDialog(subtypePref);
+            mSubtypeEnablerNotificationDialog = createDialog();
             mSubtypeEnablerNotificationDialog.show();
         }
     }
@@ -476,7 +496,7 @@
             if (findDuplicatedSubtype(subtype) == null) {
                 mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
                 mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey();
-                mSubtypeEnablerNotificationDialog = createDialog(subtypePref);
+                mSubtypeEnablerNotificationDialog = createDialog();
                 mSubtypeEnablerNotificationDialog.show();
                 return;
             }
@@ -513,7 +533,7 @@
                 localeString, keyboardLayoutSetName);
     }
 
-    private AlertDialog createDialog(final SubtypePreference subtypePref) {
+    private AlertDialog createDialog() {
         final AlertDialog.Builder builder = new AlertDialog.Builder(
                 DialogUtils.getPlatformDialogThemeContext(getActivity()));
         builder.setTitle(R.string.custom_input_styles_title)
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
index 63d848e..48f4c75 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
@@ -23,10 +23,16 @@
             "force_physical_keyboard_special_key";
     public static final String PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD =
             "pref_show_ui_to_accept_typed_word";
-    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 =
-            "pref_key_preview_dismiss_end_scale";
+    public static final String PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS =
+            "pref_has_custom_key_preview_animation_params";
+    public static final String PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE =
+            "pref_key_preview_show_up_start_x_scale";
+    public static final String PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE =
+            "pref_key_preview_show_up_start_y_scale";
+    public static final String PREF_KEY_PREVIEW_DISMISS_END_X_SCALE =
+            "pref_key_preview_dismiss_end_x_scale";
+    public static final String PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE =
+            "pref_key_preview_dismiss_end_y_scale";
     public static final String PREF_KEY_PREVIEW_SHOW_UP_DURATION =
             "pref_key_preview_show_up_duration";
     public static final String PREF_KEY_PREVIEW_DISMISS_DURATION =
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
index dc2f88a..5640e20 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
@@ -78,12 +78,18 @@
                 res.getInteger(R.integer.config_key_preview_show_up_duration));
         setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION,
                 res.getInteger(R.integer.config_key_preview_dismiss_duration));
-        setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_SCALE,
-                ResourceUtils.getFloatFromFraction(
-                        res, R.fraction.config_key_preview_show_up_start_scale));
-        setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_SCALE,
-                ResourceUtils.getFloatFromFraction(
-                        res, R.fraction.config_key_preview_dismiss_end_scale));
+        final float defaultKeyPreviewShowUpStartScale = ResourceUtils.getFloatFromFraction(
+                res, R.fraction.config_key_preview_show_up_start_scale);
+        final float defaultKeyPreviewDismissEndScale = ResourceUtils.getFloatFromFraction(
+                res, R.fraction.config_key_preview_dismiss_end_scale);
+        setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE,
+                defaultKeyPreviewShowUpStartScale);
+        setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE,
+                defaultKeyPreviewShowUpStartScale);
+        setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE,
+                defaultKeyPreviewDismissEndScale);
+        setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE,
+                defaultKeyPreviewDismissEndScale);
 
         mServiceNeedsRestart = false;
         mDebugMode = (TwoStatePreference) findPreference(DebugSettings.PREF_DEBUG_MODE);
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 91a2b67..1129195 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -104,10 +104,13 @@
 
     // Debug settings
     public final boolean mIsInternal;
+    public final boolean mHasCustomKeyPreviewAnimationParams;
     public final int mKeyPreviewShowUpDuration;
     public final int mKeyPreviewDismissDuration;
-    public final float mKeyPreviewShowUpStartScale;
-    public final float mKeyPreviewDismissEndScale;
+    public final float mKeyPreviewShowUpStartXScale;
+    public final float mKeyPreviewShowUpStartYScale;
+    public final float mKeyPreviewDismissEndXScale;
+    public final float mKeyPreviewDismissEndYScale;
 
     public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res,
             final InputAttributes inputAttributes) {
@@ -162,8 +165,8 @@
                 autoCorrectionThresholdRawValue);
         mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res);
         mGestureTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true);
-        mGestureFloatingPreviewTextEnabled = prefs.getBoolean(
-                Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true);
+        mGestureFloatingPreviewTextEnabled = !mInputAttributes.mDisableGestureFloatingPreviewText
+                && prefs.getBoolean(Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true);
         mPhraseGestureEnabled = Settings.readPhraseGestureEnabled(prefs, res);
         mAutoCorrectionEnabledPerUserSettings = mAutoCorrectEnabled
                 && !mInputAttributes.mInputTypeNoAutoCorrect;
@@ -179,20 +182,30 @@
         mTextHighlightColorForAddToDictionaryIndicator = res.getColor(
                 R.color.text_decorator_add_to_dictionary_indicator_text_highlight_color);
         mIsInternal = Settings.isInternal(prefs);
+        mHasCustomKeyPreviewAnimationParams = prefs.getBoolean(
+                DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS, false);
         mKeyPreviewShowUpDuration = Settings.readKeyPreviewAnimationDuration(
                 prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION,
                 res.getInteger(R.integer.config_key_preview_show_up_duration));
         mKeyPreviewDismissDuration = Settings.readKeyPreviewAnimationDuration(
                 prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION,
                 res.getInteger(R.integer.config_key_preview_dismiss_duration));
-        mKeyPreviewShowUpStartScale = Settings.readKeyPreviewAnimationScale(
-                prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_SCALE,
-                ResourceUtils.getFloatFromFraction(
-                        res, R.fraction.config_key_preview_show_up_start_scale));
-        mKeyPreviewDismissEndScale = Settings.readKeyPreviewAnimationScale(
-                prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_SCALE,
-                ResourceUtils.getFloatFromFraction(
-                        res, R.fraction.config_key_preview_dismiss_end_scale));
+        final float defaultKeyPreviewShowUpStartScale = ResourceUtils.getFloatFromFraction(
+                res, R.fraction.config_key_preview_show_up_start_scale);
+        final float defaultKeyPreviewDismissEndScale = ResourceUtils.getFloatFromFraction(
+                res, R.fraction.config_key_preview_dismiss_end_scale);
+        mKeyPreviewShowUpStartXScale = Settings.readKeyPreviewAnimationScale(
+                prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE,
+                defaultKeyPreviewShowUpStartScale);
+        mKeyPreviewShowUpStartYScale = Settings.readKeyPreviewAnimationScale(
+                prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE,
+                defaultKeyPreviewShowUpStartScale);
+        mKeyPreviewDismissEndXScale = Settings.readKeyPreviewAnimationScale(
+                prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE,
+                defaultKeyPreviewDismissEndScale);
+        mKeyPreviewDismissEndYScale = Settings.readKeyPreviewAnimationScale(
+                prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE,
+                defaultKeyPreviewDismissEndScale);
         mDisplayOrientation = res.getConfiguration().orientation;
         mAppWorkarounds = new AsyncResultHolder<>();
         final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo(
@@ -429,10 +442,14 @@
         sb.append("" + mKeyPreviewShowUpDuration);
         sb.append("\n   mKeyPreviewDismissDuration = ");
         sb.append("" + mKeyPreviewDismissDuration);
-        sb.append("\n   mKeyPreviewShowUpStartScale = ");
-        sb.append("" + mKeyPreviewShowUpStartScale);
-        sb.append("\n   mKeyPreviewDismissEndScale = ");
-        sb.append("" + mKeyPreviewDismissEndScale);
+        sb.append("\n   mKeyPreviewShowUpStartScaleX = ");
+        sb.append("" + mKeyPreviewShowUpStartXScale);
+        sb.append("\n   mKeyPreviewShowUpStartScaleY = ");
+        sb.append("" + mKeyPreviewShowUpStartYScale);
+        sb.append("\n   mKeyPreviewDismissEndScaleX = ");
+        sb.append("" + mKeyPreviewDismissEndXScale);
+        sb.append("\n   mKeyPreviewDismissEndScaleY = ");
+        sb.append("" + mKeyPreviewDismissEndYScale);
         return sb.toString();
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java b/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java
index 9585736..3f0b102 100644
--- a/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java
+++ b/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java
@@ -24,7 +24,6 @@
 import android.preference.PreferenceManager;
 import android.util.Log;
 
-import com.android.inputmethod.compat.IntentCompatUtils;
 import com.android.inputmethod.latin.settings.Settings;
 
 /**
@@ -55,14 +54,6 @@
 public final class LauncherIconVisibilityManager {
     private static final String TAG = LauncherIconVisibilityManager.class.getSimpleName();
 
-    public static void onReceiveGlobalIntent(final String action, final Context context) {
-        if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(action) ||
-                Intent.ACTION_BOOT_COMPLETED.equals(action) ||
-                IntentCompatUtils.is_ACTION_USER_INITIALIZE(action)) {
-            updateSetupWizardIconVisibility(context);
-        }
-    }
-
     public static void updateSetupWizardIconVisibility(final Context context) {
         final ComponentName setupWizardActivity = new ComponentName(context, SetupActivity.class);
         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java
index 2207ffe..e10571e 100644
--- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java
@@ -20,13 +20,14 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ConcurrentHashMap;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.text.InputType;
 import android.util.Log;
 import android.util.LruCache;
+import android.util.Pair;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodSubtype;
 
@@ -34,6 +35,7 @@
 import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.KeyboardLayoutSet;
 import com.android.inputmethod.latin.DictionaryFacilitator;
+import com.android.inputmethod.latin.DictionaryFacilitatorLruCache;
 import com.android.inputmethod.latin.PrevWordsInfo;
 import com.android.inputmethod.latin.RichInputMethodSubtype;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -49,15 +51,15 @@
             DistracterFilterCheckingExactMatchesAndSuggestions.class.getSimpleName();
     private static final boolean DEBUG = false;
 
-    private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120;
-    private static final int MAX_DISTRACTERS_CACHE_SIZE = 512;
+    private static final int MAX_DISTRACTERS_CACHE_SIZE = 1024;
 
     private final Context mContext;
-    private final Map<Locale, InputMethodSubtype> mLocaleToSubtypeMap;
-    private final Map<Locale, Keyboard> mLocaleToKeyboardMap;
-    private final DictionaryFacilitator mDictionaryFacilitator;
-    private final LruCache<String, Boolean> mDistractersCache;
-    private Keyboard mKeyboard;
+    private final ConcurrentHashMap<Locale, InputMethodSubtype> mLocaleToSubtypeCache;
+    private final ConcurrentHashMap<Locale, Keyboard> mLocaleToKeyboardCache;
+    private final DictionaryFacilitatorLruCache mDictionaryFacilitatorLruCache;
+    // The key is a pair of a locale and a word. The value indicates the word is a distracter to
+    // words of the locale.
+    private final LruCache<Pair<Locale, String>, Boolean> mDistractersCache;
     private final Object mLock = new Object();
 
     // If the score of the top suggestion exceeds this value, the tested word (e.g.,
@@ -71,19 +73,23 @@
      * Create a DistracterFilter instance.
      *
      * @param context the context.
+     * @param dictionaryFacilitatorLruCache the cache of dictionaryFacilitators that are used for
+     * checking distracters.
      */
-    public DistracterFilterCheckingExactMatchesAndSuggestions(final Context context) {
+    public DistracterFilterCheckingExactMatchesAndSuggestions(final Context context,
+            final DictionaryFacilitatorLruCache dictionaryFacilitatorLruCache) {
         mContext = context;
-        mLocaleToSubtypeMap = new HashMap<>();
-        mLocaleToKeyboardMap = new HashMap<>();
-        mDictionaryFacilitator = new DictionaryFacilitator();
+        mLocaleToSubtypeCache = new ConcurrentHashMap<>();
+        mLocaleToKeyboardCache = new ConcurrentHashMap<>();
+        mDictionaryFacilitatorLruCache = dictionaryFacilitatorLruCache;
         mDistractersCache = new LruCache<>(MAX_DISTRACTERS_CACHE_SIZE);
-        mKeyboard = null;
     }
 
     @Override
     public void close() {
-        mDictionaryFacilitator.closeDictionaries();
+        mLocaleToSubtypeCache.clear();
+        mLocaleToKeyboardCache.clear();
+        mDistractersCache.evictAll();
     }
 
     @Override
@@ -100,29 +106,36 @@
                 newLocaleToSubtypeMap.put(locale, subtype);
             }
         }
-        if (mLocaleToSubtypeMap.equals(newLocaleToSubtypeMap)) {
+        if (mLocaleToSubtypeCache.equals(newLocaleToSubtypeMap)) {
             // Enabled subtypes have not been changed.
             return;
         }
-        synchronized (mLock) {
-            mLocaleToSubtypeMap.clear();
-            mLocaleToSubtypeMap.putAll(newLocaleToSubtypeMap);
-            mLocaleToKeyboardMap.clear();
+        // Update subtype and keyboard map for locales that are in the current mapping.
+        for (final Locale locale: mLocaleToSubtypeCache.keySet()) {
+            if (newLocaleToSubtypeMap.containsKey(locale)) {
+                final InputMethodSubtype newSubtype = newLocaleToSubtypeMap.remove(locale);
+                if (newSubtype.equals(newLocaleToSubtypeMap.get(locale))) {
+                    // Mapping has not been changed.
+                    continue;
+                }
+                mLocaleToSubtypeCache.replace(locale, newSubtype);
+            } else {
+                mLocaleToSubtypeCache.remove(locale);
+            }
+            mLocaleToKeyboardCache.remove(locale);
         }
+        // Add locales that are not in the current mapping.
+        mLocaleToSubtypeCache.putAll(newLocaleToSubtypeMap);
     }
 
-    private void loadKeyboardForLocale(final Locale newLocale) {
-        final Keyboard cachedKeyboard = mLocaleToKeyboardMap.get(newLocale);
+    private Keyboard getKeyboardForLocale(final Locale locale) {
+        final Keyboard cachedKeyboard = mLocaleToKeyboardCache.get(locale);
         if (cachedKeyboard != null) {
-            mKeyboard = cachedKeyboard;
-            return;
+            return cachedKeyboard;
         }
-        final InputMethodSubtype subtype;
-        synchronized (mLock) {
-            subtype = mLocaleToSubtypeMap.get(newLocale);
-        }
+        final InputMethodSubtype subtype = mLocaleToSubtypeCache.get(locale);
         if (subtype == null) {
-            return;
+            return null;
         }
         final EditorInfo editorInfo = new EditorInfo();
         editorInfo.inputType = InputType.TYPE_CLASS_TEXT;
@@ -135,15 +148,9 @@
         builder.setSubtype(new RichInputMethodSubtype(subtype));
         builder.setIsSpellChecker(false /* isSpellChecker */);
         final KeyboardLayoutSet layoutSet = builder.build();
-        mKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
-    }
-
-    private void loadDictionariesForLocale(final Locale newlocale) throws InterruptedException {
-        mDictionaryFacilitator.resetDictionaries(mContext, newlocale,
-                false /* useContactsDict */, false /* usePersonalizedDicts */,
-                false /* forceReloadMainDictionary */, null /* listener */);
-        mDictionaryFacilitator.waitForLoadingMainDictionary(
-                TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS, TimeUnit.SECONDS);
+        final Keyboard newKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
+        mLocaleToKeyboardCache.put(locale, newKeyboard);
+        return newKeyboard;
     }
 
     /**
@@ -161,30 +168,18 @@
         if (locale == null) {
             return false;
         }
-        if (!locale.equals(mDictionaryFacilitator.getLocale())) {
-            synchronized (mLock) {
-                if (!mLocaleToSubtypeMap.containsKey(locale)) {
-                    Log.e(TAG, "Locale " + locale + " is not enabled.");
-                    // TODO: Investigate what we should do for disabled locales.
-                    return false;
-                }
-                loadKeyboardForLocale(locale);
-                // Reset dictionaries for the locale.
-                try {
-                    mDistractersCache.evictAll();
-                    loadDictionariesForLocale(locale);
-                } catch (final InterruptedException e) {
-                    Log.e(TAG, "Interrupted while waiting for loading dicts in DistracterFilter",
-                            e);
-                    return false;
-                }
-            }
+        if (!mLocaleToSubtypeCache.containsKey(locale)) {
+            Log.e(TAG, "Locale " + locale + " is not enabled.");
+            // TODO: Investigate what we should do for disabled locales.
+            return false;
         }
-
+        final DictionaryFacilitator dictionaryFacilitator =
+                mDictionaryFacilitatorLruCache.get(locale);
         if (DEBUG) {
             Log.d(TAG, "testedWord: " + testedWord);
         }
-        final Boolean isCachedDistracter = mDistractersCache.get(testedWord);
+        final Pair<Locale, String> cacheKey = new Pair<>(locale, testedWord);
+        final Boolean isCachedDistracter = mDistractersCache.get(cacheKey);
         if (isCachedDistracter != null && isCachedDistracter) {
             if (DEBUG) {
                 Log.d(TAG, "isDistracter: true (cache hit)");
@@ -193,13 +188,13 @@
         }
 
         final boolean isDistracterCheckedByGetMaxFreqencyOfExactMatches =
-                checkDistracterUsingMaxFreqencyOfExactMatches(testedWord);
+                checkDistracterUsingMaxFreqencyOfExactMatches(dictionaryFacilitator, testedWord);
         if (isDistracterCheckedByGetMaxFreqencyOfExactMatches) {
-            // Add the word to the cache.
-            mDistractersCache.put(testedWord, Boolean.TRUE);
+            // Add the pair of locale and word to the cache.
+            mDistractersCache.put(cacheKey, Boolean.TRUE);
             return true;
         }
-        final boolean isValidWord = mDictionaryFacilitator.isValidWord(testedWord,
+        final boolean isValidWord = dictionaryFacilitator.isValidWord(testedWord,
                 false /* ignoreCase */);
         if (isValidWord) {
             // Valid word is not a distractor.
@@ -209,21 +204,23 @@
             return false;
         }
 
+        final Keyboard keyboard = getKeyboardForLocale(locale);
         final boolean isDistracterCheckedByGetSuggestion =
-                checkDistracterUsingGetSuggestions(testedWord);
+                checkDistracterUsingGetSuggestions(dictionaryFacilitator, keyboard, testedWord);
         if (isDistracterCheckedByGetSuggestion) {
-            // Add the word to the cache.
-            mDistractersCache.put(testedWord, Boolean.TRUE);
+            // Add the pair of locale and word to the cache.
+            mDistractersCache.put(cacheKey, Boolean.TRUE);
             return true;
         }
         return false;
     }
 
-    private boolean checkDistracterUsingMaxFreqencyOfExactMatches(final String testedWord) {
+    private static boolean checkDistracterUsingMaxFreqencyOfExactMatches(
+            final DictionaryFacilitator dictionaryFacilitator, final String testedWord) {
         // The tested word is a distracter when there is a word that is exact matched to the tested
         // word and its probability is higher than the tested word's probability.
-        final int perfectMatchFreq = mDictionaryFacilitator.getFrequency(testedWord);
-        final int exactMatchFreq = mDictionaryFacilitator.getMaxFrequencyOfExactMatches(testedWord);
+        final int perfectMatchFreq = dictionaryFacilitator.getFrequency(testedWord);
+        final int exactMatchFreq = dictionaryFacilitator.getMaxFrequencyOfExactMatches(testedWord);
         final boolean isDistracter = perfectMatchFreq < exactMatchFreq;
         if (DEBUG) {
             Log.d(TAG, "perfectMatchFreq: " + perfectMatchFreq);
@@ -233,8 +230,10 @@
         return isDistracter;
     }
 
-    private boolean checkDistracterUsingGetSuggestions(final String testedWord) {
-        if (mKeyboard == null) {
+    private boolean checkDistracterUsingGetSuggestions(
+            final DictionaryFacilitator dictionaryFacilitator, final Keyboard keyboard,
+            final String testedWord) {
+        if (keyboard == null) {
             return false;
         }
         final SettingsValuesForSuggestion settingsValuesForSuggestion =
@@ -247,24 +246,24 @@
                 testedWord;
         final WordComposer composer = new WordComposer();
         final int[] codePoints = StringUtils.toCodePointArray(testedWord);
-
+        final int[] coordinates = keyboard.getCoordinates(codePoints);
+        composer.setComposingWord(codePoints, coordinates);
+        final SuggestionResults suggestionResults;
         synchronized (mLock) {
-            final int[] coordinates = mKeyboard.getCoordinates(codePoints);
-            composer.setComposingWord(codePoints, coordinates);
-            final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults(
-                    composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, mKeyboard.getProximityInfo(),
+            suggestionResults = dictionaryFacilitator.getSuggestionResults(
+                    composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, keyboard.getProximityInfo(),
                     settingsValuesForSuggestion, 0 /* sessionId */);
-            if (suggestionResults.isEmpty()) {
-                return false;
-            }
-            final SuggestedWordInfo firstSuggestion = suggestionResults.first();
-            final boolean isDistractor = suggestionExceedsDistracterThreshold(
-                    firstSuggestion, consideredWord, DISTRACTER_WORD_SCORE_THRESHOLD);
-            if (DEBUG) {
-                Log.d(TAG, "isDistracter: " + isDistractor);
-            }
-            return isDistractor;
         }
+        if (suggestionResults.isEmpty()) {
+            return false;
+        }
+        final SuggestedWordInfo firstSuggestion = suggestionResults.first();
+        final boolean isDistractor = suggestionExceedsDistracterThreshold(
+                firstSuggestion, consideredWord, DISTRACTER_WORD_SCORE_THRESHOLD);
+        if (DEBUG) {
+            Log.d(TAG, "isDistracter: " + isDistractor);
+        }
+        return isDistractor;
     }
 
     private static boolean suggestionExceedsDistracterThreshold(final SuggestedWordInfo suggestion,
diff --git a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java
index 7170bd7..eaa5743 100644
--- a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java
+++ b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java
@@ -30,18 +30,16 @@
  * than its limit
  */
 public final class SuggestionResults extends TreeSet<SuggestedWordInfo> {
-    public final Locale mLocale;
     public final ArrayList<SuggestedWordInfo> mRawSuggestions;
     private final int mCapacity;
 
-    public SuggestionResults(final Locale locale, final int capacity) {
-        this(locale, sSuggestedWordInfoComparator, capacity);
+    public SuggestionResults(final int capacity) {
+        this(sSuggestedWordInfoComparator, capacity);
     }
 
-    public SuggestionResults(final Locale locale, final Comparator<SuggestedWordInfo> comparator,
+    public SuggestionResults(final Comparator<SuggestedWordInfo> comparator,
             final int capacity) {
         super(comparator);
-        mLocale = locale;
         mCapacity = capacity;
         if (ProductionFlags.INCLUDE_RAW_SUGGESTIONS) {
             mRawSuggestions = new ArrayList<>();
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java
index 96f9255..9e795db 100644
--- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java
@@ -18,34 +18,33 @@
 
 import android.content.res.Resources;
 import android.test.suitebuilder.annotation.MediumTest;
-import android.text.InputType;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.RichInputMethodManager;
 import com.android.inputmethod.latin.utils.RunInLocale;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
+import java.util.Locale;
+
 @MediumTest
-public class KeyboardLayoutSetActionLabelKlpTests extends KeyboardLayoutSetTestsBase {
+public class KeyboardLayoutSetActionLabelKlpTests extends KeyboardLayoutSetActionLabelLxxTests {
     @Override
     protected int getKeyboardThemeForTests() {
         return KeyboardTheme.THEME_ID_KLP;
     }
 
-    private static void doTestActionKey(final String tag, final KeyboardLayoutSet layoutSet,
-            final int elementId, final CharSequence label, final int iconId) {
-        final Keyboard keyboard = layoutSet.getKeyboard(elementId);
-        final Key enterKey = keyboard.getKey(Constants.CODE_ENTER);
-        assertNotNull(tag + " enter key on " + keyboard.mId, enterKey);
-        assertEquals(tag + " enter label " + enterKey, label, enterKey.getLabel());
-        assertEquals(tag + " enter icon " + enterKey, iconId, enterKey.getIconId());
+    protected void doTestActionKeyLabel(final String tag, final InputMethodSubtype subtype,
+            final int actionId, final int labelResId) {
+        final Locale labelLocale = subtype.getLocale().equals(SubtypeLocaleUtils.NO_LANGUAGE)
+                ? null : SubtypeLocaleUtils.getSubtypeLocale(subtype);
+        doTestActionKeyLabel(tag, subtype, actionId, labelLocale, labelResId);
     }
 
-    protected void doTestActionLabel(final String tag, final InputMethodSubtype subtype,
-            final int actionId, final int labelResId) {
+    protected void doTestActionKeyLabel(final String tag, final InputMethodSubtype subtype,
+            final int actionId, final Locale labelLocale, final int labelResId) {
         final EditorInfo editorInfo = new EditorInfo();
         editorInfo.imeOptions = actionId;
         final RunInLocale<String> job = new RunInLocale<String>() {
@@ -54,138 +53,127 @@
                 return res.getString(labelResId);
             }
         };
-        final Resources res = getContext().getResources();
-        final String label;
-        if (subtype.getLocale().equals(SubtypeLocaleUtils.NO_LANGUAGE)) {
-            // Using system locale.
-            label = res.getString(labelResId);
-        } else {
-            label = job.runInLocale(res, SubtypeLocaleUtils.getSubtypeLocale(subtype));
-        }
-        doTestActionLabel(tag, subtype, editorInfo, label);
+        final String label = job.runInLocale(getContext().getResources(), labelLocale);
+        doTestActionKeyLabel(tag, subtype, editorInfo, label);
     }
 
-    protected void doTestActionLabel(final String tag, final InputMethodSubtype subtype,
-            final EditorInfo editorInfo, final CharSequence label) {
-        // Test text layouts.
-        editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
-        final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo);
-        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_ALPHABET,
-                label, KeyboardIconsSet.ICON_UNDEFINED);
-        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS,
-                label, KeyboardIconsSet.ICON_UNDEFINED);
-        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS_SHIFTED,
-                label, KeyboardIconsSet.ICON_UNDEFINED);
-        // Test phone number layouts.
-        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE,
-                label, KeyboardIconsSet.ICON_UNDEFINED);
-        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE_SYMBOLS,
-                label, KeyboardIconsSet.ICON_UNDEFINED);
-        // Test normal number layout.
-        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_NUMBER,
-                label, KeyboardIconsSet.ICON_UNDEFINED);
-        // Test number password layouts.
-        editorInfo.inputType =
-                InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD;
-        final KeyboardLayoutSet passwordSet = createKeyboardLayoutSet(subtype, editorInfo);
-        doTestActionKey(tag, passwordSet, KeyboardId.ELEMENT_NUMBER,
-                label, KeyboardIconsSet.ICON_UNDEFINED);
-    }
-
-    protected void doTestActionKeyIcon(final String tag, final InputMethodSubtype subtype,
-            final int actionId, final String iconName) {
-        final int iconId = KeyboardIconsSet.getIconId(iconName);
-        final EditorInfo editorInfo = new EditorInfo();
-        editorInfo.imeOptions = actionId;
-        // Test text layouts.
-        editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
-        final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo);
-        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_ALPHABET, null /* label */, iconId);
-        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS, null /* label */, iconId);
-        doTestActionKey(
-                tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS_SHIFTED, null /* label */, iconId);
-        // Test phone number layouts.
-        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE, null /* label */, iconId);
-        doTestActionKey(
-                tag, layoutSet, KeyboardId.ELEMENT_PHONE_SYMBOLS, null /* label */, iconId);
-        // Test normal number layout.
-        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_NUMBER, null /* label */, iconId);
-        // Test number password layout.
-        editorInfo.inputType =
-                InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD;
-        final KeyboardLayoutSet passwordSet = createKeyboardLayoutSet(subtype, editorInfo);
-        doTestActionKey(tag, passwordSet, KeyboardId.ELEMENT_NUMBER, null /* label */, iconId);
-    }
-
+    @Override
     public void testActionUnspecified() {
-        for (final InputMethodSubtype subtype : getAllSubtypesList()) {
-            final String tag = "unspecifiled "
-                    + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
-            doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_UNSPECIFIED,
-                    KeyboardIconsSet.NAME_ENTER_KEY);
-        }
+        super.testActionUnspecified();
     }
 
+    @Override
     public void testActionNone() {
-        for (final InputMethodSubtype subtype : getAllSubtypesList()) {
-            final String tag = "none " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
-            doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_NONE,
-                    KeyboardIconsSet.NAME_ENTER_KEY);
-        }
+        super.testActionNone();
     }
 
+    @Override
     public void testActionGo() {
         for (final InputMethodSubtype subtype : getAllSubtypesList()) {
             final String tag = "go " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
-            doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_GO, R.string.label_go_key);
+            doTestActionKeyLabel(tag, subtype, EditorInfo.IME_ACTION_GO, R.string.label_go_key);
         }
     }
 
+    @Override
     public void testActionSearch() {
-        for (final InputMethodSubtype subtype : getAllSubtypesList()) {
-            final String tag = "search " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
-            doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_SEARCH,
-                    KeyboardIconsSet.NAME_SEARCH_KEY);
-        }
+        super.testActionSearch();
     }
 
+    @Override
     public void testActionSend() {
         for (final InputMethodSubtype subtype : getAllSubtypesList()) {
             final String tag = "send " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
-            doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_SEND, R.string.label_send_key);
+            doTestActionKeyLabel(tag, subtype, EditorInfo.IME_ACTION_SEND, R.string.label_send_key);
         }
     }
 
+    @Override
     public void testActionNext() {
         for (final InputMethodSubtype subtype : getAllSubtypesList()) {
             final String tag = "next " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
-            doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_NEXT, R.string.label_next_key);
+            doTestActionKeyLabel(tag, subtype, EditorInfo.IME_ACTION_NEXT, R.string.label_next_key);
         }
     }
 
+    @Override
     public void testActionDone() {
         for (final InputMethodSubtype subtype : getAllSubtypesList()) {
             final String tag = "done " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
-            doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_DONE, R.string.label_done_key);
+            doTestActionKeyLabel(tag, subtype, EditorInfo.IME_ACTION_DONE, R.string.label_done_key);
         }
     }
 
+    @Override
     public void testActionPrevious() {
         for (final InputMethodSubtype subtype : getAllSubtypesList()) {
             final String tag = "previous " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
-            doTestActionLabel(
+            doTestActionKeyLabel(
                     tag, subtype, EditorInfo.IME_ACTION_PREVIOUS, R.string.label_previous_key);
         }
     }
 
+    @Override
     public void testActionCustom() {
-        for (final InputMethodSubtype subtype : getAllSubtypesList()) {
-            final String tag = "custom " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
-            final CharSequence customLabel = "customLabel";
-            final EditorInfo editorInfo = new EditorInfo();
-            editorInfo.imeOptions = EditorInfo.IME_ACTION_UNSPECIFIED;
-            editorInfo.actionLabel = customLabel;
-            doTestActionLabel(tag, subtype, editorInfo, customLabel);
+        super.testActionCustom();
+    }
+
+    // Working variable to simulate system locale changing.
+    private Locale mSystemLocale = Locale.getDefault();
+
+    private void doTestActionLabelInLocale(final InputMethodSubtype subtype,
+            final Locale labelLocale, final Locale systemLocale) {
+        // Simulate system locale changing, see {@link SystemBroadcastReceiver}.
+        if (!systemLocale.equals(mSystemLocale)) {
+            KeyboardLayoutSet.onSystemLocaleChanged();
+            mSystemLocale = systemLocale;
         }
+        final String tag = "label=" + labelLocale + " system=" + systemLocale
+                + " " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+        final RunInLocale<Void> job = new RunInLocale<Void>() {
+            @Override
+            public Void job(final Resources res) {
+                doTestActionKeyIcon(tag + " unspecified", subtype,
+                        EditorInfo.IME_ACTION_UNSPECIFIED, KeyboardIconsSet.NAME_ENTER_KEY);
+                doTestActionKeyIcon(tag + " none", subtype,
+                        EditorInfo.IME_ACTION_NONE, KeyboardIconsSet.NAME_ENTER_KEY);
+                doTestActionKeyLabel(tag + " go", subtype,
+                        EditorInfo.IME_ACTION_GO, labelLocale, R.string.label_go_key);
+                doTestActionKeyIcon(tag + " search", subtype,
+                        EditorInfo.IME_ACTION_SEARCH, KeyboardIconsSet.NAME_SEARCH_KEY);
+                doTestActionKeyLabel(tag + " send", subtype,
+                        EditorInfo.IME_ACTION_SEND, labelLocale, R.string.label_send_key);
+                doTestActionKeyLabel(tag + " next", subtype,
+                        EditorInfo.IME_ACTION_NEXT, labelLocale, R.string.label_next_key);
+                doTestActionKeyLabel(tag + " done", subtype,
+                        EditorInfo.IME_ACTION_DONE, labelLocale, R.string.label_done_key);
+                doTestActionKeyLabel(tag + " previous", subtype,
+                        EditorInfo.IME_ACTION_PREVIOUS, labelLocale, R.string.label_previous_key);
+                return null;
+            }
+        };
+        job.runInLocale(getContext().getResources(), systemLocale);
+    }
+
+    public void testActionLabelInOtherLocale() {
+        final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
+        final InputMethodSubtype italian = richImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.ITALIAN.toString(), SubtypeLocaleUtils.QWERTY);
+        // An action label should be displayed in subtype's locale regardless of the system locale.
+        doTestActionLabelInLocale(italian, Locale.ITALIAN, Locale.US);
+        doTestActionLabelInLocale(italian, Locale.ITALIAN, Locale.FRENCH);
+        doTestActionLabelInLocale(italian, Locale.ITALIAN, Locale.ITALIAN);
+        doTestActionLabelInLocale(italian, Locale.ITALIAN, Locale.JAPANESE);
+    }
+
+    public void testNoLanguageSubtypeActionLabel() {
+        final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
+        final InputMethodSubtype noLanguage = richImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.QWERTY);
+        // An action label of no language keyboard should be displayed in the system locale.
+        doTestActionLabelInLocale(noLanguage, Locale.US, Locale.US);
+        doTestActionLabelInLocale(noLanguage, Locale.FRENCH, Locale.FRENCH);
+        doTestActionLabelInLocale(noLanguage, Locale.ITALIAN, Locale.ITALIAN);
+        doTestActionLabelInLocale(noLanguage, Locale.JAPANESE, Locale.JAPANESE);
     }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java
index 7747ac5..25da509 100644
--- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java
@@ -17,30 +17,99 @@
 package com.android.inputmethod.keyboard;
 
 import android.test.suitebuilder.annotation.MediumTest;
+import android.text.InputType;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
 @MediumTest
-public class KeyboardLayoutSetActionLabelLxxTests extends KeyboardLayoutSetActionLabelKlpTests {
+public class KeyboardLayoutSetActionLabelLxxTests extends KeyboardLayoutSetTestsBase {
     @Override
     protected int getKeyboardThemeForTests() {
         return KeyboardTheme.THEME_ID_LXX_DARK;
     }
 
-    @Override
+    private static void doTestActionKey(final String tag, final KeyboardLayoutSet layoutSet,
+            final int elementId, final CharSequence label, final int iconId) {
+        final Keyboard keyboard = layoutSet.getKeyboard(elementId);
+        final Key enterKey = keyboard.getKey(Constants.CODE_ENTER);
+        assertNotNull(tag + " enter key on " + keyboard.mId, enterKey);
+        assertEquals(tag + " enter label " + enterKey, label, enterKey.getLabel());
+        assertEquals(tag + " enter icon " + enterKey, iconId, enterKey.getIconId());
+    }
+
+    protected void doTestActionKeyLabel(final String tag, final InputMethodSubtype subtype,
+            final EditorInfo editorInfo, final CharSequence label) {
+        // Test text layouts.
+        editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+        final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo);
+        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_ALPHABET,
+                label, KeyboardIconsSet.ICON_UNDEFINED);
+        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS,
+                label, KeyboardIconsSet.ICON_UNDEFINED);
+        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS_SHIFTED,
+                label, KeyboardIconsSet.ICON_UNDEFINED);
+        // Test phone number layouts.
+        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE,
+                label, KeyboardIconsSet.ICON_UNDEFINED);
+        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE_SYMBOLS,
+                label, KeyboardIconsSet.ICON_UNDEFINED);
+        // Test normal number layout.
+        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_NUMBER,
+                label, KeyboardIconsSet.ICON_UNDEFINED);
+        // Test number password layouts.
+        editorInfo.inputType =
+                InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD;
+        final KeyboardLayoutSet passwordSet = createKeyboardLayoutSet(subtype, editorInfo);
+        doTestActionKey(tag, passwordSet, KeyboardId.ELEMENT_NUMBER,
+                label, KeyboardIconsSet.ICON_UNDEFINED);
+    }
+
+    protected void doTestActionKeyIcon(final String tag, final InputMethodSubtype subtype,
+            final int actionId, final String iconName) {
+        final int iconId = KeyboardIconsSet.getIconId(iconName);
+        final EditorInfo editorInfo = new EditorInfo();
+        editorInfo.imeOptions = actionId;
+        // Test text layouts.
+        editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+        final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo);
+        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_ALPHABET, null /* label */, iconId);
+        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS, null /* label */, iconId);
+        doTestActionKey(
+                tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS_SHIFTED, null /* label */, iconId);
+        // Test phone number layouts.
+        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE, null /* label */, iconId);
+        doTestActionKey(
+                tag, layoutSet, KeyboardId.ELEMENT_PHONE_SYMBOLS, null /* label */, iconId);
+        // Test normal number layout.
+        doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_NUMBER, null /* label */, iconId);
+        // Test number password layout.
+        editorInfo.inputType =
+                InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD;
+        final KeyboardLayoutSet passwordSet = createKeyboardLayoutSet(subtype, editorInfo);
+        doTestActionKey(tag, passwordSet, KeyboardId.ELEMENT_NUMBER, null /* label */, iconId);
+    }
+
     public void testActionUnspecified() {
-        super.testActionUnspecified();
+        for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+            final String tag = "unspecifiled "
+                    + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+            doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_UNSPECIFIED,
+                    KeyboardIconsSet.NAME_ENTER_KEY);
+        }
     }
 
-    @Override
     public void testActionNone() {
-        super.testActionNone();
+        for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+            final String tag = "none " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+            doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_NONE,
+                    KeyboardIconsSet.NAME_ENTER_KEY);
+        }
     }
 
-    @Override
     public void testActionGo() {
         for (final InputMethodSubtype subtype : getAllSubtypesList()) {
             final String tag = "go " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
@@ -49,12 +118,14 @@
         }
     }
 
-    @Override
     public void testActionSearch() {
-        super.testActionSearch();
+        for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+            final String tag = "search " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+            doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_SEARCH,
+                    KeyboardIconsSet.NAME_SEARCH_KEY);
+        }
     }
 
-    @Override
     public void testActionSend() {
         for (final InputMethodSubtype subtype : getAllSubtypesList()) {
             final String tag = "send " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
@@ -63,7 +134,6 @@
         }
     }
 
-    @Override
     public void testActionNext() {
         for (final InputMethodSubtype subtype : getAllSubtypesList()) {
             final String tag = "next " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
@@ -72,7 +142,6 @@
         }
     }
 
-    @Override
     public void testActionDone() {
         for (final InputMethodSubtype subtype : getAllSubtypesList()) {
             final String tag = "done " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
@@ -81,7 +150,6 @@
         }
     }
 
-    @Override
     public void testActionPrevious() {
         for (final InputMethodSubtype subtype : getAllSubtypesList()) {
             final String tag = "previous " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
@@ -90,8 +158,14 @@
         }
     }
 
-    @Override
     public void testActionCustom() {
-        super.testActionCustom();
+        for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+            final String tag = "custom " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+            final CharSequence customLabel = "customLabel";
+            final EditorInfo editorInfo = new EditorInfo();
+            editorInfo.imeOptions = EditorInfo.IME_ACTION_UNSPECIFIED;
+            editorInfo.actionLabel = customLabel;
+            doTestActionKeyLabel(tag, subtype, editorInfo, customLabel);
+        }
     }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
index 0a76a9d..29aa758 100644
--- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
@@ -43,7 +43,6 @@
     private final ArrayList<InputMethodSubtype> mAsciiCapableSubtypesList = new ArrayList<>();
     private final ArrayList<InputMethodSubtype> mAdditionalSubtypesList = new ArrayList<>();
 
-    private Context mThemeContext;
     private int mScreenMetrics;
 
     protected abstract int getKeyboardThemeForTests();
@@ -51,12 +50,13 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mScreenMetrics = mContext.getResources().getInteger(R.integer.config_screen_metrics);
-
         final KeyboardTheme keyboardTheme = KeyboardTheme.searchKeyboardThemeById(
                 getKeyboardThemeForTests());
-        mThemeContext = new ContextThemeWrapper(mContext, keyboardTheme.mStyleId);
-        RichInputMethodManager.init(mThemeContext);
+        setContext(new ContextThemeWrapper(getContext(), keyboardTheme.mStyleId));
+
+        final Context context = getContext();
+        mScreenMetrics = context.getResources().getInteger(R.integer.config_screen_metrics);
+        RichInputMethodManager.init(context);
         final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
 
         final InputMethodInfo imi = richImm.getInputMethodInfoOfThisIme();
@@ -122,7 +122,7 @@
     protected KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype,
             final EditorInfo editorInfo, final boolean voiceInputKeyEnabled,
             final boolean languageSwitchKeyEnabled) {
-        final Context context = mThemeContext;
+        final Context context = getContext();
         final Resources res = context.getResources();
         final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
         final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
diff --git a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderAutoOrderTests.java b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderAutoOrderTests.java
new file mode 100644
index 0000000..ebefe2d
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderAutoOrderTests.java
@@ -0,0 +1,2464 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.inputmethod.keyboard.MoreKeysKeyboard.MoreKeysKeyboardParams;
+
+@MediumTest
+public class MoreKeysKeyboardBuilderAutoOrderTests extends AndroidTestCase {
+    private static final int WIDTH = 10;
+    private static final int HEIGHT = 10;
+
+    private static final int KEYBOARD_WIDTH = WIDTH * 10;
+    private static final int XPOS_L0 = WIDTH * 0 + WIDTH / 2;
+    private static final int XPOS_L1 = WIDTH * 1 + WIDTH / 2;
+    private static final int XPOS_L2 = WIDTH * 2 + WIDTH / 2;
+    private static final int XPOS_L3 = WIDTH * 3 + WIDTH / 2;
+    private static final int XPOS_M0 = WIDTH * 4 + WIDTH / 2;
+    private static final int XPOS_M1 = WIDTH * 5 + WIDTH / 2;
+    private static final int XPOS_R3 = WIDTH * 6 + WIDTH / 2;
+    private static final int XPOS_R2 = WIDTH * 7 + WIDTH / 2;
+    private static final int XPOS_R1 = WIDTH * 8 + WIDTH / 2;
+    private static final int XPOS_R0 = WIDTH * 9 + WIDTH / 2;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    private static MoreKeysKeyboardParams createParams(final int numKeys, final int columnNum,
+            final int coordXInParent) {
+        final MoreKeysKeyboardParams params = new MoreKeysKeyboardParams();
+        params.setParameters(numKeys, columnNum, WIDTH, HEIGHT, coordXInParent, KEYBOARD_WIDTH,
+                true /* isMoreKeysFixedColumn */, false /* isMoreKeysFixedOrder */,
+                0 /* dividerWidth */);
+        return params;
+    }
+
+    public void testLayoutError() {
+        MoreKeysKeyboardParams params = null;
+        try {
+            final int maxColumns = KEYBOARD_WIDTH / WIDTH;
+            params = createParams(maxColumns + 1, maxColumns + 1, HEIGHT);
+            fail("Should throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // Too small keyboard to hold more keys keyboard.
+        }
+        assertNull("Too small keyboard to hold more keys keyboard", params);
+    }
+
+    // More keys keyboard layout test.
+    // "[n]" represents n-th key position in more keys keyboard.
+    // "<1>" is the default key.
+
+    // <1>
+    public void testLayout1KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(1, 5, XPOS_M0);
+        assertEquals("1 key auto 5 M0 columns", 1, params.mNumColumns);
+        assertEquals("1 key auto 5 M0 rows", 1, params.mNumRows);
+        assertEquals("1 key auto 5 M0 left", 0, params.mLeftKeys);
+        assertEquals("1 key auto 5 M0 right", 1, params.mRightKeys);
+        assertEquals("1 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("1 key auto 5 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key auto 5 M0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |<1>
+    public void testLayout1KeyAuto5L0() {
+        MoreKeysKeyboardParams params = createParams(1, 5, XPOS_L0);
+        assertEquals("1 key auto 5 L0 columns", 1, params.mNumColumns);
+        assertEquals("1 key auto 5 L0 rows", 1, params.mNumRows);
+        assertEquals("1 key auto 5 L0 left", 0, params.mLeftKeys);
+        assertEquals("1 key auto 5 L0 right", 1, params.mRightKeys);
+        assertEquals("1 key auto 5 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("1 key auto 5 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ <1>
+    public void testLayout1KeyAuto5L1() {
+        MoreKeysKeyboardParams params = createParams(1, 5, XPOS_L1);
+        assertEquals("1 key auto 5 L1 columns", 1, params.mNumColumns);
+        assertEquals("1 key auto 5 L1 rows", 1, params.mNumRows);
+        assertEquals("1 key auto 5 L1 left", 0, params.mLeftKeys);
+        assertEquals("1 key auto 5 L1 right", 1, params.mRightKeys);
+        assertEquals("1 key auto 5 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("1 key auto 5 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ <1>
+    public void testLayout1KeyAuto5L2() {
+        MoreKeysKeyboardParams params = createParams(1, 5, XPOS_L2);
+        assertEquals("1 key auto 5 L2 columns", 1, params.mNumColumns);
+        assertEquals("1 key auto 5 L2 rows", 1, params.mNumRows);
+        assertEquals("1 key auto 5 L2 left", 0, params.mLeftKeys);
+        assertEquals("1 key auto 5 L2 right", 1, params.mRightKeys);
+        assertEquals("1 key auto 5 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("1 key auto 5 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key auto 5 L2 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // <1>|
+    public void testLayout1KeyAuto5R0() {
+        MoreKeysKeyboardParams params = createParams(1, 5, XPOS_R0);
+        assertEquals("1 key auto 5 R0 columns", 1, params.mNumColumns);
+        assertEquals("1 key auto 5 R0 rows", 1, params.mNumRows);
+        assertEquals("1 key auto 5 R0 left", 0, params.mLeftKeys);
+        assertEquals("1 key auto 5 R0 right", 1, params.mRightKeys);
+        assertEquals("1 key auto 5 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("1 key auto 5 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key auto 5 R0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // <1> ___|
+    public void testLayout1KeyAuto5R1() {
+        MoreKeysKeyboardParams params = createParams(1, 5, XPOS_R1);
+        assertEquals("1 key auto 5 R1 columns", 1, params.mNumColumns);
+        assertEquals("1 key auto 5 R1 rows", 1, params.mNumRows);
+        assertEquals("1 key auto 5 R1 left", 0, params.mLeftKeys);
+        assertEquals("1 key auto 5 R1 right", 1, params.mRightKeys);
+        assertEquals("1 key auto 5 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("1 key auto 5 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key auto 5 R1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // <1> ___ ___|
+    public void testLayout1KeyAuto5R2() {
+        MoreKeysKeyboardParams params = createParams(1, 5, XPOS_R2);
+        assertEquals("1 key auto 5 R2 columns", 1, params.mNumColumns);
+        assertEquals("1 key auto 5 R2 rows", 1, params.mNumRows);
+        assertEquals("1 key auto 5 R2 left", 0, params.mLeftKeys);
+        assertEquals("1 key auto 5 R2 right", 1, params.mRightKeys);
+        assertEquals("1 key auto 5 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("1 key auto 5 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key auto 5 R2 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // <1> [2]
+    public void testLayout2KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(2, 5, XPOS_M0);
+        assertEquals("2 key auto 5 M0 columns", 2, params.mNumColumns);
+        assertEquals("2 key auto 5 M0 rows", 1, params.mNumRows);
+        assertEquals("2 key auto 5 M0 left", 0, params.mLeftKeys);
+        assertEquals("2 key auto 5 M0 right", 2, params.mRightKeys);
+        assertEquals("2 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("2 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("2 key auto 5 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key auto 5 M0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |<1> [2]
+    public void testLayout2KeyAuto5L0() {
+        MoreKeysKeyboardParams params = createParams(2, 5, XPOS_L0);
+        assertEquals("2 key auto 5 L0 columns", 2, params.mNumColumns);
+        assertEquals("2 key auto 5 L0 rows", 1, params.mNumRows);
+        assertEquals("2 key auto 5 L0 left", 0, params.mLeftKeys);
+        assertEquals("2 key auto 5 L0 right", 2, params.mRightKeys);
+        assertEquals("2 key auto 5 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("2 key auto 5 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("2 key auto 5 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ <1> [2]
+    public void testLayout2KeyAuto5L1() {
+        MoreKeysKeyboardParams params = createParams(2, 5, XPOS_L1);
+        assertEquals("2 key auto 5 L1 columns", 2, params.mNumColumns);
+        assertEquals("2 key auto 5 L1 rows", 1, params.mNumRows);
+        assertEquals("2 key auto 5 L1 left", 0, params.mLeftKeys);
+        assertEquals("2 key auto 5 L1 right", 2, params.mRightKeys);
+        assertEquals("2 key auto 5 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("2 key auto 5 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("2 key auto 5 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ <1> [2]
+    public void testLayout2KeyAuto5L2() {
+        MoreKeysKeyboardParams params = createParams(2, 5, XPOS_L2);
+        assertEquals("2 key auto 5 L2 columns", 2, params.mNumColumns);
+        assertEquals("2 key auto 5 L2 rows", 1, params.mNumRows);
+        assertEquals("2 key auto 5 L2 left", 0, params.mLeftKeys);
+        assertEquals("2 key auto 5 L2 right", 2, params.mRightKeys);
+        assertEquals("2 key auto 5 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("2 key auto 5 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("2 key auto 5 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key auto 5 L2 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // [2] <1>|
+    public void testLayout2KeyAuto5R0() {
+        MoreKeysKeyboardParams params = createParams(2, 5, XPOS_R0);
+        assertEquals("2 key auto 5 R0 columns", 2, params.mNumColumns);
+        assertEquals("2 key auto 5 R0 rows", 1, params.mNumRows);
+        assertEquals("2 key auto 5 R0 left", 1, params.mLeftKeys);
+        assertEquals("2 key auto 5 R0 right", 1, params.mRightKeys);
+        assertEquals("2 key auto 5 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("2 key auto 5 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("2 key auto 5 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key auto 5 R0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [2] <1> ___|
+    public void testLayout2KeyAuto5R1() {
+        MoreKeysKeyboardParams params = createParams(2, 5, XPOS_R1);
+        assertEquals("2 key auto 5 R1 columns", 2, params.mNumColumns);
+        assertEquals("2 key auto 5 R1 rows", 1, params.mNumRows);
+        assertEquals("2 key auto 5 R1 left", 1, params.mLeftKeys);
+        assertEquals("2 key auto 5 R1 right", 1, params.mRightKeys);
+        assertEquals("2 key auto 5 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("2 key auto 5 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("2 key auto 5 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key auto 5 R1 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // <1> [2] ___|
+    public void testLayout2KeyAuto5R2() {
+        MoreKeysKeyboardParams params = createParams(2, 5, XPOS_R2);
+        assertEquals("2 key auto 5 R2 columns", 2, params.mNumColumns);
+        assertEquals("2 key auto 5 R2 rows", 1, params.mNumRows);
+        assertEquals("2 key auto 5 R2 left", 0, params.mLeftKeys);
+        assertEquals("2 key auto 5 R2 right", 2, params.mRightKeys);
+        assertEquals("2 key auto 5 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("2 key auto 5 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("2 key auto 5 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key auto 5 R2 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // [3] <1> [2]
+    public void testLayout3KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(3, 5, XPOS_M0);
+        assertEquals("3 key auto 5 M0 columns", 3, params.mNumColumns);
+        assertEquals("3 key auto 5 M0 rows", 1, params.mNumRows);
+        assertEquals("3 key auto 5 M0 left", 1, params.mLeftKeys);
+        assertEquals("3 key auto 5 M0 right", 2, params.mRightKeys);
+        assertEquals("3 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key auto 5 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("3 key auto 5 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 5 M0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // |<1> [2] [3]
+    public void testLayout3KeyAuto5L0() {
+        MoreKeysKeyboardParams params = createParams(3, 5, XPOS_L0);
+        assertEquals("3 key auto 5 L0 columns", 3, params.mNumColumns);
+        assertEquals("3 key auto 5 L0 rows", 1, params.mNumRows);
+        assertEquals("3 key auto 5 L0 left", 0, params.mLeftKeys);
+        assertEquals("3 key auto 5 L0 right", 3, params.mRightKeys);
+        assertEquals("3 key auto 5 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 5 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key auto 5 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("3 key auto 5 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ <1> [2] [3]
+    public void testLayout3KeyAuto5L1() {
+        MoreKeysKeyboardParams params = createParams(3, 5, XPOS_L1);
+        assertEquals("3 key auto 5 L1 columns", 3, params.mNumColumns);
+        assertEquals("3 key auto 5 L1 rows", 1, params.mNumRows);
+        assertEquals("3 key auto 5 L1 left", 0, params.mLeftKeys);
+        assertEquals("3 key auto 5 L1 right", 3, params.mRightKeys);
+        assertEquals("3 key auto 5 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 5 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key auto 5 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("3 key auto 5 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [3] <1> [2]
+    public void testLayout3KeyAuto5L2() {
+        MoreKeysKeyboardParams params = createParams(3, 5, XPOS_L2);
+        assertEquals("3 key auto 5 L2 columns", 3, params.mNumColumns);
+        assertEquals("3 key auto 5 L2 rows", 1, params.mNumRows);
+        assertEquals("3 key auto 5 L2 left", 1, params.mLeftKeys);
+        assertEquals("3 key auto 5 L2 right", 2, params.mRightKeys);
+        assertEquals("3 key auto 5 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 5 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key auto 5 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("3 key auto 5 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [3] [2] <1>|
+    public void testLayout3KeyAuto5R0() {
+        MoreKeysKeyboardParams params = createParams(3, 5, XPOS_R0);
+        assertEquals("3 key auto 5 R0 columns", 3, params.mNumColumns);
+        assertEquals("3 key auto 5 R0 rows", 1, params.mNumRows);
+        assertEquals("3 key auto 5 R0 left", 2, params.mLeftKeys);
+        assertEquals("3 key auto 5 R0 right", 1, params.mRightKeys);
+        assertEquals("3 key auto 5 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 5 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("3 key auto 5 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("3 key auto 5 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 5 R0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // [3] [2] <1> ___|
+    public void testLayout3KeyAuto5R1() {
+        MoreKeysKeyboardParams params = createParams(3, 5, XPOS_R1);
+        assertEquals("3 key auto 5 R1 columns", 3, params.mNumColumns);
+        assertEquals("3 key auto 5 R1 rows", 1, params.mNumRows);
+        assertEquals("3 key auto 5 R1 left", 2, params.mLeftKeys);
+        assertEquals("3 key auto 5 R1 right", 1, params.mRightKeys);
+        assertEquals("3 key auto 5 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 5 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("3 key auto 5 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("3 key auto 5 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 5 R1 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // [3] <1> [2] ___|
+    public void testLayout3KeyAuto5R2() {
+        MoreKeysKeyboardParams params = createParams(3, 5, XPOS_R2);
+        assertEquals("3 key auto 5 R2 columns", 3, params.mNumColumns);
+        assertEquals("3 key auto 5 R2 rows", 1, params.mNumRows);
+        assertEquals("3 key auto 5 R2 left", 1, params.mLeftKeys);
+        assertEquals("3 key auto 5 R2 right", 2, params.mRightKeys);
+        assertEquals("3 key auto 5 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 5 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key auto 5 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("3 key auto 5 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 5 R2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [3]
+    // <1> [2]
+    public void testLayout3KeyAuto2M0() {
+        MoreKeysKeyboardParams params = createParams(3, 2, XPOS_M0);
+        assertEquals("3 key auto 2 M0 columns", 2, params.mNumColumns);
+        assertEquals("3 key auto 2 M0 rows", 2, params.mNumRows);
+        assertEquals("3 key auto 2 M0 left", 0, params.mLeftKeys);
+        assertEquals("3 key auto 2 M0 right", 2, params.mRightKeys);
+        assertEquals("3 key auto 2 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 2 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key auto 2 M0 [3]", 0, params.getColumnPos(2));
+        assertEquals("3 key auto 2 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 2 M0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |[3]
+    // |<1> [2]
+    public void testLayout3KeyAuto2L0() {
+        MoreKeysKeyboardParams params = createParams(3, 2, XPOS_L0);
+        assertEquals("3 key auto 2 L0 columns", 2, params.mNumColumns);
+        assertEquals("3 key auto 2 L0 rows", 2, params.mNumRows);
+        assertEquals("3 key auto 2 L0 left", 0, params.mLeftKeys);
+        assertEquals("3 key auto 2 L0 right", 2, params.mRightKeys);
+        assertEquals("3 key auto 2 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 2 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key auto 2 L0 [3]", 0, params.getColumnPos(2));
+        assertEquals("3 key auto 2 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 2 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [3]
+    // |___ <1> [2]
+    public void testLayout3KeyAuto2L1() {
+        MoreKeysKeyboardParams params = createParams(3, 2, XPOS_L1);
+        assertEquals("3 key auto 2 L1 columns", 2, params.mNumColumns);
+        assertEquals("3 key auto 2 L1 rows", 2, params.mNumRows);
+        assertEquals("3 key auto 2 L1 left", 0, params.mLeftKeys);
+        assertEquals("3 key auto 2 L1 right", 2, params.mRightKeys);
+        assertEquals("3 key auto 2 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 2 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key auto 2 L1 [3]", 0, params.getColumnPos(2));
+        assertEquals("3 key auto 2 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 2 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |        [3]
+    // |___ ___ <1> [2]
+    public void testLayout3KeyAuto2L2() {
+        MoreKeysKeyboardParams params = createParams(3, 2, XPOS_L2);
+        assertEquals("3 key auto 2 L2 columns", 2, params.mNumColumns);
+        assertEquals("3 key auto 2 L2 rows", 2, params.mNumRows);
+        assertEquals("3 key auto 2 L2 left", 0, params.mLeftKeys);
+        assertEquals("3 key auto 2 L2 right", 2, params.mRightKeys);
+        assertEquals("3 key auto 2 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 2 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key auto 2 L2 [3]", 0, params.getColumnPos(2));
+        assertEquals("3 key auto 2 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 2 L2 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    //     [3]|
+    // [2] <1>|
+    public void testLayout3KeyAuto2R0() {
+        MoreKeysKeyboardParams params = createParams(3, 2, XPOS_R0);
+        assertEquals("3 key auto 2 R0 columns", 2, params.mNumColumns);
+        assertEquals("3 key auto 2 R0 rows", 2, params.mNumRows);
+        assertEquals("3 key auto 2 R0 left", 1, params.mLeftKeys);
+        assertEquals("3 key auto 2 R0 right", 1, params.mRightKeys);
+        assertEquals("3 key auto 2 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 2 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("3 key auto 2 R0 [3]", 0, params.getColumnPos(2));
+        assertEquals("3 key auto 2 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 2 R0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //     [3]    |
+    // [2] <1> ___|
+    public void testLayout3KeyAuto2R1() {
+        MoreKeysKeyboardParams params = createParams(3, 2, XPOS_R1);
+        assertEquals("3 key auto 2 R1 columns", 2, params.mNumColumns);
+        assertEquals("3 key auto 2 R1 rows", 2, params.mNumRows);
+        assertEquals("3 key auto 2 R1 left", 1, params.mLeftKeys);
+        assertEquals("3 key auto 2 R1 right", 1, params.mRightKeys);
+        assertEquals("3 key auto 2 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 2 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("3 key auto 2 R1 [3]", 0, params.getColumnPos(2));
+        assertEquals("3 key auto 2 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 2 R1 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [3]        |
+    // <1> [2] ___|
+    public void testLayout3KeyAuto2R2() {
+        MoreKeysKeyboardParams params = createParams(3, 2, XPOS_R2);
+        assertEquals("3 key auto 2 R2 columns", 2, params.mNumColumns);
+        assertEquals("3 key auto 2 R2 rows", 2, params.mNumRows);
+        assertEquals("3 key auto 2 R2 left", 0, params.mLeftKeys);
+        assertEquals("3 key auto 2 R2 right", 2, params.mRightKeys);
+        assertEquals("3 key auto 2 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("3 key auto 2 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key auto 2 R2 [3]", 0, params.getColumnPos(2));
+        assertEquals("3 key auto 2 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key auto 2 R2 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    //     [4]
+    // [3] <1> [2]
+    public void testLayout4KeyAuto3M0() {
+        MoreKeysKeyboardParams params = createParams(4, 3, XPOS_M0);
+        assertEquals("4 key auto 3 M0 columns", 3, params.mNumColumns);
+        assertEquals("4 key auto 3 M0 rows", 2, params.mNumRows);
+        assertEquals("4 key auto 3 M0 left", 1, params.mLeftKeys);
+        assertEquals("4 key auto 3 M0 right", 2, params.mRightKeys);
+        assertEquals("4 key auto 3 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 3 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 3 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("4 key auto 3 M0 [4]", 0, params.getColumnPos(3));
+        assertEquals("4 key auto 3 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 3 M0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // |[4]
+    // |<1> [2] [3]
+    public void testLayout4KeyAuto3L0() {
+        MoreKeysKeyboardParams params = createParams(4, 3, XPOS_L0);
+        assertEquals("4 key auto 3 L0 columns", 3, params.mNumColumns);
+        assertEquals("4 key auto 3 L0 rows", 2, params.mNumRows);
+        assertEquals("4 key auto 3 L0 left", 0, params.mLeftKeys);
+        assertEquals("4 key auto 3 L0 right", 3, params.mRightKeys);
+        assertEquals("4 key auto 3 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 3 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 3 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("4 key auto 3 L0 [4]", 0, params.getColumnPos(3));
+        assertEquals("4 key auto 3 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 3 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [4]
+    // |___ <1> [2] [3]
+    public void testLayout4KeyAuto3L1() {
+        MoreKeysKeyboardParams params = createParams(4, 3, XPOS_L1);
+        assertEquals("4 key auto 3 L1 columns", 3, params.mNumColumns);
+        assertEquals("4 key auto 3 L1 rows", 2, params.mNumRows);
+        assertEquals("4 key auto 3 L1 left", 0, params.mLeftKeys);
+        assertEquals("4 key auto 3 L1 right", 3, params.mRightKeys);
+        assertEquals("4 key auto 3 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 3 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 3 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("4 key auto 3 L1 [4]", 0, params.getColumnPos(3));
+        assertEquals("4 key auto 3 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 3 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [4]
+    // |___ [3] <1> [2]
+    public void testLayout4KeyAuto3L2() {
+        MoreKeysKeyboardParams params = createParams(4, 3, XPOS_L2);
+        assertEquals("4 key auto 3 L2 columns", 3, params.mNumColumns);
+        assertEquals("4 key auto 3 L2 rows", 2, params.mNumRows);
+        assertEquals("4 key auto 3 L2 left", 1, params.mLeftKeys);
+        assertEquals("4 key auto 3 L2 right", 2, params.mRightKeys);
+        assertEquals("4 key auto 3 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 3 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 3 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("4 key auto 3 L2 [4]", 0, params.getColumnPos(3));
+        assertEquals("4 key auto 3 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 3 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //         [4]|
+    // [3] [2] <1>|
+    public void testLayout4KeyAuto3R0() {
+        MoreKeysKeyboardParams params = createParams(4, 3, XPOS_R0);
+        assertEquals("4 key auto 3 R0 columns", 3, params.mNumColumns);
+        assertEquals("4 key auto 3 R0 rows", 2, params.mNumRows);
+        assertEquals("4 key auto 3 R0 left", 2, params.mLeftKeys);
+        assertEquals("4 key auto 3 R0 right", 1, params.mRightKeys);
+        assertEquals("4 key auto 3 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 3 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("4 key auto 3 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("4 key auto 3 R0 [4]", 0, params.getColumnPos(3));
+        assertEquals("4 key auto 3 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 3 R0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    //         [4] ___|
+    // [3] [2] <1> ___|
+    public void testLayout4KeyAuto3R1() {
+        MoreKeysKeyboardParams params = createParams(4, 3, XPOS_R1);
+        assertEquals("4 key auto 3 R1 columns", 3, params.mNumColumns);
+        assertEquals("4 key auto 3 R1 rows", 2, params.mNumRows);
+        assertEquals("4 key auto 3 R1 left", 2, params.mLeftKeys);
+        assertEquals("4 key auto 3 R1 right", 1, params.mRightKeys);
+        assertEquals("4 key auto 3 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 3 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("4 key auto 3 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("4 key auto 3 R1 [4]", 0, params.getColumnPos(3));
+        assertEquals("4 key auto 3 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 3 R1 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    //     [4]     ___|
+    // [3] <1> [2] ___|
+    public void testLayout4KeyAuto3R2() {
+        MoreKeysKeyboardParams params = createParams(4, 3, XPOS_R2);
+        assertEquals("4 key auto 3 R2 columns", 3, params.mNumColumns);
+        assertEquals("4 key auto 3 R2 rows", 2, params.mNumRows);
+        assertEquals("4 key auto 3 R2 left", 1, params.mLeftKeys);
+        assertEquals("4 key auto 3 R2 right", 2, params.mRightKeys);
+        assertEquals("4 key auto 3 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 3 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 3 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("4 key auto 3 R2 [4]", 0, params.getColumnPos(3));
+        assertEquals("4 key auto 3 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 3 R2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [3] <1> [2] [4]
+    public void testLayout4KeyAuto4M0() {
+        MoreKeysKeyboardParams params = createParams(4, 4, XPOS_M0);
+        assertEquals("4 key auto 4 M0 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 4 M0 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 4 M0 left", 1, params.mLeftKeys);
+        assertEquals("4 key auto 4 M0 right", 3, params.mRightKeys);
+        assertEquals("4 key auto 4 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 4 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 4 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("4 key auto 4 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("4 key auto 4 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 4 M0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // |<1> [2] [3] [4]
+    public void testLayout4KeyAuto4L0() {
+        MoreKeysKeyboardParams params = createParams(4, 4, XPOS_L0);
+        assertEquals("4 key auto 4 L0 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 4 L0 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 4 L0 left", 0, params.mLeftKeys);
+        assertEquals("4 key auto 4 L0 right", 4, params.mRightKeys);
+        assertEquals("4 key auto 4 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 4 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 4 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("4 key auto 4 L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("4 key auto 4 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 4 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ <1> [2] [3] [4]
+    public void testLayout4KeyAuto4L1() {
+        MoreKeysKeyboardParams params = createParams(4, 4, XPOS_L1);
+        assertEquals("4 key auto 4 L1 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 4 L1 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 4 L1 left", 0, params.mLeftKeys);
+        assertEquals("4 key auto 4 L1 right", 4, params.mRightKeys);
+        assertEquals("4 key auto 4 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 4 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 4 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("4 key auto 4 L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("4 key auto 4 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 4 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [3] <1> [2] [4]
+    public void testLayout4KeyAuto4L2() {
+        MoreKeysKeyboardParams params = createParams(4, 4, XPOS_L2);
+        assertEquals("4 key auto 4 L2 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 4 L2 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 4 L2 left", 1, params.mLeftKeys);
+        assertEquals("4 key auto 4 L2 right", 3, params.mRightKeys);
+        assertEquals("4 key auto 4 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 4 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 4 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("4 key auto 4 L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("4 key auto 4 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 4 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [4] [3] [2] <1>|
+    public void testLayout4KeyAuto4R0() {
+        MoreKeysKeyboardParams params = createParams(4, 4, XPOS_R0);
+        assertEquals("4 key auto 4 R0 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 4 R0 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 4 R0 left", 3, params.mLeftKeys);
+        assertEquals("4 key auto 4 R0 right", 1, params.mRightKeys);
+        assertEquals("4 key auto 4 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 4 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("4 key auto 4 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("4 key auto 4 R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("4 key auto 4 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 4 R0 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // [4] [3] [2] <1> ___|
+    public void testLayout4KeyAuto4R1() {
+        MoreKeysKeyboardParams params = createParams(4, 4, XPOS_R1);
+        assertEquals("4 key auto 4 R1 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 4 R1 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 4 R1 left", 3, params.mLeftKeys);
+        assertEquals("4 key auto 4 R1 right", 1, params.mRightKeys);
+        assertEquals("4 key auto 4 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 4 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("4 key auto 4 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("4 key auto 4 R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("4 key auto 4 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 4 R1 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // [4] [3] <1> [2] ___|
+    public void testLayout4KeyAuto4R2() {
+        MoreKeysKeyboardParams params = createParams(4, 4, XPOS_R2);
+        assertEquals("4 key auto 4 R2 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 4 R2 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 4 R2 left", 2, params.mLeftKeys);
+        assertEquals("4 key auto 4 R2 right", 2, params.mRightKeys);
+        assertEquals("4 key auto 4 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 4 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 4 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("4 key auto 4 R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("4 key auto 4 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 4 R2 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // [3] <1> [2] [4]
+    public void testLayout4KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(4, 5, XPOS_M0);
+        assertEquals("4 key auto 5 M0 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 5 M0 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 5 M0 left", 1, params.mLeftKeys);
+        assertEquals("4 key auto 5 M0 right", 3, params.mRightKeys);
+        assertEquals("4 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 5 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("4 key auto 5 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("4 key auto 5 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 5 M0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // |<1> [2] [3] [4]
+    public void testLayout4KeyAuto5L0() {
+        MoreKeysKeyboardParams params = createParams(4, 5, XPOS_L0);
+        assertEquals("4 key auto 5 L0 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 5 L0 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 5 L0 left", 0, params.mLeftKeys);
+        assertEquals("4 key auto 5 L0 right", 4, params.mRightKeys);
+        assertEquals("4 key auto 5 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 5 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 5 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("4 key auto 5 L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("4 key auto 5 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ <1> [2] [3] [4]
+    public void testLayout4KeyAuto5L1() {
+        MoreKeysKeyboardParams params = createParams(4, 5, XPOS_L1);
+        assertEquals("4 key auto 5 L1 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 5 L1 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 5 L1 left", 0, params.mLeftKeys);
+        assertEquals("4 key auto 5 L1 right", 4, params.mRightKeys);
+        assertEquals("4 key auto 5 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 5 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 5 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("4 key auto 5 L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("4 key auto 5 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [3] <1> [2] [4]
+    public void testLayout4KeyAuto5L2() {
+        MoreKeysKeyboardParams params = createParams(4, 5, XPOS_L2);
+        assertEquals("4 key auto 5 L2 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 5 L2 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 5 L2 left", 1, params.mLeftKeys);
+        assertEquals("4 key auto 5 L2 right", 3, params.mRightKeys);
+        assertEquals("4 key auto 5 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 5 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 5 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("4 key auto 5 L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("4 key auto 5 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [4] [3] [2] <1>|
+    public void testLayout4KeyAuto5R0() {
+        MoreKeysKeyboardParams params = createParams(4, 5, XPOS_R0);
+        assertEquals("4 key auto 5 R0 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 5 R0 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 5 R0 left", 3, params.mLeftKeys);
+        assertEquals("4 key auto 5 R0 right", 1, params.mRightKeys);
+        assertEquals("4 key auto 5 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 5 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("4 key auto 5 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("4 key auto 5 R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("4 key auto 5 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 5 R0 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // [4] [3] [2] <1> ___|
+    public void testLayout4KeyAuto5R1() {
+        MoreKeysKeyboardParams params = createParams(4, 5, XPOS_R1);
+        assertEquals("4 key auto 5 R1 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 5 R1 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 5 R1 left", 3, params.mLeftKeys);
+        assertEquals("4 key auto 5 R1 right", 1, params.mRightKeys);
+        assertEquals("4 key auto 5 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 5 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("4 key auto 5 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("4 key auto 5 R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("4 key auto 5 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 5 R1 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // [4] [3] <1> [2] ___|
+    public void testLayout4KeyAuto5R2() {
+        MoreKeysKeyboardParams params = createParams(4, 5, XPOS_R2);
+        assertEquals("4 key auto 5 R2 columns", 4, params.mNumColumns);
+        assertEquals("4 key auto 5 R2 rows", 1, params.mNumRows);
+        assertEquals("4 key auto 5 R2 left", 2, params.mLeftKeys);
+        assertEquals("4 key auto 5 R2 right", 2, params.mRightKeys);
+        assertEquals("4 key auto 5 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("4 key auto 5 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key auto 5 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("4 key auto 5 R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("4 key auto 5 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key auto 5 R2 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    //   [4] [5]
+    // [3] <1> [2]
+    public void testLayout5KeyAuto3M0() {
+        MoreKeysKeyboardParams params = createParams(5, 3, XPOS_M0);
+        assertEquals("5 key auto 3 M0 columns", 3, params.mNumColumns);
+        assertEquals("5 key auto 3 M0 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 3 M0 left", 1, params.mLeftKeys);
+        assertEquals("5 key auto 3 M0 right", 2, params.mRightKeys);
+        assertEquals("5 key auto 3 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 3 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 3 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("5 key auto 3 M0 [4]", 0, params.getColumnPos(3));
+        assertEquals("5 key auto 3 M0 [5]", 1, params.getColumnPos(4));
+        assertEquals("5 key auto 3 M0 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("5 key auto 3 M0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // |[4] [5]
+    // |<1> [2] [3]
+    public void testLayout5KeyAuto3L0() {
+        MoreKeysKeyboardParams params = createParams(5, 3, XPOS_L0);
+        assertEquals("5 key auto 3 L0 columns", 3, params.mNumColumns);
+        assertEquals("5 key auto 3 L0 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 3 L0 left", 0, params.mLeftKeys);
+        assertEquals("5 key auto 3 L0 right", 3, params.mRightKeys);
+        assertEquals("5 key auto 3 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 3 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 3 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("5 key auto 3 L0 [4]", 0, params.getColumnPos(3));
+        assertEquals("5 key auto 3 L0 [5]", 1, params.getColumnPos(4));
+        assertEquals("5 key auto 3 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 3 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [4] [5]
+    // |___ <1> [2] [3]
+    public void testLayout5KeyAuto3L1() {
+        MoreKeysKeyboardParams params = createParams(5, 3, XPOS_L1);
+        assertEquals("5 key auto 3 L1 columns", 3, params.mNumColumns);
+        assertEquals("5 key auto 3 L1 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 3 L1 left", 0, params.mLeftKeys);
+        assertEquals("5 key auto 3 L1 right", 3, params.mRightKeys);
+        assertEquals("5 key auto 3 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 3 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 3 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("5 key auto 3 L1 [4]", 0, params.getColumnPos(3));
+        assertEquals("5 key auto 3 L1 [5]", 1, params.getColumnPos(4));
+        assertEquals("5 key auto 3 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 3 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___   [4] [5]
+    // |___ [3] <1> [2]
+    public void testLayout5KeyAuto3L2() {
+        MoreKeysKeyboardParams params = createParams(5, 3, XPOS_L2);
+        assertEquals("5 key auto 3 L2 columns", 3, params.mNumColumns);
+        assertEquals("5 key auto 3 L2 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 3 L2 left", 1, params.mLeftKeys);
+        assertEquals("5 key auto 3 L2 right", 2, params.mRightKeys);
+        assertEquals("5 key auto 3 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 3 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 3 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("5 key auto 3 L2 [4]", 0, params.getColumnPos(3));
+        assertEquals("5 key auto 3 L2 [5]", 1, params.getColumnPos(4));
+        assertEquals("5 key auto 3 L2 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("5 key auto 3 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //     [5] [4]|
+    // [3] [2] <1>|
+    public void testLayout5KeyAuto3R0() {
+        MoreKeysKeyboardParams params = createParams(5, 3, XPOS_R0);
+        assertEquals("5 key auto 3 R0 columns", 3, params.mNumColumns);
+        assertEquals("5 key auto 3 R0 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 3 R0 left", 2, params.mLeftKeys);
+        assertEquals("5 key auto 3 R0 right", 1, params.mRightKeys);
+        assertEquals("5 key auto 3 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 3 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("5 key auto 3 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("5 key auto 3 R0 [4]", 0, params.getColumnPos(3));
+        assertEquals("5 key auto 3 R0 [5]", -1, params.getColumnPos(4));
+        assertEquals("5 key auto 3 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 3 R0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    //     [5] [4] ___|
+    // [3] [2] <1> ___|
+    public void testLayout5KeyAuto3R1() {
+        MoreKeysKeyboardParams params = createParams(5, 3, XPOS_R1);
+        assertEquals("5 key auto 3 R1 columns", 3, params.mNumColumns);
+        assertEquals("5 key auto 3 R1 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 3 R1 left", 2, params.mLeftKeys);
+        assertEquals("5 key auto 3 R1 right", 1, params.mRightKeys);
+        assertEquals("5 key auto 3 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 3 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("5 key auto 3 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("5 key auto 3 R1 [4]", 0, params.getColumnPos(3));
+        assertEquals("5 key auto 3 R1 [5]", -1, params.getColumnPos(4));
+        assertEquals("5 key auto 3 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 3 R1 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    //   [4] [5]   ___|
+    // [3] <1> [2] ___|
+    public void testLayout5KeyAuto3R2() {
+        MoreKeysKeyboardParams params = createParams(5, 3, XPOS_R2);
+        assertEquals("5 key auto 3 R2 columns", 3, params.mNumColumns);
+        assertEquals("5 key auto 3 R2 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 3 R2 left", 1, params.mLeftKeys);
+        assertEquals("5 key auto 3 R2 right", 2, params.mRightKeys);
+        assertEquals("5 key auto 3 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 3 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 3 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("5 key auto 3 R2 [4]", 0, params.getColumnPos(3));
+        assertEquals("5 key auto 3 R2 [5]", 1, params.getColumnPos(4));
+        assertEquals("5 key auto 3 R2 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("5 key auto 3 R2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //     [5]
+    // [3] <1> [2] [4]
+    public void testLayout5KeyAuto4M0() {
+        MoreKeysKeyboardParams params = createParams(5, 4, XPOS_M0);
+        assertEquals("5 key auto 4 M0 columns", 4, params.mNumColumns);
+        assertEquals("5 key auto 4 M0 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 4 M0 left", 1, params.mLeftKeys);
+        assertEquals("5 key auto 4 M0 right", 3, params.mRightKeys);
+        assertEquals("5 key auto 4 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 4 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 4 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("5 key auto 4 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("5 key auto 4 M0 [5]", 0, params.getColumnPos(4));
+        assertEquals("5 key auto 4 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 4 M0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // |[5]
+    // |<1> [2] [3] [4]
+    public void testLayout5KeyAuto4L0() {
+        MoreKeysKeyboardParams params = createParams(5, 4, XPOS_L0);
+        assertEquals("5 key auto 4 L0 columns", 4, params.mNumColumns);
+        assertEquals("5 key auto 4 L0 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 4 L0 left", 0, params.mLeftKeys);
+        assertEquals("5 key auto 4 L0 right", 4, params.mRightKeys);
+        assertEquals("5 key auto 4 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 4 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 4 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("5 key auto 4 L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("5 key auto 4 L0 [5]", 0, params.getColumnPos(4));
+        assertEquals("5 key auto 4 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 4 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [5]
+    // |___ <1> [2] [3] [4]
+    public void testLayout5KeyAuto4L1() {
+        MoreKeysKeyboardParams params = createParams(5, 4, XPOS_L1);
+        assertEquals("5 key auto 4 L1 columns", 4, params.mNumColumns);
+        assertEquals("5 key auto 4 L1 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 4 L1 left", 0, params.mLeftKeys);
+        assertEquals("5 key auto 4 L1 right", 4, params.mRightKeys);
+        assertEquals("5 key auto 4 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 4 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 4 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("5 key auto 4 L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("5 key auto 4 L1 [5]", 0, params.getColumnPos(4));
+        assertEquals("5 key auto 4 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 4 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___     [5]
+    // |___ [3] <1> [2] [4]
+    public void testLayout5KeyAuto4L2() {
+        MoreKeysKeyboardParams params = createParams(5, 4, XPOS_L2);
+        assertEquals("5 key auto 4 L2 columns", 4, params.mNumColumns);
+        assertEquals("5 key auto 4 L2 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 4 L2 left", 1, params.mLeftKeys);
+        assertEquals("5 key auto 4 L2 right", 3, params.mRightKeys);
+        assertEquals("5 key auto 4 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 4 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 4 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("5 key auto 4 L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("5 key auto 4 L2 [5]", 0, params.getColumnPos(4));
+        assertEquals("5 key auto 4 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 4 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //             [5]|
+    // [4] [3] [2] <1>|
+    public void testLayout5KeyAuto4R0() {
+        MoreKeysKeyboardParams params = createParams(5, 4, XPOS_R0);
+        assertEquals("5 key auto 4 R0 columns", 4, params.mNumColumns);
+        assertEquals("5 key auto 4 R0 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 4 R0 left", 3, params.mLeftKeys);
+        assertEquals("5 key auto 4 R0 right", 1, params.mRightKeys);
+        assertEquals("5 key auto 4 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 4 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("5 key auto 4 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("5 key auto 4 R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("5 key auto 4 R0 [5]", 0, params.getColumnPos(4));
+        assertEquals("5 key auto 4 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 4 R0 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    //             [5] ___|
+    // [4] [3] [2] <1> ___|
+    public void testLayout5KeyAuto4R1() {
+        MoreKeysKeyboardParams params = createParams(5, 4, XPOS_R1);
+        assertEquals("5 key auto 4 R1 columns", 4, params.mNumColumns);
+        assertEquals("5 key auto 4 R1 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 4 R1 left", 3, params.mLeftKeys);
+        assertEquals("5 key auto 4 R1 right", 1, params.mRightKeys);
+        assertEquals("5 key auto 4 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 4 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("5 key auto 4 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("5 key auto 4 R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("5 key auto 4 R1 [5]", 0, params.getColumnPos(4));
+        assertEquals("5 key auto 4 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 4 R1 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    //         [5]     ___|
+    // [4] [3] <1> [2] ___|
+    public void testLayout5KeyAuto4R2() {
+        MoreKeysKeyboardParams params = createParams(5, 4, XPOS_R2);
+        assertEquals("5 key auto 4 R2 columns", 4, params.mNumColumns);
+        assertEquals("5 key auto 4 R2 rows", 2, params.mNumRows);
+        assertEquals("5 key auto 4 R2 left", 2, params.mLeftKeys);
+        assertEquals("5 key auto 4 R2 right", 2, params.mRightKeys);
+        assertEquals("5 key auto 4 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 4 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 4 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("5 key auto 4 R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("5 key auto 4 R2 [5]", 0, params.getColumnPos(4));
+        assertEquals("5 key auto 4 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 4 R2 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // [5] [3] <1> [2] [4]
+    public void testLayout5KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(5, 5, XPOS_M0);
+        assertEquals("5 key auto 5 M0 columns", 5, params.mNumColumns);
+        assertEquals("5 key auto 5 M0 rows", 1, params.mNumRows);
+        assertEquals("5 key auto 5 M0 left", 2, params.mLeftKeys);
+        assertEquals("5 key auto 5 M0 right", 3, params.mRightKeys);
+        assertEquals("5 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 5 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("5 key auto 5 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("5 key auto 5 M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("5 key auto 5 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // |<1> [2] [3] [4] [5]
+    public void testLayout5KeyAuto5L0() {
+        MoreKeysKeyboardParams params = createParams(5, 5, XPOS_L0);
+        assertEquals("5 key auto 5 L0 columns", 5, params.mNumColumns);
+        assertEquals("5 key auto 5 L0 rows", 1, params.mNumRows);
+        assertEquals("5 key auto 5 L0 left", 0, params.mLeftKeys);
+        assertEquals("5 key auto 5 L0 right", 5, params.mRightKeys);
+        assertEquals("5 key auto 5 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 5 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 5 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("5 key auto 5 L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("5 key auto 5 L0 [5]", 4, params.getColumnPos(4));
+        assertEquals("5 key auto 5 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ <1> [2] [3] [4] [5]
+    public void testLayout5KeyAuto5L1() {
+        MoreKeysKeyboardParams params = createParams(5, 5, XPOS_L1);
+        assertEquals("5 key auto 5 L1 columns", 5, params.mNumColumns);
+        assertEquals("5 key auto 5 L1 rows", 1, params.mNumRows);
+        assertEquals("5 key auto 5 L1 left", 0, params.mLeftKeys);
+        assertEquals("5 key auto 5 L1 right", 5, params.mRightKeys);
+        assertEquals("5 key auto 5 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 5 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 5 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("5 key auto 5 L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("5 key auto 5 L1 [5]", 4, params.getColumnPos(4));
+        assertEquals("5 key auto 5 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [3] <1> [2] [4] [5]
+    public void testLayout5KeyAuto5L2() {
+        MoreKeysKeyboardParams params = createParams(5, 5, XPOS_L2);
+        assertEquals("5 key auto 5 L2 columns", 5, params.mNumColumns);
+        assertEquals("5 key auto 5 L2 rows", 1, params.mNumRows);
+        assertEquals("5 key auto 5 L2 left", 1, params.mLeftKeys);
+        assertEquals("5 key auto 5 L2 right", 4, params.mRightKeys);
+        assertEquals("5 key auto 5 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 5 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 5 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("5 key auto 5 L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("5 key auto 5 L2 [5]", 3, params.getColumnPos(4));
+        assertEquals("5 key auto 5 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [5] [4] [3] [2] <1>|
+    public void testLayout5KeyAuto5R0() {
+        MoreKeysKeyboardParams params = createParams(5, 5, XPOS_R0);
+        assertEquals("5 key auto 5 R0 columns", 5, params.mNumColumns);
+        assertEquals("5 key auto 5 R0 rows", 1, params.mNumRows);
+        assertEquals("5 key auto 5 R0 left", 4, params.mLeftKeys);
+        assertEquals("5 key auto 5 R0 right", 1, params.mRightKeys);
+        assertEquals("5 key auto 5 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 5 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("5 key auto 5 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("5 key auto 5 R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("5 key auto 5 R0 [5]", -4, params.getColumnPos(4));
+        assertEquals("5 key auto 5 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 5 R0 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    // [5] [4] [3] [2] <1> ___|
+    public void testLayout5KeyAuto5R1() {
+        MoreKeysKeyboardParams params = createParams(5, 5, XPOS_R1);
+        assertEquals("5 key auto 5 R1 columns", 5, params.mNumColumns);
+        assertEquals("5 key auto 5 R1 rows", 1, params.mNumRows);
+        assertEquals("5 key auto 5 R1 left", 4, params.mLeftKeys);
+        assertEquals("5 key auto 5 R1 right", 1, params.mRightKeys);
+        assertEquals("5 key auto 5 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 5 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("5 key auto 5 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("5 key auto 5 R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("5 key auto 5 R1 [5]", -4, params.getColumnPos(4));
+        assertEquals("5 key auto 5 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 5 R1 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    // [5] [4] [3] <1> [2] ___|
+    public void testLayout5KeyAuto5R2() {
+        MoreKeysKeyboardParams params = createParams(5, 5, XPOS_R2);
+        assertEquals("5 key auto 5 R2 columns", 5, params.mNumColumns);
+        assertEquals("5 key auto 5 R2 rows", 1, params.mNumRows);
+        assertEquals("5 key auto 5 R2 left", 3, params.mLeftKeys);
+        assertEquals("5 key auto 5 R2 right", 2, params.mRightKeys);
+        assertEquals("5 key auto 5 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("5 key auto 5 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key auto 5 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("5 key auto 5 R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("5 key auto 5 R2 [5]", -3, params.getColumnPos(4));
+        assertEquals("5 key auto 5 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key auto 5 R2 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    //     [5] [6]
+    // [3] <1> [2] [4]
+    public void testLayout6KeyAuto4M0() {
+        MoreKeysKeyboardParams params = createParams(6, 4, XPOS_M0);
+        assertEquals("6 key auto 4 M0 columns", 4, params.mNumColumns);
+        assertEquals("6 key auto 4 M0 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 4 M0 left", 1, params.mLeftKeys);
+        assertEquals("6 key auto 4 M0 right", 3, params.mRightKeys);
+        assertEquals("6 key auto 4 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 4 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key auto 4 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("6 key auto 4 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("6 key auto 4 M0 [5]", 0, params.getColumnPos(4));
+        assertEquals("6 key auto 4 M0 [6]", 1, params.getColumnPos(5));
+        assertEquals("6 key auto 4 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 4 M0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // |[5] [6]
+    // |<1> [2] [3] [4]
+    public void testLayout6KeyAuto4L0() {
+        MoreKeysKeyboardParams params = createParams(6, 4, XPOS_L0);
+        assertEquals("6 key auto 4 L0 columns", 4, params.mNumColumns);
+        assertEquals("6 key auto 4 L0 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 4 L0 left", 0, params.mLeftKeys);
+        assertEquals("6 key auto 4 L0 right", 4, params.mRightKeys);
+        assertEquals("6 key auto 4 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 4 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key auto 4 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("6 key auto 4 L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("6 key auto 4 L0 [5]", 0, params.getColumnPos(4));
+        assertEquals("6 key auto 4 L0 [6]", 1, params.getColumnPos(5));
+        assertEquals("6 key auto 4 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 4 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [5] [6]
+    // |___ <1> [2] [3] [4]
+    public void testLayout6KeyAuto4L1() {
+        MoreKeysKeyboardParams params = createParams(6, 4, XPOS_L1);
+        assertEquals("6 key auto 4 L1 columns", 4, params.mNumColumns);
+        assertEquals("6 key auto 4 L1 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 4 L1 left", 0, params.mLeftKeys);
+        assertEquals("6 key auto 4 L1 right", 4, params.mRightKeys);
+        assertEquals("6 key auto 4 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 4 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key auto 4 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("6 key auto 4 L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("6 key auto 4 L1 [5]", 0, params.getColumnPos(4));
+        assertEquals("6 key auto 4 L1 [6]", 1, params.getColumnPos(5));
+        assertEquals("6 key auto 4 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 4 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___     [5] [6]
+    // |___ [3] <1> [2] [4]
+    public void testLayout6KeyAuto4L2() {
+        MoreKeysKeyboardParams params = createParams(6, 4, XPOS_L2);
+        assertEquals("6 key auto 4 L2 columns", 4, params.mNumColumns);
+        assertEquals("6 key auto 4 L2 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 4 L2 left", 1, params.mLeftKeys);
+        assertEquals("6 key auto 4 L2 right", 3, params.mRightKeys);
+        assertEquals("6 key auto 4 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 4 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key auto 4 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("6 key auto 4 L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("6 key auto 4 L2 [5]", 0, params.getColumnPos(4));
+        assertEquals("6 key auto 4 L2 [6]", 1, params.getColumnPos(5));
+        assertEquals("6 key auto 4 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 4 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //         [6] [5]|
+    // [4] [3] [2] <1>|
+    public void testLayout6KeyAuto4R0() {
+        MoreKeysKeyboardParams params = createParams(6, 4, XPOS_R0);
+        assertEquals("6 key auto 4 R0 columns", 4, params.mNumColumns);
+        assertEquals("6 key auto 4 R0 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 4 R0 left", 3, params.mLeftKeys);
+        assertEquals("6 key auto 4 R0 right", 1, params.mRightKeys);
+        assertEquals("6 key auto 4 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 4 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("6 key auto 4 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("6 key auto 4 R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("6 key auto 4 R0 [5]", 0, params.getColumnPos(4));
+        assertEquals("6 key auto 4 R0 [6]", -1, params.getColumnPos(5));
+        assertEquals("6 key auto 4 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 4 R0 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    //         [6] [5] ___|
+    // [4] [3] [2] <1> ___|
+    public void testLayout6KeyAuto4R1() {
+        MoreKeysKeyboardParams params = createParams(6, 4, XPOS_R1);
+        assertEquals("6 key auto 4 R1 columns", 4, params.mNumColumns);
+        assertEquals("6 key auto 4 R1 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 4 R1 left", 3, params.mLeftKeys);
+        assertEquals("6 key auto 4 R1 right", 1, params.mRightKeys);
+        assertEquals("6 key auto 4 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 4 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("6 key auto 4 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("6 key auto 4 R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("6 key auto 4 R1 [5]", 0, params.getColumnPos(4));
+        assertEquals("6 key auto 4 R1 [6]", -1, params.getColumnPos(5));
+        assertEquals("6 key auto 4 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 4 R1 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    //         [5] [6] ___|
+    // [4] [3] <1> [2] ___|
+    public void testLayout6KeyAuto4R2() {
+        MoreKeysKeyboardParams params = createParams(6, 4, XPOS_R2);
+        assertEquals("6 key auto 4 R2 columns", 4, params.mNumColumns);
+        assertEquals("6 key auto 4 R2 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 4 R2 left", 2, params.mLeftKeys);
+        assertEquals("6 key auto 4 R2 right", 2, params.mRightKeys);
+        assertEquals("6 key auto 4 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 4 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key auto 4 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("6 key auto 4 R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("6 key auto 4 R2 [5]", 0, params.getColumnPos(4));
+        assertEquals("6 key auto 4 R2 [6]", 1, params.getColumnPos(5));
+        assertEquals("6 key auto 4 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 4 R2 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    //         [6]
+    // [5] [3] <1> [2] [4]
+    public void testLayout6KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(6, 5, XPOS_M0);
+        assertEquals("6 key auto 5 M0 columns", 5, params.mNumColumns);
+        assertEquals("6 key auto 5 M0 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 5 M0 left", 2, params.mLeftKeys);
+        assertEquals("6 key auto 5 M0 right", 3, params.mRightKeys);
+        assertEquals("6 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key auto 5 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("6 key auto 5 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("6 key auto 5 M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("6 key auto 5 M0 [6]", 0, params.getColumnPos(5));
+        assertEquals("6 key auto 5 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // |[6]
+    // |<1> [2] [3] [4] [5]
+    public void testLayout6KeyAuto5L0() {
+        MoreKeysKeyboardParams params = createParams(6, 5, XPOS_L0);
+        assertEquals("6 key auto 5 L0 columns", 5, params.mNumColumns);
+        assertEquals("6 key auto 5 L0 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 5 L0 left", 0, params.mLeftKeys);
+        assertEquals("6 key auto 5 L0 right", 5, params.mRightKeys);
+        assertEquals("6 key auto 5 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 5 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key auto 5 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("6 key auto 5 L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("6 key auto 5 L0 [5]", 4, params.getColumnPos(4));
+        assertEquals("6 key auto 5 L0 [6]", 0, params.getColumnPos(5));
+        assertEquals("6 key auto 5 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [6]
+    // |___ <1> [2] [3] [4] [5]
+    public void testLayout6KeyAuto5L1() {
+        MoreKeysKeyboardParams params = createParams(6, 5, XPOS_L1);
+        assertEquals("6 key auto 5 L1 columns", 5, params.mNumColumns);
+        assertEquals("6 key auto 5 L1 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 5 L1 left", 0, params.mLeftKeys);
+        assertEquals("6 key auto 5 L1 right", 5, params.mRightKeys);
+        assertEquals("6 key auto 5 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 5 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key auto 5 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("6 key auto 5 L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("6 key auto 5 L1 [5]", 4, params.getColumnPos(4));
+        assertEquals("6 key auto 5 L1 [6]", 0, params.getColumnPos(5));
+        assertEquals("6 key auto 5 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___     [6]
+    // |___ [3] <1> [2] [4] [5]
+    public void testLayout6KeyAuto5L2() {
+        MoreKeysKeyboardParams params = createParams(6, 5, XPOS_L2);
+        assertEquals("6 key auto 5 L2 columns", 5, params.mNumColumns);
+        assertEquals("6 key auto 5 L2 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 5 L2 left", 1, params.mLeftKeys);
+        assertEquals("6 key auto 5 L2 right", 4, params.mRightKeys);
+        assertEquals("6 key auto 5 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 5 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key auto 5 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("6 key auto 5 L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("6 key auto 5 L2 [5]", 3, params.getColumnPos(4));
+        assertEquals("6 key auto 5 L2 [6]", 0, params.getColumnPos(5));
+        assertEquals("6 key auto 5 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //                 [6]|
+    // [5] [4] [3] [2] <1>|
+    public void testLayout6KeyAuto5R0() {
+        MoreKeysKeyboardParams params = createParams(6, 5, XPOS_R0);
+        assertEquals("6 key auto 5 R0 columns", 5, params.mNumColumns);
+        assertEquals("6 key auto 5 R0 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 5 R0 left", 4, params.mLeftKeys);
+        assertEquals("6 key auto 5 R0 right", 1, params.mRightKeys);
+        assertEquals("6 key auto 5 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 5 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("6 key auto 5 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("6 key auto 5 R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("6 key auto 5 R0 [5]", -4, params.getColumnPos(4));
+        assertEquals("6 key auto 5 R0 [6]", 0, params.getColumnPos(5));
+        assertEquals("6 key auto 5 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 5 R0 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    //                 [6] ___|
+    // [5] [4] [3] [2] <1> ___|
+    public void testLayout6KeyAuto5R1() {
+        MoreKeysKeyboardParams params = createParams(6, 5, XPOS_R1);
+        assertEquals("6 key auto 5 R1 columns", 5, params.mNumColumns);
+        assertEquals("6 key auto 5 R1 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 5 R1 left", 4, params.mLeftKeys);
+        assertEquals("6 key auto 5 R1 right", 1, params.mRightKeys);
+        assertEquals("6 key auto 5 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 5 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("6 key auto 5 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("6 key auto 5 R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("6 key auto 5 R1 [5]", -4, params.getColumnPos(4));
+        assertEquals("6 key auto 5 R1 [6]", 0, params.getColumnPos(5));
+        assertEquals("6 key auto 5 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 5 R1 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    //             [6]     ___|
+    // [5] [4] [3] <1> [2] ___|
+    public void testLayout6KeyAuto5R2() {
+        MoreKeysKeyboardParams params = createParams(6, 5, XPOS_R2);
+        assertEquals("6 key auto 5 R2 columns", 5, params.mNumColumns);
+        assertEquals("6 key auto 5 R2 rows", 2, params.mNumRows);
+        assertEquals("6 key auto 5 R2 left", 3, params.mLeftKeys);
+        assertEquals("6 key auto 5 R2 right", 2, params.mRightKeys);
+        assertEquals("6 key auto 5 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("6 key auto 5 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key auto 5 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("6 key auto 5 R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("6 key auto 5 R2 [5]", -3, params.getColumnPos(4));
+        assertEquals("6 key auto 5 R2 [6]", 0, params.getColumnPos(5));
+        assertEquals("6 key auto 5 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key auto 5 R2 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // |<1> [2] [3] [4] [5] [6] [7] ___ ___ ___|
+    public void testLayout7KeyAuto7L0() {
+        MoreKeysKeyboardParams params = createParams(7, 7, XPOS_L0);
+        assertEquals("7 key auto 7 L0 columns", 7, params.mNumColumns);
+        assertEquals("7 key auto 7 L0 rows", 1, params.mNumRows);
+        assertEquals("7 key auto 7 L0 left", 0, params.mLeftKeys);
+        assertEquals("7 key auto 7 L0 right", 7, params.mRightKeys);
+        assertEquals("7 key auto 7 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 7 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 7 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("7 key auto 7 L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("7 key auto 7 L0 [5]", 4, params.getColumnPos(4));
+        assertEquals("7 key auto 7 L0 [6]", 5, params.getColumnPos(5));
+        assertEquals("7 key auto 7 L0 [7]", 6, params.getColumnPos(6));
+        assertEquals("7 key auto 7 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 7 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ <1> [2] [3] [4] [5] [6] [7] ___ ___|
+    public void testLayout7KeyAuto7L1() {
+        MoreKeysKeyboardParams params = createParams(7, 7, XPOS_L1);
+        assertEquals("7 key auto 7 L1 columns", 7, params.mNumColumns);
+        assertEquals("7 key auto 7 L1 rows", 1, params.mNumRows);
+        assertEquals("7 key auto 7 L1 left", 0, params.mLeftKeys);
+        assertEquals("7 key auto 7 L1 right", 7, params.mRightKeys);
+        assertEquals("7 key auto 7 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 7 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 7 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("7 key auto 7 L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("7 key auto 7 L1 [5]", 4, params.getColumnPos(4));
+        assertEquals("7 key auto 7 L1 [6]", 5, params.getColumnPos(5));
+        assertEquals("7 key auto 7 L1 [7]", 6, params.getColumnPos(6));
+        assertEquals("7 key auto 7 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 7 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [3] <1> [2] [4] [5] [6] [7] ___ ___|
+    public void testLayout7KeyAuto7L2() {
+        MoreKeysKeyboardParams params = createParams(7, 7, XPOS_L2);
+        assertEquals("7 key auto 7 L2 columns", 7, params.mNumColumns);
+        assertEquals("7 key auto 7 L2 rows", 1, params.mNumRows);
+        assertEquals("7 key auto 7 L2 left", 1, params.mLeftKeys);
+        assertEquals("7 key auto 7 L2 right", 6, params.mRightKeys);
+        assertEquals("7 key auto 7 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 7 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 7 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key auto 7 L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("7 key auto 7 L2 [5]", 3, params.getColumnPos(4));
+        assertEquals("7 key auto 7 L2 [6]", 4, params.getColumnPos(5));
+        assertEquals("7 key auto 7 L2 [7]", 5, params.getColumnPos(6));
+        assertEquals("7 key auto 7 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 7 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [5] [3] <1> [2] [4] [6] [7] ___ ___|
+    public void testLayout7KeyAuto7L3() {
+        MoreKeysKeyboardParams params = createParams(7, 7, XPOS_L3);
+        assertEquals("7 key auto 7 L3 columns", 7, params.mNumColumns);
+        assertEquals("7 key auto 7 L3 rows", 1, params.mNumRows);
+        assertEquals("7 key auto 7 L3 left", 2, params.mLeftKeys);
+        assertEquals("7 key auto 7 L3 right", 5, params.mRightKeys);
+        assertEquals("7 key auto 7 L3 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 7 L3 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 7 L3 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key auto 7 L3 [4]", 2, params.getColumnPos(3));
+        assertEquals("7 key auto 7 L3 [5]", -2, params.getColumnPos(4));
+        assertEquals("7 key auto 7 L3 [6]", 3, params.getColumnPos(5));
+        assertEquals("7 key auto 7 L3 [7]", 4, params.getColumnPos(6));
+        assertEquals("7 key auto 7 L3 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 7 L3 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [7] [5] [3] <1> [2] [4] [6] ___ ___|
+    public void testLayout7KeyAuto7M0() {
+        MoreKeysKeyboardParams params = createParams(7, 7, XPOS_M0);
+        assertEquals("7 key auto 7 M0 columns", 7, params.mNumColumns);
+        assertEquals("7 key auto 7 M0 rows", 1, params.mNumRows);
+        assertEquals("7 key auto 7 M0 left", 3, params.mLeftKeys);
+        assertEquals("7 key auto 7 M0 right", 4, params.mRightKeys);
+        assertEquals("7 key auto 7 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 7 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 7 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key auto 7 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("7 key auto 7 M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("7 key auto 7 M0 [6]", 3, params.getColumnPos(5));
+        assertEquals("7 key auto 7 M0 [7]", -3, params.getColumnPos(6));
+        assertEquals("7 key auto 7 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 7 M0 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [7] [5] [3] <1> [2] [4] [6] ___|
+    public void testLayout7KeyAuto7M1() {
+        MoreKeysKeyboardParams params = createParams(7, 7, XPOS_M1);
+        assertEquals("7 key auto 7 M1 columns", 7, params.mNumColumns);
+        assertEquals("7 key auto 7 M1 rows", 1, params.mNumRows);
+        assertEquals("7 key auto 7 M1 left", 3, params.mLeftKeys);
+        assertEquals("7 key auto 7 M1 right", 4, params.mRightKeys);
+        assertEquals("7 key auto 7 M1 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 7 M1 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 7 M1 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key auto 7 M1 [4]", 2, params.getColumnPos(3));
+        assertEquals("7 key auto 7 M1 [5]", -2, params.getColumnPos(4));
+        assertEquals("7 key auto 7 M1 [6]", 3, params.getColumnPos(5));
+        assertEquals("7 key auto 7 M1 [7]", -3, params.getColumnPos(6));
+        assertEquals("7 key auto 7 M1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 7 M1 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [7] [6] [5] [3] <1> [2] [4] ___|
+    public void testLayout7KeyAuto7R3() {
+        MoreKeysKeyboardParams params = createParams(7, 7, XPOS_R3);
+        assertEquals("7 key auto 7 R3 columns", 7, params.mNumColumns);
+        assertEquals("7 key auto 7 R3 rows", 1, params.mNumRows);
+        assertEquals("7 key auto 7 R3 left", 4, params.mLeftKeys);
+        assertEquals("7 key auto 7 R3 right", 3, params.mRightKeys);
+        assertEquals("7 key auto 7 R3 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 7 R3 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 7 R3 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key auto 7 R3 [4]", 2, params.getColumnPos(3));
+        assertEquals("7 key auto 7 R3 [5]", -2, params.getColumnPos(4));
+        assertEquals("7 key auto 7 R3 [6]", -3, params.getColumnPos(5));
+        assertEquals("7 key auto 7 R3 [7]", -4, params.getColumnPos(6));
+        assertEquals("7 key auto 7 R3 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 7 R3 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [7] [6] [5] [4] [3] <1> [2] ___|
+    public void testLayout7KeyAuto7R2() {
+        MoreKeysKeyboardParams params = createParams(7, 7, XPOS_R2);
+        assertEquals("7 key auto 7 R2 columns", 7, params.mNumColumns);
+        assertEquals("7 key auto 7 R2 rows", 1, params.mNumRows);
+        assertEquals("7 key auto 7 R2 left", 5, params.mLeftKeys);
+        assertEquals("7 key auto 7 R2 right", 2, params.mRightKeys);
+        assertEquals("7 key auto 7 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 7 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 7 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key auto 7 R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("7 key auto 7 R2 [5]", -3, params.getColumnPos(4));
+        assertEquals("7 key auto 7 R2 [6]", -4, params.getColumnPos(5));
+        assertEquals("7 key auto 7 R2 [7]", -5, params.getColumnPos(6));
+        assertEquals("7 key auto 7 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 7 R2 default", WIDTH * 5, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [7] [6] [5] [4] [3] [2] <1> ___|
+    public void testLayout7KeyAuto7R1() {
+        MoreKeysKeyboardParams params = createParams(7, 7, XPOS_R1);
+        assertEquals("7 key auto 7 R1 columns", 7, params.mNumColumns);
+        assertEquals("7 key auto 7 R1 rows", 1, params.mNumRows);
+        assertEquals("7 key auto 7 R1 left", 6, params.mLeftKeys);
+        assertEquals("7 key auto 7 R1 right", 1, params.mRightKeys);
+        assertEquals("7 key auto 7 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 7 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("7 key auto 7 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("7 key auto 7 R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("7 key auto 7 R1 [5]", -4, params.getColumnPos(4));
+        assertEquals("7 key auto 7 R1 [6]", -5, params.getColumnPos(5));
+        assertEquals("7 key auto 7 R1 [7]", -6, params.getColumnPos(6));
+        assertEquals("7 key auto 7 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 7 R1 default", WIDTH * 6, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [7] [6] [5] [4] [3] [2] <1>|
+    public void testLayout7KeyAuto7R0() {
+        MoreKeysKeyboardParams params = createParams(7, 7, XPOS_R0);
+        assertEquals("7 key auto 7 R0 columns", 7, params.mNumColumns);
+        assertEquals("7 key auto 7 R0 rows", 1, params.mNumRows);
+        assertEquals("7 key auto 7 R0 left", 6, params.mLeftKeys);
+        assertEquals("7 key auto 7 R0 right", 1, params.mRightKeys);
+        assertEquals("7 key auto 7 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 7 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("7 key auto 7 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("7 key auto 7 R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("7 key auto 7 R0 [5]", -4, params.getColumnPos(4));
+        assertEquals("7 key auto 7 R0 [6]", -5, params.getColumnPos(5));
+        assertEquals("7 key auto 7 R0 [7]", -6, params.getColumnPos(6));
+        assertEquals("7 key auto 7 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 7 R0 default", WIDTH * 6, params.getDefaultKeyCoordX());
+    }
+
+    //       [6] [7]
+    // [5] [3] <1> [2] [4]
+    public void testLayout7KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(7, 5, XPOS_M0);
+        assertEquals("7 key auto 5 M0 columns", 5, params.mNumColumns);
+        assertEquals("7 key auto 5 M0 rows", 2, params.mNumRows);
+        assertEquals("7 key auto 5 M0 left", 2, params.mLeftKeys);
+        assertEquals("7 key auto 5 M0 right", 3, params.mRightKeys);
+        assertEquals("7 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 5 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key auto 5 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("7 key auto 5 M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("7 key auto 5 M0 [6]", 0, params.getColumnPos(5));
+        assertEquals("7 key auto 5 M0 [7]", 1, params.getColumnPos(6));
+        assertEquals("7 key auto 5 M0 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("7 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // |[6] [7]
+    // |<1> [2] [3] [4] [5]
+    public void testLayout7KeyAuto5L0() {
+        MoreKeysKeyboardParams params = createParams(7, 5, XPOS_L0);
+        assertEquals("7 key auto 5 L0 columns", 5, params.mNumColumns);
+        assertEquals("7 key auto 5 L0 rows", 2, params.mNumRows);
+        assertEquals("7 key auto 5 L0 left", 0, params.mLeftKeys);
+        assertEquals("7 key auto 5 L0 right", 5, params.mRightKeys);
+        assertEquals("7 key auto 5 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 5 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 5 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("7 key auto 5 L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("7 key auto 5 L0 [5]", 4, params.getColumnPos(4));
+        assertEquals("7 key auto 5 L0 [6]", 0, params.getColumnPos(5));
+        assertEquals("7 key auto 5 L0 [7]", 1, params.getColumnPos(6));
+        assertEquals("7 key auto 5 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [6] [7]
+    // |___ <1> [2] [3] [4] [5]
+    public void testLayout7KeyAuto5L1() {
+        MoreKeysKeyboardParams params = createParams(7, 5, XPOS_L1);
+        assertEquals("7 key auto 5 L1 columns", 5, params.mNumColumns);
+        assertEquals("7 key auto 5 L1 rows", 2, params.mNumRows);
+        assertEquals("7 key auto 5 L1 left", 0, params.mLeftKeys);
+        assertEquals("7 key auto 5 L1 right", 5, params.mRightKeys);
+        assertEquals("7 key auto 5 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 5 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 5 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("7 key auto 5 L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("7 key auto 5 L1 [5]", 4, params.getColumnPos(4));
+        assertEquals("7 key auto 5 L1 [6]", 0, params.getColumnPos(5));
+        assertEquals("7 key auto 5 L1 [7]", 1, params.getColumnPos(6));
+        assertEquals("7 key auto 5 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___   [6] [7]
+    // |___ [3] <1> [2] [4] [5]
+    public void testLayout7KeyAuto5L2() {
+        MoreKeysKeyboardParams params = createParams(7, 5, XPOS_L2);
+        assertEquals("7 key auto 5 L2 columns", 5, params.mNumColumns);
+        assertEquals("7 key auto 5 L2 rows", 2, params.mNumRows);
+        assertEquals("7 key auto 5 L2 left", 1, params.mLeftKeys);
+        assertEquals("7 key auto 5 L2 right", 4, params.mRightKeys);
+        assertEquals("7 key auto 5 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 5 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 5 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key auto 5 L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("7 key auto 5 L2 [5]", 3, params.getColumnPos(4));
+        assertEquals("7 key auto 5 L2 [6]", 0, params.getColumnPos(5));
+        assertEquals("7 key auto 5 L2 [7]", 1, params.getColumnPos(6));
+        assertEquals("7 key auto 5 L2 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("7 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //             [7] [6]|
+    // [5] [4] [3] [2] <1>|
+    public void testLayout7KeyAuto5R0() {
+        MoreKeysKeyboardParams params = createParams(7, 5, XPOS_R0);
+        assertEquals("7 key auto 5 R0 columns", 5, params.mNumColumns);
+        assertEquals("7 key auto 5 R0 rows", 2, params.mNumRows);
+        assertEquals("7 key auto 5 R0 left", 4, params.mLeftKeys);
+        assertEquals("7 key auto 5 R0 right", 1, params.mRightKeys);
+        assertEquals("7 key auto 5 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 5 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("7 key auto 5 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("7 key auto 5 R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("7 key auto 5 R0 [5]", -4, params.getColumnPos(4));
+        assertEquals("7 key auto 5 R0 [6]", 0, params.getColumnPos(5));
+        assertEquals("7 key auto 5 R0 [7]", -1, params.getColumnPos(6));
+        assertEquals("7 key auto 5 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 5 R0 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    //             [7] [6] ___|
+    // [5] [4] [3] [2] <1> ___|
+    public void testLayout7KeyAuto5R1() {
+        MoreKeysKeyboardParams params = createParams(7, 5, XPOS_R1);
+        assertEquals("7 key auto 5 R1 columns", 5, params.mNumColumns);
+        assertEquals("7 key auto 5 R1 rows", 2, params.mNumRows);
+        assertEquals("7 key auto 5 R1 left", 4, params.mLeftKeys);
+        assertEquals("7 key auto 5 R1 right", 1, params.mRightKeys);
+        assertEquals("7 key auto 5 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 5 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("7 key auto 5 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("7 key auto 5 R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("7 key auto 5 R1 [5]", -4, params.getColumnPos(4));
+        assertEquals("7 key auto 5 R1 [6]", 0, params.getColumnPos(5));
+        assertEquals("7 key auto 5 R1 [7]", -1, params.getColumnPos(6));
+        assertEquals("7 key auto 5 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 5 R1 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    //           [6] [7]   ___|
+    // [5] [4] [3] <1> [2] ___|
+    public void testLayout7KeyAuto5R2() {
+        MoreKeysKeyboardParams params = createParams(7, 5, XPOS_R2);
+        assertEquals("7 key auto 5 R2 columns", 5, params.mNumColumns);
+        assertEquals("7 key auto 5 R2 rows", 2, params.mNumRows);
+        assertEquals("7 key auto 5 R2 left", 3, params.mLeftKeys);
+        assertEquals("7 key auto 5 R2 right", 2, params.mRightKeys);
+        assertEquals("7 key auto 5 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 5 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 5 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key auto 5 R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("7 key auto 5 R2 [5]", -3, params.getColumnPos(4));
+        assertEquals("7 key auto 5 R2 [6]", 0, params.getColumnPos(5));
+        assertEquals("7 key auto 5 R2 [7]", 1, params.getColumnPos(6));
+        assertEquals("7 key auto 5 R2 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("7 key auto 5 R2 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    //     [7]
+    // [6] [4] [5]
+    // [3] <1> [2]
+    public void testLayout7KeyAuto3M0() {
+        MoreKeysKeyboardParams params = createParams(7, 3, XPOS_M0);
+        assertEquals("7 key auto 3 M0 columns", 3, params.mNumColumns);
+        assertEquals("7 key auto 3 M0 rows", 3, params.mNumRows);
+        assertEquals("7 key auto 3 M0 left", 1, params.mLeftKeys);
+        assertEquals("7 key auto 3 M0 right", 2, params.mRightKeys);
+        assertEquals("7 key auto 3 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 3 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 3 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key auto 3 M0 [4]", 0, params.getColumnPos(3));
+        assertEquals("7 key auto 3 M0 [5]", 1, params.getColumnPos(4));
+        assertEquals("7 key auto 3 M0 [6]", -1, params.getColumnPos(5));
+        assertEquals("7 key auto 3 M0 [7]", 0, params.getColumnPos(6));
+        assertEquals("7 key auto 3 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 3 M0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // |[7]
+    // |[4] [5] [6]
+    // |<1> [2] [3]
+    public void testLayout7KeyAuto3L0() {
+        MoreKeysKeyboardParams params = createParams(7, 3, XPOS_L0);
+        assertEquals("7 key auto 3 L0 columns", 3, params.mNumColumns);
+        assertEquals("7 key auto 3 L0 rows", 3, params.mNumRows);
+        assertEquals("7 key auto 3 L0 left", 0, params.mLeftKeys);
+        assertEquals("7 key auto 3 L0 right", 3, params.mRightKeys);
+        assertEquals("7 key auto 3 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 3 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 3 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("7 key auto 3 L0 [4]", 0, params.getColumnPos(3));
+        assertEquals("7 key auto 3 L0 [5]", 1, params.getColumnPos(4));
+        assertEquals("7 key auto 3 L0 [6]", 2, params.getColumnPos(5));
+        assertEquals("7 key auto 3 L0 [7]", 0, params.getColumnPos(6));
+        assertEquals("7 key auto 3 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 3 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [7]
+    // |___ [4] [5] [6]
+    // |___ <1> [2] [3]
+    public void testLayout7KeyAuto3L1() {
+        MoreKeysKeyboardParams params = createParams(7, 3, XPOS_L1);
+        assertEquals("7 key auto 3 L1 columns", 3, params.mNumColumns);
+        assertEquals("7 key auto 3 L1 rows", 3, params.mNumRows);
+        assertEquals("7 key auto 3 L1 left", 0, params.mLeftKeys);
+        assertEquals("7 key auto 3 L1 right", 3, params.mRightKeys);
+        assertEquals("7 key auto 3 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 3 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 3 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("7 key auto 3 L1 [4]", 0, params.getColumnPos(3));
+        assertEquals("7 key auto 3 L1 [5]", 1, params.getColumnPos(4));
+        assertEquals("7 key auto 3 L1 [6]", 2, params.getColumnPos(5));
+        assertEquals("7 key auto 3 L1 [7]", 0, params.getColumnPos(6));
+        assertEquals("7 key auto 3 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 3 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___     [7]
+    // |___ [6] [4] [5]
+    // |___ [3] <1> [2]
+    public void testLayout7KeyAuto3L2() {
+        MoreKeysKeyboardParams params = createParams(7, 3, XPOS_L2);
+        assertEquals("7 key auto 3 L2 columns", 3, params.mNumColumns);
+        assertEquals("7 key auto 3 L2 rows", 3, params.mNumRows);
+        assertEquals("7 key auto 3 L2 left", 1, params.mLeftKeys);
+        assertEquals("7 key auto 3 L2 right", 2, params.mRightKeys);
+        assertEquals("7 key auto 3 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 3 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 3 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key auto 3 L2 [4]", 0, params.getColumnPos(3));
+        assertEquals("7 key auto 3 L2 [5]", 1, params.getColumnPos(4));
+        assertEquals("7 key auto 3 L2 [6]", -1, params.getColumnPos(5));
+        assertEquals("7 key auto 3 L2 [7]", 0, params.getColumnPos(6));
+        assertEquals("7 key auto 3 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 3 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //         [7]|
+    // [6] [5] [4]|
+    // [3] [2] <1>|
+    public void testLayout7KeyAuto3R0() {
+        MoreKeysKeyboardParams params = createParams(7, 3, XPOS_R0);
+        assertEquals("7 key auto 3 R0 columns", 3, params.mNumColumns);
+        assertEquals("7 key auto 3 R0 rows", 3, params.mNumRows);
+        assertEquals("7 key auto 3 R0 left", 2, params.mLeftKeys);
+        assertEquals("7 key auto 3 R0 right", 1, params.mRightKeys);
+        assertEquals("7 key auto 3 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 3 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("7 key auto 3 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("7 key auto 3 R0 [4]", 0, params.getColumnPos(3));
+        assertEquals("7 key auto 3 R0 [5]", -1, params.getColumnPos(4));
+        assertEquals("7 key auto 3 R0 [6]", -2, params.getColumnPos(5));
+        assertEquals("7 key auto 3 R0 [7]", 0, params.getColumnPos(6));
+        assertEquals("7 key auto 3 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 3 R0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    //         [7] ___|
+    // [6] [5] [4] ___|
+    // [3] [2] <1> ___|
+    public void testLayout7KeyAuto3R1() {
+        MoreKeysKeyboardParams params = createParams(7, 3, XPOS_R1);
+        assertEquals("7 key auto 3 R1 columns", 3, params.mNumColumns);
+        assertEquals("7 key auto 3 R1 rows", 3, params.mNumRows);
+        assertEquals("7 key auto 3 R1 left", 2, params.mLeftKeys);
+        assertEquals("7 key auto 3 R1 right", 1, params.mRightKeys);
+        assertEquals("7 key auto 3 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 3 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("7 key auto 3 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("7 key auto 3 R1 [4]", 0, params.getColumnPos(3));
+        assertEquals("7 key auto 3 R1 [5]", -1, params.getColumnPos(4));
+        assertEquals("7 key auto 3 R1 [6]", -2, params.getColumnPos(5));
+        assertEquals("7 key auto 3 R1 [7]", 0, params.getColumnPos(6));
+        assertEquals("7 key auto 3 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 3 R1 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    //     [7]     ___|
+    // [6] [4] [5] ___|
+    // [3] <1> [2] ___|
+    public void testLayout7KeyAuto3R2() {
+        MoreKeysKeyboardParams params = createParams(7, 3, XPOS_R2);
+        assertEquals("7 key auto 3 R2 columns", 3, params.mNumColumns);
+        assertEquals("7 key auto 3 R2 rows", 3, params.mNumRows);
+        assertEquals("7 key auto 3 R2 left", 1, params.mLeftKeys);
+        assertEquals("7 key auto 3 R2 right", 2, params.mRightKeys);
+        assertEquals("7 key auto 3 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("7 key auto 3 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key auto 3 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key auto 3 R2 [4]", 0, params.getColumnPos(3));
+        assertEquals("7 key auto 3 R2 [5]", 1, params.getColumnPos(4));
+        assertEquals("7 key auto 3 R2 [6]", -1, params.getColumnPos(5));
+        assertEquals("7 key auto 3 R2 [7]", 0, params.getColumnPos(6));
+        assertEquals("7 key auto 3 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key auto 3 R2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //     [8] [6] [7]
+    // [5] [3] <1> [2] [4]
+    public void testLayout8KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(8, 5, XPOS_M0);
+        assertEquals("8 key auto 5 M0 columns", 5, params.mNumColumns);
+        assertEquals("8 key auto 5 M0 rows", 2, params.mNumRows);
+        assertEquals("8 key auto 5 M0 left", 2, params.mLeftKeys);
+        assertEquals("8 key auto 5 M0 right", 3, params.mRightKeys);
+        assertEquals("8 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("8 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("8 key auto 5 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("8 key auto 5 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("8 key auto 5 M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("8 key auto 5 M0 [6]", 0, params.getColumnPos(5));
+        assertEquals("8 key auto 5 M0 [7]", 1, params.getColumnPos(6));
+        assertEquals("8 key auto 5 M0 [8]", -1, params.getColumnPos(7));
+        assertEquals("8 key auto 5 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // |[6] [7] [8]
+    // |<1> [2] [3] [4] [5]
+    public void testLayout8KeyAuto5L0() {
+        MoreKeysKeyboardParams params = createParams(8, 5, XPOS_L0);
+        assertEquals("8 key auto 5 L0 columns", 5, params.mNumColumns);
+        assertEquals("8 key auto 5 L0 rows", 2, params.mNumRows);
+        assertEquals("8 key auto 5 L0 left", 0, params.mLeftKeys);
+        assertEquals("8 key auto 5 L0 right", 5, params.mRightKeys);
+        assertEquals("8 key auto 5 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("8 key auto 5 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("8 key auto 5 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("8 key auto 5 L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("8 key auto 5 L0 [5]", 4, params.getColumnPos(4));
+        assertEquals("8 key auto 5 L0 [6]", 0, params.getColumnPos(5));
+        assertEquals("8 key auto 5 L0 [7]", 1, params.getColumnPos(6));
+        assertEquals("8 key auto 5 L0 [8]", 2, params.getColumnPos(7));
+        assertEquals("8 key auto 5 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [6] [7] [8]
+    // |___ <1> [2] [3] [4] [5]
+    public void testLayout8KeyAuto5L1() {
+        MoreKeysKeyboardParams params = createParams(8, 5, XPOS_L1);
+        assertEquals("8 key auto 5 L1 columns", 5, params.mNumColumns);
+        assertEquals("8 key auto 5 L1 rows", 2, params.mNumRows);
+        assertEquals("8 key auto 5 L1 left", 0, params.mLeftKeys);
+        assertEquals("8 key auto 5 L1 right", 5, params.mRightKeys);
+        assertEquals("8 key auto 5 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("8 key auto 5 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("8 key auto 5 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("8 key auto 5 L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("8 key auto 5 L1 [5]", 4, params.getColumnPos(4));
+        assertEquals("8 key auto 5 L1 [6]", 0, params.getColumnPos(5));
+        assertEquals("8 key auto 5 L1 [7]", 1, params.getColumnPos(6));
+        assertEquals("8 key auto 5 L1 [8]", 2, params.getColumnPos(7));
+        assertEquals("8 key auto 5 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [8] [6] [7]
+    // |___ [3] <1> [2] [4] [5]
+    public void testLayout8KeyAuto5L2() {
+        MoreKeysKeyboardParams params = createParams(8, 5, XPOS_L2);
+        assertEquals("8 key auto 5 L2 columns", 5, params.mNumColumns);
+        assertEquals("8 key auto 5 L2 rows", 2, params.mNumRows);
+        assertEquals("8 key auto 5 L2 left", 1, params.mLeftKeys);
+        assertEquals("8 key auto 5 L2 right", 4, params.mRightKeys);
+        assertEquals("8 key auto 5 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("8 key auto 5 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("8 key auto 5 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("8 key auto 5 L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("8 key auto 5 L2 [5]", 3, params.getColumnPos(4));
+        assertEquals("8 key auto 5 L2 [6]", 0, params.getColumnPos(5));
+        assertEquals("8 key auto 5 L2 [7]", 1, params.getColumnPos(6));
+        assertEquals("8 key auto 5 L2 [8]", -1, params.getColumnPos(7));
+        assertEquals("8 key auto 5 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //         [8] [7] [6]|
+    // [5] [4] [3] [2] <1>|
+    public void testLayout8KeyAuto5R0() {
+        MoreKeysKeyboardParams params = createParams(8, 5, XPOS_R0);
+        assertEquals("8 key auto 5 R0 columns", 5, params.mNumColumns);
+        assertEquals("8 key auto 5 R0 rows", 2, params.mNumRows);
+        assertEquals("8 key auto 5 R0 left", 4, params.mLeftKeys);
+        assertEquals("8 key auto 5 R0 right", 1, params.mRightKeys);
+        assertEquals("8 key auto 5 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("8 key auto 5 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("8 key auto 5 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("8 key auto 5 R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("8 key auto 5 R0 [5]", -4, params.getColumnPos(4));
+        assertEquals("8 key auto 5 R0 [6]", 0, params.getColumnPos(5));
+        assertEquals("8 key auto 5 R0 [7]", -1, params.getColumnPos(6));
+        assertEquals("8 key auto 5 R0 [8]", -2, params.getColumnPos(7));
+        assertEquals("8 key auto 5 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key auto 5 R0 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    //         [8] [7] [6] ___|
+    // [5] [4] [3] [2] <1> ___|
+    public void testLayout8KeyAuto5R1() {
+        MoreKeysKeyboardParams params = createParams(8, 5, XPOS_R1);
+        assertEquals("8 key auto 5 R1 columns", 5, params.mNumColumns);
+        assertEquals("8 key auto 5 R1 rows", 2, params.mNumRows);
+        assertEquals("8 key auto 5 R1 left", 4, params.mLeftKeys);
+        assertEquals("8 key auto 5 R1 right", 1, params.mRightKeys);
+        assertEquals("8 key auto 5 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("8 key auto 5 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("8 key auto 5 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("8 key auto 5 R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("8 key auto 5 R1 [5]", -4, params.getColumnPos(4));
+        assertEquals("8 key auto 5 R1 [6]", 0, params.getColumnPos(5));
+        assertEquals("8 key auto 5 R1 [7]", -1, params.getColumnPos(6));
+        assertEquals("8 key auto 5 R1 [8]", -2, params.getColumnPos(7));
+        assertEquals("8 key auto 5 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key auto 5 R1 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    //         [8] [6] [7] ___|
+    // [5] [4] [3] <1> [2] ___|
+    public void testLayout8KeyAuto5R2() {
+        MoreKeysKeyboardParams params = createParams(8, 5, XPOS_R2);
+        assertEquals("8 key auto 5 R2 columns", 5, params.mNumColumns);
+        assertEquals("8 key auto 5 R2 rows", 2, params.mNumRows);
+        assertEquals("8 key auto 5 R2 left", 3, params.mLeftKeys);
+        assertEquals("8 key auto 5 R2 right", 2, params.mRightKeys);
+        assertEquals("8 key auto 5 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("8 key auto 5 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("8 key auto 5 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("8 key auto 5 R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("8 key auto 5 R2 [5]", -3, params.getColumnPos(4));
+        assertEquals("8 key auto 5 R2 [6]", 0, params.getColumnPos(5));
+        assertEquals("8 key auto 5 R2 [7]", 1, params.getColumnPos(6));
+        assertEquals("8 key auto 5 R2 [8]", -1, params.getColumnPos(7));
+        assertEquals("8 key auto 5 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key auto 5 R2 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    //   [8] [6] [7] [9]
+    // [5] [3] <1> [2] [4]
+    public void testLayout9KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(9, 5, XPOS_M0);
+        assertEquals("9 key auto 5 M0 columns", 5, params.mNumColumns);
+        assertEquals("9 key auto 5 M0 rows", 2, params.mNumRows);
+        assertEquals("9 key auto 5 M0 left", 2, params.mLeftKeys);
+        assertEquals("9 key auto 5 M0 right", 3, params.mRightKeys);
+        assertEquals("9 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("9 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("9 key auto 5 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("9 key auto 5 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("9 key auto 5 M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("9 key auto 5 M0 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key auto 5 M0 [7]", 1, params.getColumnPos(6));
+        assertEquals("9 key auto 5 M0 [8]", -1, params.getColumnPos(7));
+        assertEquals("9 key auto 5 M0 [9]", 2, params.getColumnPos(8));
+        assertEquals("9 key auto 5 M0 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("9 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // |[6] [7] [8] [9]
+    // |<1> [2] [3] [4] [5]
+    public void testLayout9KeyAuto5L0() {
+        MoreKeysKeyboardParams params = createParams(9, 5, XPOS_L0);
+        assertEquals("9 key auto 5 L0 columns", 5, params.mNumColumns);
+        assertEquals("9 key auto 5 L0 rows", 2, params.mNumRows);
+        assertEquals("9 key auto 5 L0 left", 0, params.mLeftKeys);
+        assertEquals("9 key auto 5 L0 right", 5, params.mRightKeys);
+        assertEquals("9 key auto 5 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("9 key auto 5 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("9 key auto 5 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("9 key auto 5 L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("9 key auto 5 L0 [5]", 4, params.getColumnPos(4));
+        assertEquals("9 key auto 5 L0 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key auto 5 L0 [7]", 1, params.getColumnPos(6));
+        assertEquals("9 key auto 5 L0 [8]", 2, params.getColumnPos(7));
+        assertEquals("9 key auto 5 L0 [9]", 3, params.getColumnPos(8));
+        assertEquals("9 key auto 5 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("9 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [6] [7] [8] [9]
+    // |___ <1> [2] [3] [4] [5]
+    public void testLayout9KeyAuto5L1() {
+        MoreKeysKeyboardParams params = createParams(9, 5, XPOS_L1);
+        assertEquals("9 key auto 5 L1 columns", 5, params.mNumColumns);
+        assertEquals("9 key auto 5 L1 rows", 2, params.mNumRows);
+        assertEquals("9 key auto 5 L1 left", 0, params.mLeftKeys);
+        assertEquals("9 key auto 5 L1 right", 5, params.mRightKeys);
+        assertEquals("9 key auto 5 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("9 key auto 5 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("9 key auto 5 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("9 key auto 5 L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("9 key auto 5 L1 [5]", 4, params.getColumnPos(4));
+        assertEquals("9 key auto 5 L1 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key auto 5 L1 [7]", 1, params.getColumnPos(6));
+        assertEquals("9 key auto 5 L1 [8]", 2, params.getColumnPos(7));
+        assertEquals("9 key auto 5 L1 [9]", 3, params.getColumnPos(8));
+        assertEquals("9 key auto 5 L1 adjust",0, params.mTopRowAdjustment);
+        assertEquals("9 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___   [6] [7] [8] [9]
+    // |___ [3] <1> [2] [4] [5]
+    public void testLayout9KeyAuto5L2() {
+        MoreKeysKeyboardParams params = createParams(9, 5, XPOS_L2);
+        assertEquals("9 key auto 5 L2 columns", 5, params.mNumColumns);
+        assertEquals("9 key auto 5 L2 rows", 2, params.mNumRows);
+        assertEquals("9 key auto 5 L2 left", 1, params.mLeftKeys);
+        assertEquals("9 key auto 5 L2 right", 4, params.mRightKeys);
+        assertEquals("9 key auto 5 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("9 key auto 5 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("9 key auto 5 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("9 key auto 5 L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("9 key auto 5 L2 [5]", 3, params.getColumnPos(4));
+        assertEquals("9 key auto 5 L2 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key auto 5 L2 [7]", 1, params.getColumnPos(6));
+        assertEquals("9 key auto 5 L2 [8]", 2, params.getColumnPos(7));
+        assertEquals("9 key auto 5 L2 [9]", 3, params.getColumnPos(8));
+        assertEquals("9 key auto 5 L2 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("9 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //     [9] [8] [7] [6]|
+    // [5] [4] [3] [2] <1>|
+    public void testLayout9KeyAuto5R0() {
+        MoreKeysKeyboardParams params = createParams(9, 5, XPOS_R0);
+        assertEquals("9 key auto 5 R0 columns", 5, params.mNumColumns);
+        assertEquals("9 key auto 5 R0 rows", 2, params.mNumRows);
+        assertEquals("9 key auto 5 R0 left", 4, params.mLeftKeys);
+        assertEquals("9 key auto 5 R0 right", 1, params.mRightKeys);
+        assertEquals("9 key auto 5 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("9 key auto 5 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("9 key auto 5 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("9 key auto 5 R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("9 key auto 5 R0 [5]", -4, params.getColumnPos(4));
+        assertEquals("9 key auto 5 R0 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key auto 5 R0 [7]", -1, params.getColumnPos(6));
+        assertEquals("9 key auto 5 R0 [8]", -2, params.getColumnPos(7));
+        assertEquals("9 key auto 5 R0 [9]", -3, params.getColumnPos(8));
+        assertEquals("9 key auto 5 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("9 key auto 5 R0 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    //     [9] [8] [7] [6] ___|
+    // [5] [4] [3] [2] <1> ___|
+    public void testLayout9KeyAuto5R1() {
+        MoreKeysKeyboardParams params = createParams(9, 5, XPOS_R1);
+        assertEquals("9 key auto 5 R1 columns", 5, params.mNumColumns);
+        assertEquals("9 key auto 5 R1 rows", 2, params.mNumRows);
+        assertEquals("9 key auto 5 R1 left", 4, params.mLeftKeys);
+        assertEquals("9 key auto 5 R1 right", 1, params.mRightKeys);
+        assertEquals("9 key auto 5 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("9 key auto 5 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("9 key auto 5 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("9 key auto 5 R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("9 key auto 5 R1 [5]", -4, params.getColumnPos(4));
+        assertEquals("9 key auto 5 R1 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key auto 5 R1 [7]", -1, params.getColumnPos(6));
+        assertEquals("9 key auto 5 R1 [8]", -2, params.getColumnPos(7));
+        assertEquals("9 key auto 5 R1 [9]", -3, params.getColumnPos(8));
+        assertEquals("9 key auto 5 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("9 key auto 5 R1 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    //   [9] [8] [6] [7]   ___|
+    // [5] [4] [3] <1> [2] ___|
+    public void testLayout9KeyAuto5R2() {
+        MoreKeysKeyboardParams params = createParams(9, 5, XPOS_R2);
+        assertEquals("9 key auto 5 R2 columns", 5, params.mNumColumns);
+        assertEquals("9 key auto 5 R2 rows", 2, params.mNumRows);
+        assertEquals("9 key auto 5 R2 left", 3, params.mLeftKeys);
+        assertEquals("9 key auto 5 R2 right", 2, params.mRightKeys);
+        assertEquals("9 key auto 5 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("9 key auto 5 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("9 key auto 5 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("9 key auto 5 R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("9 key auto 5 R2 [5]", -3, params.getColumnPos(4));
+        assertEquals("9 key auto 5 R2 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key auto 5 R2 [7]", 1, params.getColumnPos(6));
+        assertEquals("9 key auto 5 R2 [8]", -1, params.getColumnPos(7));
+        assertEquals("9 key auto 5 R2 [9]", -2, params.getColumnPos(8));
+        assertEquals("9 key auto 5 R2 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("9 key auto 5 R2 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // [A] [8] [6] [7] [9]
+    // [5] [3] <1> [2] [4]
+    public void testLayout10KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(10, 5, XPOS_M0);
+        assertEquals("10 key auto 5 M0 columns", 5, params.mNumColumns);
+        assertEquals("10 key auto 5 M0 rows", 2, params.mNumRows);
+        assertEquals("10 key auto 5 M0 left", 2, params.mLeftKeys);
+        assertEquals("10 key auto 5 M0 right", 3, params.mRightKeys);
+        assertEquals("10 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("10 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("10 key auto 5 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("10 key auto 5 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("10 key auto 5 M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("10 key auto 5 M0 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key auto 5 M0 [7]", 1, params.getColumnPos(6));
+        assertEquals("10 key auto 5 M0 [8]", -1, params.getColumnPos(7));
+        assertEquals("10 key auto 5 M0 [9]", 2, params.getColumnPos(8));
+        assertEquals("10 key auto 5 M0 [A]", -2, params.getColumnPos(9));
+        assertEquals("10 key auto 5 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // |[6] [7] [8] [9] [A]
+    // |<1> [2] [3] [4] [5]
+    public void testLayout10KeyAuto5L0() {
+        MoreKeysKeyboardParams params = createParams(10, 5, XPOS_L0);
+        assertEquals("10 key auto 5 L0 columns", 5, params.mNumColumns);
+        assertEquals("10 key auto 5 L0 rows", 2, params.mNumRows);
+        assertEquals("10 key auto 5 L0 left", 0, params.mLeftKeys);
+        assertEquals("10 key auto 5 L0 right", 5, params.mRightKeys);
+        assertEquals("10 key auto 5 L0 <1>", 0, params.getColumnPos(0));
+        assertEquals("10 key auto 5 L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("10 key auto 5 L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("10 key auto 5 L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("10 key auto 5 L0 [5]", 4, params.getColumnPos(4));
+        assertEquals("10 key auto 5 L0 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key auto 5 L0 [7]", 1, params.getColumnPos(6));
+        assertEquals("10 key auto 5 L0 [8]", 2, params.getColumnPos(7));
+        assertEquals("10 key auto 5 L0 [9]", 3, params.getColumnPos(8));
+        assertEquals("10 key auto 5 L0 [A]", 4, params.getColumnPos(9));
+        assertEquals("10 key auto 5 L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [6] [7] [8] [9] [A]
+    // |___ <1> [2] [3] [4] [5]
+    public void testLayout10KeyAuto5L1() {
+        MoreKeysKeyboardParams params = createParams(10, 5, XPOS_L1);
+        assertEquals("10 key auto 5 L1 columns", 5, params.mNumColumns);
+        assertEquals("10 key auto 5 L1 rows", 2, params.mNumRows);
+        assertEquals("10 key auto 5 L1 left", 0, params.mLeftKeys);
+        assertEquals("10 key auto 5 L1 right", 5, params.mRightKeys);
+        assertEquals("10 key auto 5 L1 <1>", 0, params.getColumnPos(0));
+        assertEquals("10 key auto 5 L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("10 key auto 5 L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("10 key auto 5 L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("10 key auto 5 L1 [5]", 4, params.getColumnPos(4));
+        assertEquals("10 key auto 5 L1 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key auto 5 L1 [7]", 1, params.getColumnPos(6));
+        assertEquals("10 key auto 5 L1 [8]", 2, params.getColumnPos(7));
+        assertEquals("10 key auto 5 L1 [9]", 3, params.getColumnPos(8));
+        assertEquals("10 key auto 5 L1 [A]", 4, params.getColumnPos(9));
+        assertEquals("10 key auto 5 L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [8] [6] [7] [9] [A]
+    // |___ [3] <1> [2] [4] [5]
+    public void testLayout10KeyAuto5L2() {
+        MoreKeysKeyboardParams params = createParams(10, 5, XPOS_L2);
+        assertEquals("10 key auto 5 L2 columns", 5, params.mNumColumns);
+        assertEquals("10 key auto 5 L2 rows", 2, params.mNumRows);
+        assertEquals("10 key auto 5 L2 left", 1, params.mLeftKeys);
+        assertEquals("10 key auto 5 L2 right", 4, params.mRightKeys);
+        assertEquals("10 key auto 5 L2 <1>", 0, params.getColumnPos(0));
+        assertEquals("10 key auto 5 L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("10 key auto 5 L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("10 key auto 5 L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("10 key auto 5 L2 [5]", 3, params.getColumnPos(4));
+        assertEquals("10 key auto 5 L2 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key auto 5 L2 [7]", 1, params.getColumnPos(6));
+        assertEquals("10 key auto 5 L2 [8]", -1, params.getColumnPos(7));
+        assertEquals("10 key auto 5 L2 [9]", 2, params.getColumnPos(8));
+        assertEquals("10 key auto 5 L2 [A]", 3, params.getColumnPos(9));
+        assertEquals("10 key auto 5 L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [A] [9] [8] [7] [6]|
+    // [5] [4] [3] [2] <1>|
+    public void testLayout10KeyAuto5R0() {
+        MoreKeysKeyboardParams params = createParams(10, 5, XPOS_R0);
+        assertEquals("10 key auto 5 R0 columns", 5, params.mNumColumns);
+        assertEquals("10 key auto 5 R0 rows", 2, params.mNumRows);
+        assertEquals("10 key auto 5 R0 left", 4, params.mLeftKeys);
+        assertEquals("10 key auto 5 R0 right", 1, params.mRightKeys);
+        assertEquals("10 key auto 5 R0 <1>", 0, params.getColumnPos(0));
+        assertEquals("10 key auto 5 R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("10 key auto 5 R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("10 key auto 5 R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("10 key auto 5 R0 [5]", -4, params.getColumnPos(4));
+        assertEquals("10 key auto 5 R0 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key auto 5 R0 [7]", -1, params.getColumnPos(6));
+        assertEquals("10 key auto 5 R0 [8]", -2, params.getColumnPos(7));
+        assertEquals("10 key auto 5 R0 [9]", -3, params.getColumnPos(8));
+        assertEquals("10 key auto 5 R0 [A]", -4, params.getColumnPos(9));
+        assertEquals("10 key auto 5 R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key auto 5 R0 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    // [A] [9] [8] [7] [6] ___|
+    // [5] [4] [3] [2] <1> ___|
+    public void testLayout10KeyAuto5R1() {
+        MoreKeysKeyboardParams params = createParams(10, 5, XPOS_R1);
+        assertEquals("10 key auto 5 R1 columns", 5, params.mNumColumns);
+        assertEquals("10 key auto 5 R1 rows", 2, params.mNumRows);
+        assertEquals("10 key auto 5 R1 left", 4, params.mLeftKeys);
+        assertEquals("10 key auto 5 R1 right", 1, params.mRightKeys);
+        assertEquals("10 key auto 5 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("10 key auto 5 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("10 key auto 5 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("10 key auto 5 R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("10 key auto 5 R1 [5]", -4, params.getColumnPos(4));
+        assertEquals("10 key auto 5 R1 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key auto 5 R1 [7]", -1, params.getColumnPos(6));
+        assertEquals("10 key auto 5 R1 [8]", -2, params.getColumnPos(7));
+        assertEquals("10 key auto 5 R1 [9]", -3, params.getColumnPos(8));
+        assertEquals("10 key auto 5 R1 [A]", -4, params.getColumnPos(9));
+        assertEquals("10 key auto 5 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key auto 5 R1 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    // [A] [9] [8] [6] [7] ___|
+    // [5] [4] [3] <1> [2] ___|
+    public void testLayout10KeyAuto5R2() {
+        MoreKeysKeyboardParams params = createParams(10, 5, XPOS_R2);
+        assertEquals("10 key auto 5 R2 columns", 5, params.mNumColumns);
+        assertEquals("10 key auto 5 R2 rows", 2, params.mNumRows);
+        assertEquals("10 key auto 5 R2 left", 3, params.mLeftKeys);
+        assertEquals("10 key auto 5 R2 right", 2, params.mRightKeys);
+        assertEquals("10 key auto 5 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("10 key auto 5 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("10 key auto 5 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("10 key auto 5 R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("10 key auto 5 R2 [5]", -3, params.getColumnPos(4));
+        assertEquals("10 key auto 5 R2 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key auto 5 R2 [7]", 1, params.getColumnPos(6));
+        assertEquals("10 key auto 5 R2 [8]", -1, params.getColumnPos(7));
+        assertEquals("10 key auto 5 R2 [9]", -2, params.getColumnPos(8));
+        assertEquals("10 key auto 5 R2 [A]", -3, params.getColumnPos(9));
+        assertEquals("10 key auto 5 R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key auto 5 R2 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    //         [B]
+    // [A] [8] [6] [7] [9]
+    // [5] [3] <1> [2] [4]
+    public void testLayout11KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(11, 5, XPOS_M0);
+        assertEquals("11 key auto 5 M0 columns", 5, params.mNumColumns);
+        assertEquals("11 key auto 5 M0 rows", 3, params.mNumRows);
+        assertEquals("11 key auto 5 M0 left", 2, params.mLeftKeys);
+        assertEquals("11 key auto 5 M0 right", 3, params.mRightKeys);
+        assertEquals("11 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("11 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("11 key auto 5 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("11 key auto 5 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("11 key auto 5 M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("11 key auto 5 M0 [6]", 0, params.getColumnPos(5));
+        assertEquals("11 key auto 5 M0 [7]", 1, params.getColumnPos(6));
+        assertEquals("11 key auto 5 M0 [8]", -1, params.getColumnPos(7));
+        assertEquals("11 key auto 5 M0 [9]", 2, params.getColumnPos(8));
+        assertEquals("11 key auto 5 M0 [A]", -2, params.getColumnPos(9));
+        assertEquals("11 key auto 5 M0 [B]", 0, params.getColumnPos(10));
+        assertEquals("11 key auto 5 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("11 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    //       [B] [C]
+    // [A] [8] [6] [7] [9]
+    // [5] [3] <1> [2] [4]
+    public void testLayout12KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(12, 5, XPOS_M0);
+        assertEquals("12 key auto 5 M0 columns", 5, params.mNumColumns);
+        assertEquals("12 key auto 5 M0 rows", 3, params.mNumRows);
+        assertEquals("12 key auto 5 M0 left", 2, params.mLeftKeys);
+        assertEquals("12 key auto 5 M0 right", 3, params.mRightKeys);
+        assertEquals("12 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("12 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("12 key auto 5 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("12 key auto 5 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("12 key auto 5 M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("12 key auto 5 M0 [6]", 0, params.getColumnPos(5));
+        assertEquals("12 key auto 5 M0 [7]", 1, params.getColumnPos(6));
+        assertEquals("12 key auto 5 M0 [8]", -1, params.getColumnPos(7));
+        assertEquals("12 key auto 5 M0 [9]", 2, params.getColumnPos(8));
+        assertEquals("12 key auto 5 M0 [A]", -2, params.getColumnPos(9));
+        assertEquals("12 key auto 5 M0 [B]", 0, params.getColumnPos(10));
+        assertEquals("12 key auto 5 M0 [C]", 1, params.getColumnPos(11));
+        assertEquals("12 key auto 5 M0 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("12 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    //     [D] [B] [C]
+    // [A] [8] [6] [7] [9]
+    // [5] [3] <1> [2] [4]
+    public void testLayout13KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(13, 5, XPOS_M0);
+        assertEquals("13 key auto 5 M0 columns", 5, params.mNumColumns);
+        assertEquals("13 key auto 5 M0 rows", 3, params.mNumRows);
+        assertEquals("13 key auto 5 M0 left", 2, params.mLeftKeys);
+        assertEquals("13 key auto 5 M0 right", 3, params.mRightKeys);
+        assertEquals("13 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("13 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("13 key auto 5 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("13 key auto 5 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("13 key auto 5 M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("13 key auto 5 M0 [6]", 0, params.getColumnPos(5));
+        assertEquals("13 key auto 5 M0 [7]", 1, params.getColumnPos(6));
+        assertEquals("13 key auto 5 M0 [8]", -1, params.getColumnPos(7));
+        assertEquals("13 key auto 5 M0 [9]", 2, params.getColumnPos(8));
+        assertEquals("13 key auto 5 M0 [A]", -2, params.getColumnPos(9));
+        assertEquals("13 key auto 5 M0 [B]", 0, params.getColumnPos(10));
+        assertEquals("13 key auto 5 M0 [C]", 1, params.getColumnPos(11));
+        assertEquals("13 key auto 5 M0 [D]", -1, params.getColumnPos(12));
+        assertEquals("13 key auto 5 M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("13 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    //   [D] [B] [C] [E]
+    // [A] [8] [6] [7] [9]
+    // [5] [3] <1> [2] [4]
+    public void testLayout14KeyAuto5M0() {
+        MoreKeysKeyboardParams params = createParams(14, 5, XPOS_M0);
+        assertEquals("13 key auto 5 M0 columns", 5, params.mNumColumns);
+        assertEquals("13 key auto 5 M0 rows", 3, params.mNumRows);
+        assertEquals("13 key auto 5 M0 left", 2, params.mLeftKeys);
+        assertEquals("13 key auto 5 M0 right", 3, params.mRightKeys);
+        assertEquals("13 key auto 5 M0 <1>", 0, params.getColumnPos(0));
+        assertEquals("13 key auto 5 M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("13 key auto 5 M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("13 key auto 5 M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("13 key auto 5 M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("13 key auto 5 M0 [6]", 0, params.getColumnPos(5));
+        assertEquals("13 key auto 5 M0 [7]", 1, params.getColumnPos(6));
+        assertEquals("13 key auto 5 M0 [8]", -1, params.getColumnPos(7));
+        assertEquals("13 key auto 5 M0 [9]", 2, params.getColumnPos(8));
+        assertEquals("13 key auto 5 M0 [A]", -2, params.getColumnPos(9));
+        assertEquals("13 key auto 5 M0 [B]", 0, params.getColumnPos(10));
+        assertEquals("13 key auto 5 M0 [C]", 1, params.getColumnPos(11));
+        assertEquals("13 key auto 5 M0 [D]", -1, params.getColumnPos(12));
+        assertEquals("13 key auto 5 M0 [E]", 2, params.getColumnPos(13));
+        assertEquals("13 key auto 5 M0 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("13 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    //                     [J] [I] [H] ___|
+    // [G] [F] [E] [D] [C] [B] [A] [9] ___|
+    // [8] [7] [6] [5] [4] [3] [2] <1> ___|
+    public void testLayout19KeyAuto8R1() {
+        MoreKeysKeyboardParams params = createParams(19, 8, XPOS_R1);
+        assertEquals("19 key auto 8 R1 columns", 8, params.mNumColumns);
+        assertEquals("19 key auto 8 R1 rows", 3, params.mNumRows);
+        assertEquals("19 key auto 8 R1 left", 7, params.mLeftKeys);
+        assertEquals("19 key auto 8 R1 right", 1, params.mRightKeys);
+        assertEquals("19 key auto 8 R1 <1>", 0, params.getColumnPos(0));
+        assertEquals("19 key auto 8 R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("19 key auto 8 R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("19 key auto 8 R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("19 key auto 8 R1 [5]", -4, params.getColumnPos(4));
+        assertEquals("19 key auto 8 R1 [6]", -5, params.getColumnPos(5));
+        assertEquals("19 key auto 8 R1 [7]", -6, params.getColumnPos(6));
+        assertEquals("19 key auto 8 R1 [8]", -7, params.getColumnPos(7));
+        assertEquals("19 key auto 8 R1 [9]", 0, params.getColumnPos(8));
+        assertEquals("19 key auto 8 R1 [A]", -1, params.getColumnPos(9));
+        assertEquals("19 key auto 8 R1 [B]", -2, params.getColumnPos(10));
+        assertEquals("19 key auto 8 R1 [C]", -3, params.getColumnPos(11));
+        assertEquals("19 key auto 8 R1 [D]", -4, params.getColumnPos(12));
+        assertEquals("19 key auto 8 R1 [E]", -5, params.getColumnPos(13));
+        assertEquals("19 key auto 8 R1 [F]", -6, params.getColumnPos(14));
+        assertEquals("19 key auto 8 R1 [G]", -7, params.getColumnPos(15));
+        assertEquals("19 key auto 8 R1 [H]", 0, params.getColumnPos(16));
+        assertEquals("19 key auto 8 R1 [I]", -1, params.getColumnPos(17));
+        assertEquals("19 key auto 8 R1 [J]", -2, params.getColumnPos(18));
+        assertEquals("19 key auto 8 R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("19 key auto 8 R1 default", WIDTH * 7, params.getDefaultKeyCoordX());
+    }
+
+    //                   [J] [H] [I]   ___|
+    // [G] [F] [E] [D] [C] [B] [9] [A] ___|
+    // [8] [7] [6] [5] [4] [3] <1> [2] ___|
+    public void testLayout19KeyAuto8R2() {
+        MoreKeysKeyboardParams params = createParams(19, 8, XPOS_R2);
+        assertEquals("19 key auto 8 R2 columns", 8, params.mNumColumns);
+        assertEquals("19 key auto 8 R2 rows", 3, params.mNumRows);
+        assertEquals("19 key auto 8 R2 left", 6, params.mLeftKeys);
+        assertEquals("19 key auto 8 R2 right", 2, params.mRightKeys);
+        assertEquals("19 key auto 8 R2 <1>", 0, params.getColumnPos(0));
+        assertEquals("19 key auto 8 R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("19 key auto 8 R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("19 key auto 8 R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("19 key auto 8 R2 [5]", -3, params.getColumnPos(4));
+        assertEquals("19 key auto 8 R2 [6]", -4, params.getColumnPos(5));
+        assertEquals("19 key auto 8 R2 [7]", -5, params.getColumnPos(6));
+        assertEquals("19 key auto 8 R2 [8]", -6, params.getColumnPos(7));
+        assertEquals("19 key auto 8 R2 [9]", 0, params.getColumnPos(8));
+        assertEquals("19 key auto 8 R2 [A]", 1, params.getColumnPos(9));
+        assertEquals("19 key auto 8 R2 [B]", -1, params.getColumnPos(10));
+        assertEquals("19 key auto 8 R2 [C]", -2, params.getColumnPos(11));
+        assertEquals("19 key auto 8 R2 [D]", -3, params.getColumnPos(12));
+        assertEquals("19 key auto 8 R2 [E]", -4, params.getColumnPos(13));
+        assertEquals("19 key auto 8 R2 [F]", -5, params.getColumnPos(14));
+        assertEquals("19 key auto 8 R2 [G]", -6, params.getColumnPos(15));
+        assertEquals("19 key auto 8 R2 [H]", 0, params.getColumnPos(16));
+        assertEquals("19 key auto 8 R2 [I]", 1, params.getColumnPos(17));
+        assertEquals("19 key auto 8 R2 [J]", -1, params.getColumnPos(18));
+        assertEquals("19 key auto 8 R2 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("19 key auto 8 R2 default", WIDTH * 6, params.getDefaultKeyCoordX());
+    }
+
+    //               [J] [H] [I]       ___|
+    // [G] [F] [E] [D] [B] [9] [A] [C] ___|
+    // [8] [7] [6] [5] [3] <1> [2] [4] ___|
+    public void testLayout19KeyAuto8R3() {
+        MoreKeysKeyboardParams params = createParams(19, 8, XPOS_R3);
+        assertEquals("19 key auto 8 R3 columns", 8, params.mNumColumns);
+        assertEquals("19 key auto 8 R3 rows", 3, params.mNumRows);
+        assertEquals("19 key auto 8 R3 left", 5, params.mLeftKeys);
+        assertEquals("19 key auto 8 R3 right", 3, params.mRightKeys);
+        assertEquals("19 key auto 8 R3 <1>", 0, params.getColumnPos(0));
+        assertEquals("19 key auto 8 R3 [2]", 1, params.getColumnPos(1));
+        assertEquals("19 key auto 8 R3 [3]", -1, params.getColumnPos(2));
+        assertEquals("19 key auto 8 R3 [4]", 2, params.getColumnPos(3));
+        assertEquals("19 key auto 8 R3 [5]", -2, params.getColumnPos(4));
+        assertEquals("19 key auto 8 R3 [6]", -3, params.getColumnPos(5));
+        assertEquals("19 key auto 8 R3 [7]", -4, params.getColumnPos(6));
+        assertEquals("19 key auto 8 R3 [8]", -5, params.getColumnPos(7));
+        assertEquals("19 key auto 8 R3 [9]", 0, params.getColumnPos(8));
+        assertEquals("19 key auto 8 R3 [A]", 1, params.getColumnPos(9));
+        assertEquals("19 key auto 8 R3 [B]", -1, params.getColumnPos(10));
+        assertEquals("19 key auto 8 R3 [C]", 2, params.getColumnPos(11));
+        assertEquals("19 key auto 8 R3 [D]", -2, params.getColumnPos(12));
+        assertEquals("19 key auto 8 R3 [E]", -3, params.getColumnPos(13));
+        assertEquals("19 key auto 8 R3 [F]", -4, params.getColumnPos(14));
+        assertEquals("19 key auto 8 R3 [G]", -5, params.getColumnPos(15));
+        assertEquals("19 key auto 8 R3 [H]", 0, params.getColumnPos(16));
+        assertEquals("19 key auto 8 R3 [I]", 1, params.getColumnPos(17));
+        assertEquals("19 key auto 8 R3 [J]", -1, params.getColumnPos(18));
+        assertEquals("19 key auto 8 R3 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("19 key auto 8 R3 default", WIDTH * 5, params.getDefaultKeyCoordX());
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java
index 6d9c3fd..7e51d95 100644
--- a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java
@@ -47,7 +47,8 @@
             final int coordXInParent) {
         final MoreKeysKeyboardParams params = new MoreKeysKeyboardParams();
         params.setParameters(numKeys, columnNum, WIDTH, HEIGHT, coordXInParent, KEYBOARD_WIDTH,
-                true /* isFixedOrderColumn */, 0 /* dividerWidth */);
+                true /* isMoreKeysFixedColumn */, true /* isMoreKeysFixedOrder */,
+                0 /* dividerWidth */);
         return params;
     }
 
diff --git a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderMaxOrderTests.java
similarity index 99%
rename from tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java
rename to tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderMaxOrderTests.java
index b213721..806790e 100644
--- a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderMaxOrderTests.java
@@ -22,7 +22,7 @@
 import com.android.inputmethod.keyboard.MoreKeysKeyboard.MoreKeysKeyboardParams;
 
 @MediumTest
-public class MoreKeysKeyboardBuilderTests extends AndroidTestCase {
+public class MoreKeysKeyboardBuilderMaxOrderTests extends AndroidTestCase {
     private static final int WIDTH = 10;
     private static final int HEIGHT = 10;
 
@@ -47,7 +47,8 @@
             final int coordXInParent) {
         final MoreKeysKeyboardParams params = new MoreKeysKeyboardParams();
         params.setParameters(numKeys, maxColumns, WIDTH, HEIGHT, coordXInParent, KEYBOARD_WIDTH,
-                false /* isFixedOrderColumn */, 0 /* dividerWidth */);
+                false /* isMoreKeysFixedColumn */, false /* isMoreKeysFixedOrder */,
+                0 /* dividerWidth */);
         return params;
     }
 
diff --git a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java
index e6fb282..af22fb8 100644
--- a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java
+++ b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java
@@ -31,13 +31,17 @@
  */
 @LargeTest
 public class DistracterFilterTest extends AndroidTestCase {
+    private DictionaryFacilitatorLruCache mDictionaryFacilitatorLruCache;
     private DistracterFilterCheckingExactMatchesAndSuggestions mDistracterFilter;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         final Context context = getContext();
-        mDistracterFilter = new DistracterFilterCheckingExactMatchesAndSuggestions(context);
+        mDictionaryFacilitatorLruCache = new DictionaryFacilitatorLruCache(context,
+                2 /* maxSize */, "" /* dictionaryNamePrefix */);
+        mDistracterFilter = new DistracterFilterCheckingExactMatchesAndSuggestions(context,
+                mDictionaryFacilitatorLruCache);
         RichInputMethodManager.init(context);
         final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
         final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
@@ -50,6 +54,11 @@
         mDistracterFilter.updateEnabledSubtypes(subtypes);
     }
 
+    @Override
+    protected void tearDown() {
+        mDictionaryFacilitatorLruCache.evictAll();
+    }
+
     public void testIsDistractorToWordsInDictionaries() {
         final PrevWordsInfo EMPTY_PREV_WORDS_INFO = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
 
diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
index c28d08c..869c550 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
@@ -148,5 +148,15 @@
 
         // Make sure getTypedWordInfoOrNull() returns null.
         assertNull(SuggestedWords.EMPTY.getTypedWordInfoOrNull());
+
+        final SuggestedWords emptySuggestedWords = new SuggestedWords(
+                new ArrayList<SuggestedWordInfo>(), null /* rawSuggestions */,
+                false /* typedWordValid */,
+                false /* willAutoCorrect */,
+                false /* isObsoleteSuggestions */,
+                SuggestedWords.INPUT_STYLE_NONE);
+        assertNull(emptySuggestedWords.getTypedWordInfoOrNull());
+
+        assertNull(SuggestedWords.EMPTY.getTypedWordInfoOrNull());
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java
index b656292..e9a97ff 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java
@@ -52,7 +52,7 @@
         final ArrayList<String> dictTypes = new ArrayList<>();
         dictTypes.add(Dictionary.TYPE_MAIN);
         dictTypes.add(Dictionary.TYPE_PERSONALIZATION);
-        final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator();
+        final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator(getContext());
         dictionaryFacilitator.resetDictionariesForTesting(getContext(), LOCALE_EN_US, dictTypes,
                 new HashMap<String, File>(), new HashMap<String, Map<String, String>>());
         return dictionaryFacilitator;