Merge "Fix French mini keyboard keys precedence"
diff --git a/java/res/anim/key_preview_fadein.xml b/java/res/anim/key_preview_fadein.xml
deleted file mode 100644
index 9fad7b9..0000000
--- a/java/res/anim/key_preview_fadein.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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"
-    android:interpolator="@android:anim/decelerate_interpolator"
->
-    <alpha
-        android:fromAlpha="0.5"
-        android:toAlpha="1.0"
-        android:duration="@integer/config_preview_fadein_anim_time" />
-</set>
diff --git a/java/res/anim/key_preview_fadeout.xml b/java/res/anim/key_preview_fadeout.xml
deleted file mode 100644
index 7de5123..0000000
--- a/java/res/anim/key_preview_fadeout.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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"
-    android:interpolator="@android:anim/accelerate_interpolator"
->
-    <alpha
-        android:fromAlpha="1.0"
-        android:toAlpha="0.0"
-        android:duration="@integer/config_preview_fadeout_anim_time" />
-</set>
diff --git a/java/res/anim/mini_keyboard_fadein.xml b/java/res/anim/mini_keyboard_fadein.xml
index 9fad7b9..f80e8b8 100644
--- a/java/res/anim/mini_keyboard_fadein.xml
+++ b/java/res/anim/mini_keyboard_fadein.xml
@@ -25,5 +25,5 @@
     <alpha
         android:fromAlpha="0.5"
         android:toAlpha="1.0"
-        android:duration="@integer/config_preview_fadein_anim_time" />
+        android:duration="@integer/config_mini_keyboard_fadein_anim_time" />
 </set>
diff --git a/java/res/anim/mini_keyboard_fadeout.xml b/java/res/anim/mini_keyboard_fadeout.xml
index 7de5123..535b100 100644
--- a/java/res/anim/mini_keyboard_fadeout.xml
+++ b/java/res/anim/mini_keyboard_fadeout.xml
@@ -25,5 +25,5 @@
     <alpha
         android:fromAlpha="1.0"
         android:toAlpha="0.0"
-        android:duration="@integer/config_preview_fadeout_anim_time" />
+        android:duration="@integer/config_mini_keyboard_fadeout_anim_time" />
 </set>
diff --git a/java/res/layout-xlarge/candidates.xml b/java/res/layout-xlarge/candidates.xml
index d10035c..096a0ad 100644
--- a/java/res/layout-xlarge/candidates.xml
+++ b/java/res/layout-xlarge/candidates.xml
@@ -21,25 +21,34 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="horizontal"
+    android:gravity="bottom"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@drawable/keyboard_suggest_strip_holo"
-    android:paddingRight="@dimen/candidate_strip_padding"
-    android:paddingLeft="@dimen/candidate_strip_padding"
+    android:minHeight="@dimen/candidate_strip_minimum_height"
 >
-    <HorizontalScrollView
-        android:id="@+id/candidates_scroll_view"
-        android:layout_width="wrap_content"
+    <!-- On tablets, the candidate strip is centered with horizontal paddings on both sides because
+         width of the landscape mode is too long for the candidate strip. This LinearLayout is
+         required to hold the paddings. -->
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length"
-        android:scrollbars="none"
+        android:background="@drawable/keyboard_suggest_strip_holo"
+        android:paddingRight="@dimen/candidate_strip_padding"
+        android:paddingLeft="@dimen/candidate_strip_padding"
     >
-        <com.android.inputmethod.latin.CandidateView
-            android:id="@+id/candidates"
-            android:orientation="horizontal"
+        <HorizontalScrollView
             android:layout_width="match_parent"
-            android:layout_height="@dimen/candidate_strip_height"
-            android:background="@drawable/keyboard_suggest_strip_holo" />
-    </HorizontalScrollView>
+            android:layout_height="wrap_content"
+            android:fadingEdge="horizontal"
+            android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length"
+            android:scrollbars="none"
+        >
+            <com.android.inputmethod.latin.CandidateView
+                android:id="@+id/candidates"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/candidate_strip_height"
+                android:gravity="center_vertical" />
+        </HorizontalScrollView>
+    </LinearLayout>
 </LinearLayout>
diff --git a/java/res/layout/candidates.xml b/java/res/layout/candidates.xml
index 794c4ed..3d91c1d 100644
--- a/java/res/layout/candidates.xml
+++ b/java/res/layout/candidates.xml
@@ -21,25 +21,25 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="horizontal"
+    android:gravity="bottom"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@drawable/keyboard_suggest_strip"
+    android:minHeight="@dimen/candidate_strip_minimum_height"
     android:paddingRight="@dimen/candidate_strip_padding"
     android:paddingLeft="@dimen/candidate_strip_padding"
 >
     <HorizontalScrollView
-        android:id="@+id/candidates_scroll_view"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:background="@drawable/keyboard_suggest_strip"
         android:fadingEdge="horizontal"
         android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length"
         android:scrollbars="none"
     >
         <com.android.inputmethod.latin.CandidateView
             android:id="@+id/candidates"
-            android:orientation="horizontal"
             android:layout_width="match_parent"
             android:layout_height="@dimen/candidate_strip_height"
-            android:background="@drawable/keyboard_suggest_strip" />
+            android:gravity="center_vertical" />
     </HorizontalScrollView>
 </LinearLayout>
diff --git a/java/res/values-xlarge/dimens.xml b/java/res/values-xlarge/dimens.xml
index 0dc5621..9bb50f6 100644
--- a/java/res/values-xlarge/dimens.xml
+++ b/java/res/values-xlarge/dimens.xml
@@ -46,6 +46,9 @@
     <dimen name="key_preview_offset_holo">8.0mm</dimen>
 
     <dimen name="candidate_strip_height">46dip</dimen>
+    <!-- candidate_strip_minimum_height =
+         key_preview_height_holo - key_preview_offset_holo + alpha -->
+    <dimen name="candidate_strip_minimum_height">18mm</dimen>
     <dimen name="candidate_strip_padding">15.0mm</dimen>
     <dimen name="candidate_min_width">0.3in</dimen>
     <dimen name="candidate_padding">12dip</dimen>
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index bdb4409..5aabcb8 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -49,8 +49,6 @@
     <integer name="config_final_fadeout_percentage_of_language_on_spacebar">15</integer>
     <integer name="config_delay_before_preview">0</integer>
     <integer name="config_delay_after_preview">10</integer>
-    <integer name="config_preview_fadein_anim_time">0</integer>
-    <integer name="config_preview_fadeout_anim_time">70</integer>
     <integer name="config_mini_keyboard_fadein_anim_time">0</integer>
     <integer name="config_mini_keyboard_fadeout_anim_time">100</integer>
     <integer name="config_delay_before_key_repeat_start">400</integer>
@@ -65,7 +63,7 @@
     <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
     <string name="config_default_keyboard_theme_id" translatable="false">4</string>
     <string name="config_text_size_of_language_on_spacebar" translatable="false">small</string>
-    <integer name="config_max_popup_keyboard_column">10</integer>
+    <integer name="config_max_popup_keyboard_column">5</integer>
     <!-- Whether or not auto-correction should be enabled by default -->
     <bool name="enable_autocorrect">true</bool>
     <string-array name="auto_correction_threshold_values" translatable="false">
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 767dc4a..0a0b16d 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -52,6 +52,9 @@
     <dimen name="key_preview_offset_holo">0.193in</dimen>
 
     <dimen name="candidate_strip_height">42dip</dimen>
+    <!-- candidate_strip_minimum_height =
+         key_preview_height_holo - key_preview_offset_holo + alpha -->
+    <dimen name="candidate_strip_minimum_height">100sp</dimen>
     <dimen name="candidate_strip_fading_edge_length">63dip</dimen>
     <dimen name="candidate_strip_padding">0dip</dimen>
     <dimen name="candidate_min_width">0.3in</dimen>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 130714f..ef48a4b 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -35,10 +35,6 @@
         <item name="backgroundDimAmount">0.5</item>
         <item name="colorScheme">white</item>
     </style>
-    <style name="KeyPreviewAnimation">
-        <item name="android:windowEnterAnimation">@anim/key_preview_fadein</item>
-        <item name="android:windowExitAnimation">@anim/key_preview_fadeout</item>
-    </style>
     <style name="MiniKeyboardAnimation">
         <item name="android:windowEnterAnimation">@anim/mini_keyboard_fadein</item>
         <item name="android:windowExitAnimation">@anim/mini_keyboard_fadeout</item>
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
index 80586b7..2b7e255 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.compat;
 
+import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
 import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.SubtypeSwitcher;
 import com.android.inputmethod.latin.Utils;
@@ -56,6 +57,14 @@
     private static final InputMethodManagerCompatWrapper sInstance =
             new InputMethodManagerCompatWrapper();
 
+    public static final boolean SUBTYPE_SUPPORTED;
+
+    static {
+        // This static initializer guarantees that METHOD_getShortcutInputMethodsAndSubtypes is
+        // already instantiated.
+        SUBTYPE_SUPPORTED = METHOD_getShortcutInputMethodsAndSubtypes != null;
+    }
+
     // For the compatibility, IMM will create dummy subtypes if subtypes are not found.
     // This is required to be false if the current behavior is broken. For now, it's ok to be true.
     private static final boolean ALLOW_DUMMY_SUBTYPE = true;
@@ -64,7 +73,9 @@
     private static final String KEYBOARD_MODE = "keyboard";
 
     private InputMethodManager mImm;
+    private LanguageSwitcherProxy mLanguageSwitcherProxy;
     private String mLatinImePackageName;
+
     private InputMethodManagerCompatWrapper() {
     }
 
@@ -81,15 +92,30 @@
         if (context instanceof LatinIME) {
             mLatinImePackageName = context.getPackageName();
         }
+        mLanguageSwitcherProxy = LanguageSwitcherProxy.getInstance();
     }
 
     public InputMethodSubtypeCompatWrapper getCurrentInputMethodSubtype() {
+        if (!SUBTYPE_SUPPORTED) {
+            return new InputMethodSubtypeCompatWrapper(
+                    0, 0, mLanguageSwitcherProxy.getInputLocale().toString(), KEYBOARD_MODE, "");
+        }
         Object o = CompatUtils.invoke(mImm, null, METHOD_getCurrentInputMethodSubtype);
         return new InputMethodSubtypeCompatWrapper(o);
     }
 
     public List<InputMethodSubtypeCompatWrapper> getEnabledInputMethodSubtypeList(
             InputMethodInfoCompatWrapper imi, boolean allowsImplicitlySelectedSubtypes) {
+        if (!SUBTYPE_SUPPORTED) {
+            String[] languages = mLanguageSwitcherProxy.getEnabledLanguages(
+                    allowsImplicitlySelectedSubtypes);
+            List<InputMethodSubtypeCompatWrapper> subtypeList =
+                    new ArrayList<InputMethodSubtypeCompatWrapper>();
+            for (String lang: languages) {
+                subtypeList.add(new InputMethodSubtypeCompatWrapper(0, 0, lang, KEYBOARD_MODE, ""));
+            }
+            return subtypeList;
+        }
         Object retval = CompatUtils.invoke(mImm, null, METHOD_getEnabledInputMethodSubtypeList,
                 (imi != null ? imi.getInputMethodInfo() : null), allowsImplicitlySelectedSubtypes);
         if (retval == null || !(retval instanceof List) || ((List<?>)retval).isEmpty()) {
diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
index e02aac7..399dcf2 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
@@ -16,23 +16,13 @@
 
 package com.android.inputmethod.compat;
 
+import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
 import com.android.inputmethod.latin.SubtypeSwitcher;
 
 import android.inputmethodservice.InputMethodService;
-import android.view.View;
 import android.view.inputmethod.InputMethodSubtype;
-import android.widget.HorizontalScrollView;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 
 public class InputMethodServiceCompatWrapper extends InputMethodService {
-    private static final Method METHOD_HorizontalScrollView_setOverScrollMode =
-            CompatUtils.getMethod(HorizontalScrollView.class, "setOverScrollMode", int.class);
-    private static final Field FIELD_View_OVER_SCROLL_NEVER =
-            CompatUtils.getField(View.class, "OVER_SCROLL_NEVER");
-    private static final Integer View_OVER_SCROLL_NEVER =
-            (Integer)CompatUtils.getFieldValue(null, null, FIELD_View_OVER_SCROLL_NEVER);
     // CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED needs to be false if the API level is 10
     // or previous. Note that InputMethodSubtype was added in the API level 11.
     // For the API level 11 or later, LatinIME should override onCurrentInputMethodSubtypeChanged().
@@ -62,17 +52,13 @@
             subtype = mImm.getCurrentInputMethodSubtype();
         }
         if (subtype != null) {
+            if (!InputMethodManagerCompatWrapper.SUBTYPE_SUPPORTED) {
+                LanguageSwitcherProxy.getInstance().setLocale(subtype.getLocale());
+            }
             SubtypeSwitcher.getInstance().updateSubtype(subtype);
         }
     }
 
-    protected static void setOverScrollModeNever(HorizontalScrollView scrollView) {
-        if (View_OVER_SCROLL_NEVER != null) {
-            CompatUtils.invoke(scrollView, null, METHOD_HorizontalScrollView_setOverScrollMode,
-                    View_OVER_SCROLL_NEVER);
-        }
-    }
-
     //////////////////////////////////////
     // Functions using API v11 or later //
     //////////////////////////////////////
diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
index 317b022..86c8af3 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
@@ -58,9 +58,6 @@
     public InputMethodSubtypeCompatWrapper(Object subtype) {
         super((CLASS_InputMethodSubtype != null && CLASS_InputMethodSubtype.isInstance(subtype))
                 ? subtype : null);
-        if (DBG) {
-            Log.d(TAG, "CreateInputMethodSubtypeCompatWrapper");
-        }
         mDummyNameResId = 0;
         mDummyIconResId = 0;
         mDummyLocale = DEFAULT_LOCALE;
diff --git a/java/src/com/android/inputmethod/deprecated/LanguageSwitcherProxy.java b/java/src/com/android/inputmethod/deprecated/LanguageSwitcherProxy.java
new file mode 100644
index 0000000..73f0b7a
--- /dev/null
+++ b/java/src/com/android/inputmethod/deprecated/LanguageSwitcherProxy.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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.deprecated;
+
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.deprecated.languageswitcher.LanguageSwitcher;
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.Settings;
+
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+
+import java.util.Locale;
+
+// This class is used only when the IME doesn't use method.xml for language switching.
+public class LanguageSwitcherProxy implements SharedPreferences.OnSharedPreferenceChangeListener {
+    private static final LanguageSwitcherProxy sInstance = new LanguageSwitcherProxy();
+    private LanguageSwitcher mLanguageSwitcher;
+    private SharedPreferences mPrefs;
+
+    public static LanguageSwitcherProxy getInstance() {
+        if (InputMethodManagerCompatWrapper.SUBTYPE_SUPPORTED) return null;
+        return sInstance;
+    }
+
+    public static void init(LatinIME service, SharedPreferences prefs) {
+        if (InputMethodManagerCompatWrapper.SUBTYPE_SUPPORTED) return;
+        final Configuration conf = service.getResources().getConfiguration();
+        sInstance.mLanguageSwitcher = new LanguageSwitcher(service);
+        sInstance.mLanguageSwitcher.loadLocales(prefs, conf.locale);
+        sInstance.mPrefs = prefs;
+        prefs.registerOnSharedPreferenceChangeListener(sInstance);
+    }
+
+    public static void onConfigurationChanged(Configuration conf) {
+        if (InputMethodManagerCompatWrapper.SUBTYPE_SUPPORTED) return;
+        sInstance.mLanguageSwitcher.onConfigurationChanged(conf, sInstance.mPrefs);
+    }
+
+    public static void loadSettings() {
+        if (InputMethodManagerCompatWrapper.SUBTYPE_SUPPORTED) return;
+        sInstance.mLanguageSwitcher.loadLocales(sInstance.mPrefs, null);
+    }
+
+    public int getLocaleCount() {
+        return mLanguageSwitcher.getLocaleCount();
+    }
+
+    public String[] getEnabledLanguages(boolean allowImplicitlySelectedLanguages) {
+        return mLanguageSwitcher.getEnabledLanguages(allowImplicitlySelectedLanguages);
+    }
+
+    public Locale getInputLocale() {
+        return mLanguageSwitcher.getInputLocale();
+    }
+
+    public void setLocale(String localeStr) {
+        mLanguageSwitcher.setLocale(localeStr);
+        mLanguageSwitcher.persist(mPrefs);
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+        // PREF_SELECTED_LANGUAGES: enabled input subtypes
+        // PREF_INPUT_LANGUAGE: current input subtype
+        if (key.equals(Settings.PREF_SELECTED_LANGUAGES)
+                || key.equals(Settings.PREF_INPUT_LANGUAGE)) {
+            mLanguageSwitcher.loadLocales(prefs, null);
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LanguageSwitcher.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java
similarity index 71%
rename from java/src/com/android/inputmethod/latin/LanguageSwitcher.java
rename to java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java
index 6faf7f9..e4b2e03 100644
--- a/java/src/com/android/inputmethod/latin/LanguageSwitcher.java
+++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java
@@ -14,11 +14,19 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.deprecated.languageswitcher;
+
+import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.Settings;
+import com.android.inputmethod.latin.SharedPreferencesCompat;
 
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.Editor;
+import android.content.res.Configuration;
 import android.text.TextUtils;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Locale;
@@ -28,10 +36,14 @@
  * input language that the user has selected.
  */
 public class LanguageSwitcher {
+    private static final String TAG = LanguageSwitcher.class.getSimpleName();
+
+    private static final String KEYBOARD_MODE = "keyboard";
+    private static final String[] EMPTY_STIRNG_ARRAY = new String[0];
 
     private final ArrayList<Locale> mLocales = new ArrayList<Locale>();
     private final LatinIME mIme;
-    private String[] mSelectedLanguageArray;
+    private String[] mSelectedLanguageArray = EMPTY_STIRNG_ARRAY;
     private String   mSelectedLanguages;
     private int      mCurrentIndex = 0;
     private String   mDefaultInputLanguage;
@@ -46,15 +58,31 @@
         return mLocales.size();
     }
 
+    public void onConfigurationChanged(Configuration conf, SharedPreferences prefs) {
+        final Locale newLocale = conf.locale;
+        if (!getSystemLocale().toString().equals(newLocale.toString())) {
+            loadLocales(prefs, newLocale);
+        }
+    }
+
     /**
      * Loads the currently selected input languages from shared preferences.
-     * @param sp
+     * @param sp shared preference for getting the current input language and enabled languages
+     * @param systemLocale the current system locale, stored for changing the current input language
+     * based on the system current system locale.
      * @return whether there was any change
      */
-    public boolean loadLocales(SharedPreferences sp) {
+    public boolean loadLocales(SharedPreferences sp, Locale systemLocale) {
+        if (LatinImeLogger.sDBG) {
+            Log.d(TAG, "load locales");
+        }
+        if (systemLocale != null) {
+            setSystemLocale(systemLocale);
+        }
         String selectedLanguages = sp.getString(Settings.PREF_SELECTED_LANGUAGES, null);
         String currentLanguage   = sp.getString(Settings.PREF_INPUT_LANGUAGE, null);
-        if (selectedLanguages == null || selectedLanguages.length() < 1) {
+        if (TextUtils.isEmpty(selectedLanguages)) {
+            mSelectedLanguageArray = EMPTY_STIRNG_ARRAY;
             loadDefaults();
             if (mLocales.size() == 0) {
                 return false;
@@ -84,6 +112,9 @@
     }
 
     private void loadDefaults() {
+        if (LatinImeLogger.sDBG) {
+            Log.d(TAG, "load default locales:");
+        }
         mDefaultInputLocale = mIme.getResources().getConfiguration().locale;
         String country = mDefaultInputLocale.getCountry();
         mDefaultInputLanguage = mDefaultInputLocale.getLanguage() +
@@ -112,7 +143,10 @@
     /**
      * Returns the list of enabled language codes.
      */
-    public String[] getEnabledLanguages() {
+    public String[] getEnabledLanguages(boolean allowImplicitlySelectedLanguages) {
+        if (mSelectedLanguageArray.length == 0 && allowImplicitlySelectedLanguages) {
+            return new String[] { mDefaultInputLanguage };
+        }
         return mSelectedLanguageArray;
     }
 
@@ -151,7 +185,7 @@
      * Sets the system locale (display UI) used for comparing with the input language.
      * @param locale the locale of the system
      */
-    public void setSystemLocale(Locale locale) {
+    private void setSystemLocale(Locale locale) {
         mSystemLocale = locale;
     }
 
@@ -159,7 +193,7 @@
      * Returns the system locale.
      * @return the system locale
      */
-    public Locale getSystemLocale() {
+    private Locale getSystemLocale() {
         return mSystemLocale;
     }
 
@@ -185,6 +219,15 @@
         mCurrentIndex = prevLocaleIndex();
     }
 
+    public void setLocale(String localeStr) {
+        final int N = mLocales.size();
+        for (int i = 0; i < N; ++i) {
+            if (mLocales.get(i).toString().equals(localeStr)) {
+                mCurrentIndex = i;
+            }
+        }
+    }
+
     public void persist(SharedPreferences prefs) {
         Editor editor = prefs.edit();
         editor.putString(Settings.PREF_INPUT_LANGUAGE, getInputLanguage());
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 1a4f901..bf0fb9a 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -17,7 +17,6 @@
 package com.android.inputmethod.keyboard;
 
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 
 public abstract class KeyDetector {
@@ -110,31 +109,4 @@
      * @return The nearest key index
      */
     abstract public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes);
-
-    /**
-     * Compute the most common key width in order to use it as proximity key detection threshold.
-     *
-     * @param keyboard The keyboard to compute the most common key width
-     * @return The most common key width in the keyboard
-     */
-    public static int getMostCommonKeyWidth(final Keyboard keyboard) {
-        if (keyboard == null) return 0;
-        final List<Key> keys = keyboard.getKeys();
-        if (keys == null || keys.size() == 0) return 0;
-        final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
-        int maxCount = 0;
-        int mostCommonWidth = 0;
-        for (final Key key : keys) {
-            final Integer width = key.mWidth + key.mGap;
-            Integer count = histogram.get(width);
-            if (count == null)
-                count = 0;
-            histogram.put(width, ++count);
-            if (count > maxCount) {
-                maxCount = count;
-                mostCommonWidth = width;
-            }
-        }
-        return mostCommonWidth;
-    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index f720334..9b8d75e 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -405,6 +405,29 @@
         return EMPTY_INT_ARRAY;
     }
 
+    /**
+     * Compute the most common key width in order to use it as proximity key detection threshold.
+     *
+     * @return The most common key width in the keyboard
+     */
+    public int getMostCommonKeyWidth() {
+        final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
+        int maxCount = 0;
+        int mostCommonWidth = 0;
+        for (final Key key : mKeys) {
+            final Integer width = key.mWidth + key.mGap;
+            Integer count = histogram.get(width);
+            if (count == null)
+                count = 0;
+            histogram.put(width, ++count);
+            if (count > maxCount) {
+                maxCount = count;
+                mostCommonWidth = width;
+            }
+        }
+        return mostCommonWidth;
+    }
+
     private void loadKeyboard(Context context, int xmlLayoutResId) {
         try {
             KeyboardParser parser = new KeyboardParser(this, context.getResources());
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 7b570d7..1e19a8a 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -45,8 +45,9 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 import android.widget.PopupWindow;
 import android.widget.TextView;
 
@@ -110,14 +111,12 @@
     // Key preview popup
     private boolean mInForeground;
     private TextView mPreviewText;
-    private PopupWindow mPreviewPopup;
     private int mPreviewTextSizeLarge;
-    private int[] mOffsetInWindow;
+    private final int[] mOffsetInWindow = new int[2];
     private int mOldPreviewKeyIndex = KeyDetector.NOT_A_KEY;
     private boolean mShowPreview = true;
     private int mPopupPreviewOffsetX;
     private int mPopupPreviewOffsetY;
-    private int mWindowY;
     private int mPopupPreviewDisplayedY;
     private final int mDelayBeforePreview;
     private final int mDelayAfterPreview;
@@ -125,7 +124,6 @@
     // Popup mini keyboard
     private PopupWindow mMiniKeyboardPopup;
     private KeyboardView mMiniKeyboardView;
-    private View mMiniKeyboardParent;
     private final WeakHashMap<Key, View> mMiniKeyboardCache = new WeakHashMap<Key, View>();
     private int mMiniKeyboardOriginX;
     private int mMiniKeyboardOriginY;
@@ -204,7 +202,7 @@
                     showKey(msg.arg1, (PointerTracker)msg.obj);
                     break;
                 case MSG_DISMISS_PREVIEW:
-                    mPreviewPopup.dismiss();
+                    mPreviewText.setVisibility(View.INVISIBLE);
                     break;
                 case MSG_REPEAT_KEY: {
                     final PointerTracker tracker = (PointerTracker)msg.obj;
@@ -227,12 +225,11 @@
 
         public void popupPreview(long delay, int keyIndex, PointerTracker tracker) {
             removeMessages(MSG_POPUP_PREVIEW);
-            if (mPreviewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) {
+            if (mPreviewText.getVisibility() == VISIBLE) {
                 // Show right away, if it's already visible and finger is moving around
                 showKey(keyIndex, tracker);
             } else {
-                sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0, tracker),
-                        delay);
+                sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0, tracker), delay);
             }
         }
 
@@ -241,9 +238,7 @@
         }
 
         public void dismissPreview(long delay) {
-            if (mPreviewPopup.isShowing()) {
-                sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay);
-            }
+            sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay);
         }
 
         public void cancelDismissPreview() {
@@ -366,24 +361,17 @@
 
         final Resources res = getResources();
 
-        mPreviewPopup = new PopupWindow(context);
         if (previewLayout != 0) {
             mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null);
             mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large);
-            mPreviewPopup.setContentView(mPreviewText);
-            mPreviewPopup.setBackgroundDrawable(null);
         } else {
             mShowPreview = false;
         }
-        mPreviewPopup.setTouchable(false);
-        mPreviewPopup.setAnimationStyle(R.style.KeyPreviewAnimation);
-        mPreviewPopup.setClippingEnabled(false);
         mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview);
         mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview);
         mKeyLabelHorizontalPadding = (int)res.getDimension(
                 R.dimen.key_label_horizontal_alignment_padding);
 
-        mMiniKeyboardParent = this;
         mMiniKeyboardPopup = new PopupWindow(context);
         mMiniKeyboardPopup.setBackgroundDrawable(null);
         mMiniKeyboardPopup.setAnimationStyle(R.style.MiniKeyboardAnimation);
@@ -513,7 +501,7 @@
         requestLayout();
         mKeyboardChanged = true;
         invalidateAllKeys();
-        mKeyDetector.setProximityThreshold(KeyDetector.getMostCommonKeyWidth(keyboard));
+        mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth());
         mMiniKeyboardCache.clear();
     }
 
@@ -583,7 +571,6 @@
     public void setPopupOffset(int x, int y) {
         mPopupPreviewOffsetX = x;
         mPopupPreviewOffsetY = y;
-        mPreviewPopup.dismiss();
     }
 
     /**
@@ -915,9 +902,25 @@
         }
     }
 
-    // TODO Must fix popup preview on xlarge layout
+    // TODO: Introduce minimum duration for displaying key previews
+    // TODO: Display up to two key previews when the user presses two keys at the same time
     private void showKey(final int keyIndex, PointerTracker tracker) {
-        Key key = tracker.getKey(keyIndex);
+        // If the preview popup has no parent view yet, add it to the screen FrameLayout.
+        if (mPreviewText.getParent() == null) {
+            final FrameLayout screenContent = (FrameLayout) getRootView()
+                    .findViewById(android.R.id.content);
+            if (android.os.Build.VERSION.SDK_INT >= /* HONEYCOMB */ 11) {
+                screenContent.addView(mPreviewText, new FrameLayout.LayoutParams(0, 0));
+            } else {
+                // Insert LinearLayout to be able to setMargin because pre-Honeycomb FrameLayout
+                // could not handle setMargin properly.
+                final LinearLayout placer = new LinearLayout(getContext());
+                screenContent.addView(placer);
+                placer.addView(mPreviewText, new LinearLayout.LayoutParams(0, 0));
+            }
+        }
+
+        final Key key = tracker.getKey(keyIndex);
         // If keyIndex is invalid or IME is already closed, we must not show key preview.
         // Trying to show preview PopupWindow while root window is closed causes
         // WindowManager.BadTokenException.
@@ -948,57 +951,31 @@
         int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), keyDrawWidth
                 + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
         final int popupHeight = mPreviewHeight;
-        LayoutParams lp = mPreviewText.getLayoutParams();
-        if (lp != null) {
-            lp.width = popupWidth;
-            lp.height = popupHeight;
-        }
+        final ViewGroup.LayoutParams lp = mPreviewText.getLayoutParams();
+        lp.width = popupWidth;
+        lp.height = popupHeight;
 
         int popupPreviewX = keyDrawX - (popupWidth - keyDrawWidth) / 2;
         int popupPreviewY = key.mY - popupHeight + mPreviewOffset;
 
         mHandler.cancelDismissPreview();
-        if (mOffsetInWindow == null) {
-            mOffsetInWindow = new int[2];
-            getLocationInWindow(mOffsetInWindow);
-            mOffsetInWindow[0] += mPopupPreviewOffsetX; // Offset may be zero
-            mOffsetInWindow[1] += mPopupPreviewOffsetY; // Offset may be zero
-            int[] windowLocation = new int[2];
-            getLocationOnScreen(windowLocation);
-            mWindowY = windowLocation[1];
-        }
+        getLocationInWindow(mOffsetInWindow);
+        mOffsetInWindow[0] += mPopupPreviewOffsetX; // Offset may be zero
+        mOffsetInWindow[1] += mPopupPreviewOffsetY; // Offset may be zero
+
         // Set the preview background state
         mPreviewText.getBackground().setState(
                 key.mPopupCharacters != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
         popupPreviewX += mOffsetInWindow[0];
         popupPreviewY += mOffsetInWindow[1];
 
-        // If the popup cannot be shown above the key, put it on the side
-        if (popupPreviewY + mWindowY < 0) {
-            // If the key you're pressing is on the left side of the keyboard, show the popup on
-            // the right, offset by enough to see at least one key to the left/right.
-            if (keyDrawX + keyDrawWidth <= getWidth() / 2) {
-                popupPreviewX += (int) (keyDrawWidth * 2.5);
-            } else {
-                popupPreviewX -= (int) (keyDrawWidth * 2.5);
-            }
-            popupPreviewY += popupHeight;
+        // Place the key preview.
+        // TODO: Adjust position of key previews which touch screen edges
+        if (lp instanceof ViewGroup.MarginLayoutParams) {
+            ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp;
+            mlp.setMargins(popupPreviewX, popupPreviewY, 0, 0);
         }
-
-        try {
-            if (mPreviewPopup.isShowing()) {
-                mPreviewPopup.update(popupPreviewX, popupPreviewY, popupWidth, popupHeight);
-            } else {
-                mPreviewPopup.setWidth(popupWidth);
-                mPreviewPopup.setHeight(popupHeight);
-                mPreviewPopup.showAtLocation(mMiniKeyboardParent, Gravity.NO_GRAVITY,
-                        popupPreviewX, popupPreviewY);
-            }
-        } catch (WindowManager.BadTokenException e) {
-            // Swallow the exception which will be happened when IME is already closed.
-            Log.w(TAG, "LatinIME is already closed when tried showing key preview.");
-        }
-        // Record popup preview position to display mini-keyboard later at the same positon
+        // Record popup preview position to display mini-keyboard later at the same position
         mPopupPreviewDisplayedY = popupPreviewY;
         mPreviewText.setVisibility(VISIBLE);
     }
@@ -1114,7 +1091,6 @@
         final Keyboard keyboard = new MiniKeyboardBuilder(this, mKeyboard.getPopupKeyboardResId(),
                 popupKey).build();
         miniKeyboardView.setKeyboard(keyboard);
-        miniKeyboardView.mMiniKeyboardParent = this;
 
         container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
                 MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
@@ -1349,7 +1325,7 @@
     }
 
     public void closing() {
-        mPreviewPopup.dismiss();
+        mPreviewText.setVisibility(View.GONE);
         mHandler.cancelAllMessages();
 
         dismissPopupKeyboard();
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index 9b87df3..9d9793e 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -271,7 +271,7 @@
             canvas.drawText(language, width / 2, baseline - descent, paint);
 
             // Put arrows that are already layed out on either side of the text
-            if (SubtypeSwitcher.getInstance().useSpacebarLanguageSwitcher()
+            if (subtypeSwitcher.useSpacebarLanguageSwitcher()
                     && subtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
                 mButtonArrowLeftIcon.draw(canvas);
                 mButtonArrowRightIcon.draw(canvas);
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java
index 53dab94..e540fa1 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java
@@ -34,7 +34,7 @@
     /* package */ static class MiniKeyboardLayoutParams {
         public final int mKeyWidth;
         public final int mRowHeight;
-        /* package */ final boolean mTopRowNeedsCentering;
+        /* package */ final int mTopRowAdjustment;
         public final int mNumRows;
         public final int mNumColumns;
         public final int mLeftKeys;
@@ -55,29 +55,52 @@
             if (parentKeyboardWidth / keyWidth < maxColumns)
                 throw new IllegalArgumentException("Keyboard is too small to hold mini keyboard: "
                         + parentKeyboardWidth + " " + keyWidth + " " + maxColumns);
-            final int numRows = (numKeys + maxColumns - 1) / maxColumns;
             mKeyWidth = keyWidth;
             mRowHeight = rowHeight;
-            mNumRows = numRows;
 
-            final int numColumns = Math.min(numKeys, maxColumns);
-            final int topRowKeys = numKeys % numColumns;
+            final int numRows = (numKeys + maxColumns - 1) / maxColumns;
+            mNumRows = numRows;
+            final int numColumns = getOptimizedColumns(numKeys, maxColumns);
             mNumColumns = numColumns;
-            mTopRowNeedsCentering = topRowKeys != 0 && (numColumns - topRowKeys) % 2 != 0;
 
             final int numLeftKeys = (numColumns - 1) / 2;
             final int numRightKeys = numColumns - numLeftKeys; // including default key.
             final int maxLeftKeys = coordXInParent / keyWidth;
             final int maxRightKeys = Math.max(1, (parentKeyboardWidth - coordXInParent) / keyWidth);
+            int leftKeys, rightKeys;
             if (numLeftKeys > maxLeftKeys) {
-                mLeftKeys = maxLeftKeys;
-                mRightKeys = numColumns - maxLeftKeys;
+                leftKeys = maxLeftKeys;
+                rightKeys = numColumns - maxLeftKeys;
             } else if (numRightKeys > maxRightKeys) {
-                mLeftKeys = numColumns - maxRightKeys;
-                mRightKeys = maxRightKeys;
+                leftKeys = numColumns - maxRightKeys;
+                rightKeys = maxRightKeys;
             } else {
-                mLeftKeys = numLeftKeys;
-                mRightKeys = numRightKeys;
+                leftKeys = numLeftKeys;
+                rightKeys = numRightKeys;
+            }
+            // Shift right if the left edge of mini keyboard is on the edge of parent keyboard
+            // unless the parent key is on the left edge.
+            if (leftKeys * keyWidth >= coordXInParent && leftKeys > 0) {
+                leftKeys--;
+                rightKeys++;
+            }
+            // Shift left if the right edge of mini keyboard is on the edge of parent keyboard
+            // unless the parent key is on the right edge.
+            if (rightKeys * keyWidth + coordXInParent >= parentKeyboardWidth && rightKeys > 1) {
+                leftKeys++;
+                rightKeys--;
+            }
+            mLeftKeys = leftKeys;
+            mRightKeys = rightKeys;
+
+            // Centering of the top row.
+            final boolean onEdge = (leftKeys == 0 || rightKeys == 1);
+            if (numRows < 2 || onEdge || getTopRowEmptySlots(numKeys, numColumns) % 2 == 0) {
+                mTopRowAdjustment = 0;
+            } else if (mLeftKeys < mRightKeys - 1) {
+                mTopRowAdjustment = 1;
+            } else {
+                mTopRowAdjustment = -1;
             }
         }
 
@@ -113,14 +136,32 @@
             return pos;
         }
 
+        private static int getTopRowEmptySlots(int numKeys, int numColumns) {
+            final int remainingKeys = numKeys % numColumns;
+            if (remainingKeys == 0) {
+                return 0;
+            } else {
+                return numColumns - remainingKeys;
+            }
+        }
+
+        private int getOptimizedColumns(int numKeys, int maxColumns) {
+            int numColumns = Math.min(numKeys, maxColumns);
+            while (getTopRowEmptySlots(numKeys, numColumns) >= mNumRows) {
+                numColumns--;
+            }
+            return numColumns;
+        }
+
         public int getDefaultKeyCoordX() {
             return mLeftKeys * mKeyWidth;
         }
 
         public int getX(int n, int row) {
             final int x = getColumnPos(n) * mKeyWidth + getDefaultKeyCoordX();
-            if (isLastRow(row) && mTopRowNeedsCentering)
-                return x - mKeyWidth / 2;
+            if (isTopRow(row)) {
+                return x + mTopRowAdjustment * (mKeyWidth / 2);
+            }
             return x;
         }
 
@@ -131,27 +172,27 @@
         public int getRowFlags(int row) {
             int rowFlags = 0;
             if (row == 0) rowFlags |= Keyboard.EDGE_TOP;
-            if (isLastRow(row)) rowFlags |= Keyboard.EDGE_BOTTOM;
+            if (isTopRow(row)) rowFlags |= Keyboard.EDGE_BOTTOM;
             return rowFlags;
         }
 
-        private boolean isLastRow(int rowCount) {
+        private boolean isTopRow(int rowCount) {
             return rowCount == mNumRows - 1;
         }
     }
 
-    public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key popupKey) {
+    public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key parentKey) {
         final Context context = view.getContext();
         mRes = context.getResources();
         final MiniKeyboard keyboard = new MiniKeyboard(context, layoutTemplateResId, null);
         mKeyboard = keyboard;
-        mPopupCharacters = popupKey.mPopupCharacters;
+        mPopupCharacters = parentKey.mPopupCharacters;
 
         final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, keyboard.getKeyWidth());
         final MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                mPopupCharacters.length, popupKey.mMaxPopupColumn,
+                mPopupCharacters.length, parentKey.mMaxPopupColumn,
                 keyWidth, keyboard.getRowHeight(),
-                popupKey.mX + (popupKey.mWidth + popupKey.mGap) / 2 - keyWidth / 2,
+                parentKey.mX + (parentKey.mWidth + parentKey.mGap) / 2 - keyWidth / 2,
                 view.getMeasuredWidth());
         mParams = params;
 
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index eb5335f..add38cf 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -549,7 +549,6 @@
         final Key key = getKey(keyIndex);
         if (key != null && !key.mEnabled)
             return;
-        updateKeyGraphics(keyIndex);
         // The modifier key, such as shift key, should not be shown as preview when multi-touch is
         // supported. On the other hand, if multi-touch is not supported, the modifier key should
         // be shown as preview. If accessibility is turned on, the modifier key should be shown as
@@ -559,6 +558,7 @@
         } else {
             mProxy.showPreview(keyIndex, this);
         }
+        updateKeyGraphics(keyIndex);
     }
 
     private void startLongPressTimer(int keyIndex) {
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index 5719b90..6fb80ad 100644
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -133,7 +133,6 @@
                 ViewGroup.LayoutParams.WRAP_CONTENT);
         mPreviewPopup.setContentView(mPreviewText);
         mPreviewPopup.setBackgroundDrawable(null);
-        mPreviewPopup.setAnimationStyle(R.style.KeyPreviewAnimation);
         mConfigCandidateHighlightFontColorEnabled =
                 res.getBoolean(R.bool.config_candidate_highlight_font_color_enabled);
         mColorNormal = res.getColor(R.color.candidate_normal);
diff --git a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
index c786462..9d30af8 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
@@ -30,6 +30,11 @@
 public class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver {
 
     final LatinIME mService;
+    /**
+     * The action of the intent for publishing that new dictionary data is available.
+     */
+    /* package */ static final String NEW_DICTIONARY_INTENT_ACTION =
+            "com.android.inputmethod.latin.dictionarypack.newdict";
 
     public DictionaryPackInstallBroadcastReceiver(final LatinIME service) {
         mService = service;
@@ -77,6 +82,8 @@
             // TODO: Only reload dictionary on REMOVED when the removed package is the one we
             // read dictionary from?
             mService.resetSuggestMainDict();
+        } else if (action.equals(NEW_DICTIONARY_INTENT_ACTION)) {
+            mService.resetSuggestMainDict();
         }
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 6a858fe..88b3ded 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -23,6 +23,7 @@
 import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
 import com.android.inputmethod.compat.InputTypeCompatUtils;
 import com.android.inputmethod.compat.VibratorCompatWrapper;
+import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
 import com.android.inputmethod.deprecated.VoiceProxy;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardActionListener;
@@ -56,7 +57,6 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
-import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -70,8 +70,6 @@
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
-import android.widget.FrameLayout;
-import android.widget.HorizontalScrollView;
 import android.widget.LinearLayout;
 
 import java.io.FileDescriptor;
@@ -143,6 +141,7 @@
     };
 
     private View mCandidateViewContainer;
+    private int mCandidateStripHeight;
     private CandidateView mCandidateView;
     private Suggest mSuggest;
     private CompletionInfo[] mApplicationSpecifiedCompletions;
@@ -376,6 +375,7 @@
         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
         mPrefs = prefs;
         LatinImeLogger.init(this, prefs);
+        LanguageSwitcherProxy.init(this, prefs);
         SubtypeSwitcher.init(this, prefs);
         KeyboardSwitcher.init(this, prefs);
         AccessibilityUtils.init(this, prefs);
@@ -440,6 +440,11 @@
         packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         packageFilter.addDataScheme(SCHEME_PACKAGE);
         registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
+
+        final IntentFilter newDictFilter = new IntentFilter();
+        newDictFilter.addAction(
+                DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION);
+        registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
     }
 
     private void initSuggest() {
@@ -516,6 +521,9 @@
         super.onConfigurationChanged(conf);
         mVoiceProxy.onConfigurationChanged(conf);
         mConfigurationChanging = false;
+
+        // This will work only when the subtype is not supported.
+        LanguageSwitcherProxy.onConfigurationChanged(conf);
     }
 
     @Override
@@ -528,12 +536,7 @@
         LayoutInflater inflater = getLayoutInflater();
         LinearLayout container = (LinearLayout)inflater.inflate(R.layout.candidates, null);
         mCandidateViewContainer = container;
-        if (container.getPaddingRight() != 0) {
-            HorizontalScrollView scrollView =
-                    (HorizontalScrollView) container.findViewById(R.id.candidates_scroll_view);
-            setOverScrollModeNever(scrollView);
-            container.setGravity(Gravity.CENTER_HORIZONTAL);
-        }
+        mCandidateStripHeight = (int)mResources.getDimension(R.dimen.candidate_strip_height);
         mCandidateView = (CandidateView) container.findViewById(R.id.candidates);
         mCandidateView.setService(this);
         setCandidatesViewShown(true);
@@ -581,8 +584,7 @@
             switcher.updateShiftState();
         }
 
-        setCandidatesViewShownInternal(isCandidateStripVisible(),
-                false /* needsInputViewShown */ );
+        setCandidatesViewShownInternal(isCandidateStripVisible(), false /* needsInputViewShown */ );
         // Delay updating suggestions because keyboard input view may not be shown at this point.
         mHandler.postUpdateSuggestions();
 
@@ -872,10 +874,21 @@
     }
 
     private void setCandidatesViewShownInternal(boolean shown, boolean needsInputViewShown) {
-        // TODO: Remove this if we support candidates with hard keyboard
+        // TODO: Modify this if we support candidates with hard keyboard
         if (onEvaluateInputViewShown()) {
-            super.setCandidatesViewShown(shown
-                    && (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true));
+            final boolean shouldShowCandidates = shown
+                    && (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true);
+            if (isExtractViewShown()) {
+                // No need to have extra space to show the key preview.
+                mCandidateViewContainer.setMinimumHeight(0);
+                super.setCandidatesViewShown(shown);
+            } else {
+                // We must control the visibility of the suggestion strip in order to avoid clipped
+                // key previews, even when we don't show the suggestion strip.
+                mCandidateViewContainer.setVisibility(
+                        shouldShowCandidates ? View.VISIBLE : View.INVISIBLE);
+                super.setCandidatesViewShown(true);
+            }
         }
     }
 
@@ -887,35 +900,25 @@
     @Override
     public void onComputeInsets(InputMethodService.Insets outInsets) {
         super.onComputeInsets(outInsets);
-        if (!isFullscreenMode()) {
-            outInsets.contentTopInsets = outInsets.visibleTopInsets;
-        }
-        KeyboardView inputView = mKeyboardSwitcher.getInputView();
+        final KeyboardView inputView = mKeyboardSwitcher.getInputView();
         // Need to set touchable region only if input view is being shown
         if (inputView != null && mKeyboardSwitcher.isInputViewShown()) {
-            final int x = 0;
-            int y = 0;
-            final int width = inputView.getWidth();
-            int height = inputView.getHeight() + EXTENDED_TOUCHABLE_REGION_HEIGHT;
-            if (mCandidateViewContainer != null) {
-                ViewParent candidateParent = mCandidateViewContainer.getParent();
-                if (candidateParent instanceof FrameLayout) {
-                    FrameLayout fl = (FrameLayout) candidateParent;
-                    if (fl != null) {
-                        // Check frame layout's visibility
-                        if (fl.getVisibility() == View.INVISIBLE) {
-                            y = fl.getHeight();
-                            height += y;
-                        } else if (fl.getVisibility() == View.VISIBLE) {
-                            height += fl.getHeight();
-                        }
-                    }
-                }
+            final int containerHeight = mCandidateViewContainer.getHeight();
+            int touchY = containerHeight;
+            if (mCandidateViewContainer.getVisibility() == View.VISIBLE) {
+                touchY -= mCandidateStripHeight;
             }
+            outInsets.contentTopInsets = touchY;
+            outInsets.visibleTopInsets = touchY;
+            final int touchWidth = inputView.getWidth();
+            final int touchHeight = inputView.getHeight() + containerHeight
+                    // Extend touchable region below the keyboard.
+                    + EXTENDED_TOUCHABLE_REGION_HEIGHT;
             if (DEBUG) {
-                Log.d(TAG, "Touchable region " + x + ", " + y + ", " + width + ", " + height);
+                Log.d(TAG, "Touchable region: y=" + touchY + " width=" + touchWidth
+                        + " height=" + touchHeight);
             }
-            setTouchableRegionCompat(outInsets, x, y, width, height);
+            setTouchableRegionCompat(outInsets, 0, touchY, touchWidth, touchHeight);
         }
     }
 
@@ -1155,10 +1158,10 @@
             onSettingsKeyLongPressed();
             break;
         case Keyboard.CODE_NEXT_LANGUAGE:
-            toggleLanguage(false, true);
+            toggleLanguage(true);
             break;
         case Keyboard.CODE_PREV_LANGUAGE:
-            toggleLanguage(false, false);
+            toggleLanguage(false);
             break;
         case Keyboard.CODE_CAPSLOCK:
             switcher.toggleCapsLock();
@@ -1930,17 +1933,9 @@
         return mWord.isFirstCharCapitalized();
     }
 
-    // Notify that language or mode have been changed and toggleLanguage will update KeyboaredID
+    // Notify that language or mode have been changed and toggleLanguage will update KeyboardID
     // according to new language or mode.
     public void onRefreshKeyboard() {
-        toggleLanguage(true, true);
-    }
-
-    // "reset" and "next" are used only for USE_SPACEBAR_LANGUAGE_SWITCHER.
-    private void toggleLanguage(boolean reset, boolean next) {
-        if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()) {
-            mSubtypeSwitcher.toggleLanguage(reset, next);
-        }
         // Reload keyboard because the current language has been changed.
         mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(),
                 mSubtypeSwitcher.isShortcutImeEnabled() && mVoiceProxy.isVoiceButtonEnabled(),
@@ -1949,6 +1944,14 @@
         mKeyboardSwitcher.updateShiftState();
     }
 
+    // "reset" and "next" are used only for USE_SPACEBAR_LANGUAGE_SWITCHER.
+    private void toggleLanguage(boolean next) {
+        if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()) {
+            mSubtypeSwitcher.toggleLanguage(next);
+        }
+        onRefreshKeyboard();// no need??
+    }
+
     @Override
     public void onSwipeDown() {
         if (mConfigSwipeDownDismissKeyboardEnabled)
@@ -2134,7 +2137,9 @@
         updateCorrectionMode();
         updateAutoTextEnabled();
         updateSuggestionVisibility(prefs);
-        SubtypeSwitcher.getInstance().loadSettings();
+
+        // This will work only when the subtype is not supported.
+        LanguageSwitcherProxy.loadSettings();
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 053e2ab..2cdc4d2 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -57,7 +57,6 @@
 
     private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
     private /* final */ LatinIME mService;
-    private /* final */ SharedPreferences mPrefs;
     private /* final */ InputMethodManagerCompatWrapper mImm;
     private /* final */ Resources mResources;
     private /* final */ ConnectivityManager mConnectivityManager;
@@ -66,6 +65,7 @@
             mEnabledKeyboardSubtypesOfCurrentInputMethod =
                     new ArrayList<InputMethodSubtypeCompatWrapper>();
     private final ArrayList<String> mEnabledLanguagesOfCurrentInputMethod = new ArrayList<String>();
+    private final LanguageBarInfo mLanguageBarInfo = new LanguageBarInfo();
 
     /*-----------------------------------------------------------*/
     // Variants which should be changed only by reload functions.
@@ -78,6 +78,7 @@
     private Locale mSystemLocale;
     private Locale mInputLocale;
     private String mInputLocaleStr;
+    private String mInputMethodId;
     private VoiceProxy.VoiceInputWrapper mVoiceInputWrapper;
     /*-----------------------------------------------------------*/
 
@@ -100,7 +101,6 @@
 
     private void initialize(LatinIME service, SharedPreferences prefs) {
         mService = service;
-        mPrefs = prefs;
         mResources = service.getResources();
         mImm = InputMethodManagerCompatWrapper.getInstance(service);
         mConnectivityManager = (ConnectivityManager) service.getSystemService(
@@ -114,13 +114,12 @@
         mAllEnabledSubtypesOfCurrentInputMethod = null;
         // TODO: Voice input should be created here
         mVoiceInputWrapper = null;
-        mConfigUseSpacebarLanguageSwitcher = mResources.getBoolean(
+        mConfigUseSpacebarLanguageSwitcher = service.getResources().getBoolean(
                 R.bool.config_use_spacebar_language_switcher);
-        if (mConfigUseSpacebarLanguageSwitcher)
-            initLanguageSwitcher(service);
 
         final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
         mIsNetworkConnected = (info != null && info.isConnected());
+        mInputMethodId = Utils.getInputMethodId(mImm, service.getPackageName());
     }
 
     // Update all parameters stored in SubtypeSwitcher.
@@ -134,11 +133,7 @@
     // Update parameters which are changed outside LatinIME. This parameters affect UI so they
     // should be updated every time onStartInputview.
     public void updateParametersOnStartInputView() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            updateForSpacebarLanguageSwitch();
-        } else {
-            updateEnabledSubtypes();
-        }
+        updateEnabledSubtypes();
         updateShortcutIME();
     }
 
@@ -150,7 +145,7 @@
                 null, true);
         mEnabledLanguagesOfCurrentInputMethod.clear();
         mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
-        for (InputMethodSubtypeCompatWrapper ims: mAllEnabledSubtypesOfCurrentInputMethod) {
+        for (InputMethodSubtypeCompatWrapper ims : mAllEnabledSubtypesOfCurrentInputMethod) {
             final String locale = ims.getLocale();
             final String mode = ims.getMode();
             mLocaleSplitter.setString(locale);
@@ -172,6 +167,10 @@
                 Log.w(TAG, "Last subtype was disabled. Update to the current one.");
             }
             updateSubtype(mImm.getCurrentInputMethodSubtype());
+        } else {
+            // mLanguageBarInfo.update() will be called in updateSubtype so there is no need
+            // to call this in the if-clause above.
+            mLanguageBarInfo.update();
         }
     }
 
@@ -269,6 +268,7 @@
                 mVoiceInputWrapper.reset();
             }
         }
+        mLanguageBarInfo.update();
     }
 
     // Update the current input locale from Locale string.
@@ -303,12 +303,21 @@
     ////////////////////////////
 
     public void switchToShortcutIME() {
-        final IBinder token = mService.getWindow().getWindow().getAttributes().token;
-        if (token == null || mShortcutInputMethodInfo == null) {
+        if (mShortcutInputMethodInfo == null) {
             return;
         }
+
         final String imiId = mShortcutInputMethodInfo.getId();
         final InputMethodSubtypeCompatWrapper subtype = mShortcutSubtype;
+        switchToTargetIME(imiId, subtype);
+    }
+
+    private void switchToTargetIME(
+            final String imiId, final InputMethodSubtypeCompatWrapper subtype) {
+        final IBinder token = mService.getWindow().getWindow().getAttributes().token;
+        if (token == null) {
+            return;
+        }
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
@@ -412,11 +421,7 @@
     //////////////////////////////////
 
     public int getEnabledKeyboardLocaleCount() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return mLanguageSwitcher.getLocaleCount();
-        } else {
-            return mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
-        }
+        return mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
     }
 
     public boolean useSpacebarLanguageSwitcher() {
@@ -428,74 +433,37 @@
     }
 
     public Locale getInputLocale() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return mLanguageSwitcher.getInputLocale();
-        } else {
-            return mInputLocale;
-        }
+        return mInputLocale;
     }
 
     public String getInputLocaleStr() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            String inputLanguage = null;
-            inputLanguage = mLanguageSwitcher.getInputLanguage();
-            // Should return system locale if there is no Language available.
-            if (inputLanguage == null) {
-                inputLanguage = getSystemLocale().getLanguage();
-            }
-            return inputLanguage;
-        } else {
-            return mInputLocaleStr;
-        }
+        return mInputLocaleStr;
     }
 
     public String[] getEnabledLanguages() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return mLanguageSwitcher.getEnabledLanguages();
-        } else {
-            int enabledLanguageCount = mEnabledLanguagesOfCurrentInputMethod.size();
-            // Workaround for explicitly specifying the voice language
-            if (enabledLanguageCount == 1) {
-                mEnabledLanguagesOfCurrentInputMethod.add(
-                        mEnabledLanguagesOfCurrentInputMethod.get(0));
-                ++enabledLanguageCount;
-            }
-            return mEnabledLanguagesOfCurrentInputMethod.toArray(
-                    new String[enabledLanguageCount]);
+        int enabledLanguageCount = mEnabledLanguagesOfCurrentInputMethod.size();
+        // Workaround for explicitly specifying the voice language
+        if (enabledLanguageCount == 1) {
+            mEnabledLanguagesOfCurrentInputMethod.add(mEnabledLanguagesOfCurrentInputMethod
+                    .get(0));
+            ++enabledLanguageCount;
         }
+        return mEnabledLanguagesOfCurrentInputMethod.toArray(new String[enabledLanguageCount]);
     }
 
     public Locale getSystemLocale() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return mLanguageSwitcher.getSystemLocale();
-        } else {
-            return mSystemLocale;
-        }
+        return mSystemLocale;
     }
 
     public boolean isSystemLanguageSameAsInputLanguage() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return getSystemLocale().getLanguage().equalsIgnoreCase(
-                    getInputLocaleStr().substring(0, 2));
-        } else {
-            return mIsSystemLanguageSameAsInputLanguage;
-        }
+        return mIsSystemLanguageSameAsInputLanguage;
     }
 
     public void onConfigurationChanged(Configuration conf) {
         final Locale systemLocale = conf.locale;
         // If system configuration was changed, update all parameters.
         if (!TextUtils.equals(systemLocale.toString(), mSystemLocale.toString())) {
-            if (mConfigUseSpacebarLanguageSwitcher) {
-                // If the system locale changes and is different from the saved
-                // locale (mSystemLocale), then reload the input locale list from the
-                // latin ime settings (shared prefs) and reset the input locale
-                // to the first one.
-                mLanguageSwitcher.loadLocales(mPrefs);
-                mLanguageSwitcher.setSystemLocale(systemLocale);
-            } else {
-                updateAllParameters();
-            }
+            updateAllParameters();
         }
     }
 
@@ -554,7 +522,70 @@
     // Spacebar Language Switch support //
     //////////////////////////////////////
 
-    private LanguageSwitcher mLanguageSwitcher;
+    private class LanguageBarInfo {
+        private int mCurrentKeyboardSubtypeIndex;
+        private InputMethodSubtypeCompatWrapper mNextKeyboardSubtype;
+        private InputMethodSubtypeCompatWrapper mPreviousKeyboardSubtype;
+        private String mNextLanguage;
+        private String mPreviousLanguage;
+        public LanguageBarInfo() {
+            update();
+        }
+
+        private String getNextLanguage() {
+            return mNextLanguage;
+        }
+
+        private String getPreviousLanguage() {
+            return mPreviousLanguage;
+        }
+
+        public InputMethodSubtypeCompatWrapper getNextKeyboardSubtype() {
+            return mNextKeyboardSubtype;
+        }
+
+        public InputMethodSubtypeCompatWrapper getPreviousKeyboardSubtype() {
+            return mPreviousKeyboardSubtype;
+        }
+
+        public void update() {
+            if (!mConfigUseSpacebarLanguageSwitcher
+                    || mEnabledKeyboardSubtypesOfCurrentInputMethod == null
+                    || mEnabledKeyboardSubtypesOfCurrentInputMethod.size() == 0) return;
+            mCurrentKeyboardSubtypeIndex = getCurrentIndex();
+            mNextKeyboardSubtype = getNextKeyboardSubtypeInternal(mCurrentKeyboardSubtypeIndex);
+            Locale locale = new Locale(mNextKeyboardSubtype.getLocale());
+            mNextLanguage = getDisplayLanguage(locale);
+            mPreviousKeyboardSubtype = getPreviousKeyboardSubtypeInternal(
+                    mCurrentKeyboardSubtypeIndex);
+            locale = new Locale(mPreviousKeyboardSubtype.getLocale());
+            mPreviousLanguage = getDisplayLanguage(locale);
+        }
+
+        private int normalize(int index) {
+            final int N = mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
+            final int ret = index % N;
+            return ret < 0 ? ret + N : ret;
+        }
+
+        private int getCurrentIndex() {
+            final int N = mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
+            for (int i = 0; i < N; ++i) {
+                if (mEnabledKeyboardSubtypesOfCurrentInputMethod.get(i).equals(mCurrentSubtype)) {
+                    return i;
+                }
+            }
+            return 0;
+        }
+
+        private InputMethodSubtypeCompatWrapper getNextKeyboardSubtypeInternal(int index) {
+            return mEnabledKeyboardSubtypesOfCurrentInputMethod.get(normalize(index + 1));
+        }
+
+        private InputMethodSubtypeCompatWrapper getPreviousKeyboardSubtypeInternal(int index) {
+            return mEnabledKeyboardSubtypesOfCurrentInputMethod.get(normalize(index - 1));
+        }
+    }
 
     public static String getFullDisplayName(Locale locale, boolean returnsNameInThisLocale) {
         if (returnsNameInThisLocale) {
@@ -579,32 +610,16 @@
         return Character.toUpperCase(s.charAt(0)) + s.substring(1);
     }
 
-    private void updateForSpacebarLanguageSwitch() {
-        // We need to update mNeedsToDisplayLanguage in onStartInputView because
-        // getEnabledKeyboardLocaleCount could have been changed.
-        mNeedsToDisplayLanguage = !(getEnabledKeyboardLocaleCount() <= 1
-                && getSystemLocale().getLanguage().equalsIgnoreCase(
-                        getInputLocale().getLanguage()));
-    }
-
     public String getInputLanguageName() {
         return getDisplayLanguage(getInputLocale());
     }
 
     public String getNextInputLanguageName() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return getDisplayLanguage(mLanguageSwitcher.getNextInputLocale());
-        } else {
-            return "";
-        }
+        return mLanguageBarInfo.getNextLanguage();
     }
 
     public String getPreviousInputLanguageName() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return getDisplayLanguage(mLanguageSwitcher.getPrevInputLocale());
-        } else {
-            return "";
-        }
+        return mLanguageBarInfo.getPreviousLanguage();
     }
 
     /////////////////////////////
@@ -644,31 +659,23 @@
         return voiceInputSupportedLocales.contains(locale);
     }
 
-    public void loadSettings() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            mLanguageSwitcher.loadLocales(mPrefs);
-        }
+    private void changeToNextSubtype() {
+        final InputMethodSubtypeCompatWrapper subtype =
+                mLanguageBarInfo.getNextKeyboardSubtype();
+        switchToTargetIME(mInputMethodId, subtype);
     }
 
-    public void toggleLanguage(boolean reset, boolean next) {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            if (reset) {
-                mLanguageSwitcher.reset();
-            } else {
-                if (next) {
-                    mLanguageSwitcher.next();
-                } else {
-                    mLanguageSwitcher.prev();
-                }
-            }
-            mLanguageSwitcher.persist(mPrefs);
-        }
+    private void changeToPreviousSubtype() {
+        final InputMethodSubtypeCompatWrapper subtype =
+                mLanguageBarInfo.getPreviousKeyboardSubtype();
+        switchToTargetIME(mInputMethodId, subtype);
     }
 
-    private void initLanguageSwitcher(LatinIME service) {
-        final Configuration conf = service.getResources().getConfiguration();
-        mLanguageSwitcher = new LanguageSwitcher(service);
-        mLanguageSwitcher.loadLocales(mPrefs);
-        mLanguageSwitcher.setSystemLocale(conf.locale);
+    public void toggleLanguage(boolean next) {
+        if (next) {
+            changeToNextSubtype();
+        } else {
+            changeToPreviousSubtype();
+        }
     }
 }
diff --git a/native/src/defines.h b/native/src/defines.h
index ff195f4..bdab19f 100644
--- a/native/src/defines.h
+++ b/native/src/defines.h
@@ -138,13 +138,14 @@
 #define SUGGEST_WORDS_WITH_SPACE_PROXIMITY true
 
 // The following "rate"s are used as a multiplier before dividing by 100, so they are in percent.
-#define WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE 90
+#define WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE 80
 #define WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X 12
 #define WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE 80
 #define WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE 75
 #define WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE 75
 #define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 60
 #define FULL_MATCHED_WORDS_PROMOTION_RATE 120
+#define WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE 90
 
 // This should be greater than or equal to MAX_WORD_LENGTH defined in BinaryDictionary.java
 // This is only used for the size of array. Not to be used in c functions.
diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp
index 2ae2bca..89a1c9e 100644
--- a/native/src/unigram_dictionary.cpp
+++ b/native/src/unigram_dictionary.cpp
@@ -523,6 +523,9 @@
                     * (10 * mInputLength - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X)
                     / (10 * mInputLength
                             - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X + 10);
+            if (DEBUG_DICT) {
+                LOGI("Demotion rate for missing character is %d.", demotionRate);
+            }
             multiplyRate(demotionRate, &finalFreq);
         } else {
             finalFreq = 0;
@@ -539,6 +542,7 @@
     int lengthFreq = TYPED_LETTER_MULTIPLIER;
     for (int i = 0; i < depth; ++i) lengthFreq *= TYPED_LETTER_MULTIPLIER;
     if (lengthFreq == matchWeight) {
+        // Full exact match
         if (depth > 1) {
             if (DEBUG_DICT) {
                 LOGI("Found full matched word.");
@@ -548,6 +552,13 @@
         if (sameLength && transposedPos < 0 && skipPos < 0 && excessivePos < 0) {
             finalFreq = capped255MultForFullMatchAccentsOrCapitalizationDifference(finalFreq);
         }
+    } else if (sameLength && transposedPos < 0 && skipPos < 0 && excessivePos < 0 && depth > 1) {
+        // A word with proximity corrections
+        if (DEBUG_DICT) {
+            LOGI("Found one proximity correction.");
+        }
+        finalFreq *= 2;
+        multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
     }
     if (sameLength) finalFreq *= FULL_WORD_MULTIPLIER;
     return finalFreq;
diff --git a/tests/src/com/android/inputmethod/keyboard/MiniKeyboardBuilderTests.java b/tests/src/com/android/inputmethod/keyboard/MiniKeyboardBuilderTests.java
index 7e3106d..600342a 100644
--- a/tests/src/com/android/inputmethod/keyboard/MiniKeyboardBuilderTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/MiniKeyboardBuilderTests.java
@@ -25,6 +25,16 @@
     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;
+    private static final int XPOS_L1 = WIDTH * 1;
+    private static final int XPOS_L2 = WIDTH * 2;
+    private static final int XPOS_M0 = WIDTH * 5;
+    private static final int XPOS_R3 = WIDTH * 6;
+    private static final int XPOS_R2 = WIDTH * 7;
+    private static final int XPOS_R1 = WIDTH * 8;
+    private static final int XPOS_R0 = WIDTH * 9;
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -33,9 +43,8 @@
     public void testLayoutError() {
         MiniKeyboardLayoutParams params = null;
         try {
-            params = new MiniKeyboardLayoutParams(
-                    10, MAX_COLUMNS + 1, WIDTH, HEIGHT,
-                    WIDTH * 2, WIDTH * MAX_COLUMNS);
+            params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS + 1, WIDTH, HEIGHT, WIDTH * 2,
+                    WIDTH * MAX_COLUMNS);
             fail("Should throw IllegalArgumentException");
         } catch (IllegalArgumentException e) {
             // Too small keyboard to hold mini keyboard.
@@ -48,39 +57,198 @@
     // "[1]" is the default key.
 
     // [1]
-    public void testLayout1Key() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                1, MAX_COLUMNS, WIDTH, HEIGHT,
-                WIDTH * 5, WIDTH * 10);
-        assertEquals("1 key columns", 1, params.mNumColumns);
-        assertEquals("1 key rows", 1, params.mNumRows);
-        assertEquals("1 key left", 0, params.mLeftKeys);
-        assertEquals("1 key right", 1, params.mRightKeys);
-        assertEquals("1 key [1]", 0, params.getColumnPos(0));
-        assertEquals("1 key centering", false, params.mTopRowNeedsCentering);
-        assertEquals("1 key default", 0, params.getDefaultKeyCoordX());
+    public void testLayout1KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
+        assertEquals("1 key M0 columns", 1, params.mNumColumns);
+        assertEquals("1 key M0 rows", 1, params.mNumRows);
+        assertEquals("1 key M0 left", 0, params.mLeftKeys);
+        assertEquals("1 key M0 right", 1, params.mRightKeys);
+        assertEquals("1 key M0 [1]", 0, params.getColumnPos(0));
+        assertEquals("1 key M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key M0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |[1]
+    public void testLayout1KeyL0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
+        assertEquals("1 key L0 columns", 1, params.mNumColumns);
+        assertEquals("1 key L0 rows", 1, params.mNumRows);
+        assertEquals("1 key L0 left", 0, params.mLeftKeys);
+        assertEquals("1 key L0 right", 1, params.mRightKeys);
+        assertEquals("1 key L0 [1]", 0, params.getColumnPos(0));
+        assertEquals("1 key L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [1]
+    public void testLayout1KeyL1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
+        assertEquals("1 key L1 columns", 1, params.mNumColumns);
+        assertEquals("1 key L1 rows", 1, params.mNumRows);
+        assertEquals("1 key L1 left", 0, params.mLeftKeys);
+        assertEquals("1 key L1 right", 1, params.mRightKeys);
+        assertEquals("1 key L1 [1]", 0, params.getColumnPos(0));
+        assertEquals("1 key L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [1]
+    public void testLayout1KeyL2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
+        assertEquals("1 key L2 columns", 1, params.mNumColumns);
+        assertEquals("1 key L2 rows", 1, params.mNumRows);
+        assertEquals("1 key L2 left", 0, params.mLeftKeys);
+        assertEquals("1 key L2 right", 1, params.mRightKeys);
+        assertEquals("1 key L2 [1]", 0, params.getColumnPos(0));
+        assertEquals("1 key L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key L2 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // [1]|
+    public void testLayout1KeyR0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
+        assertEquals("1 key R0 columns", 1, params.mNumColumns);
+        assertEquals("1 key R0 rows", 1, params.mNumRows);
+        assertEquals("1 key R0 left", 0, params.mLeftKeys);
+        assertEquals("1 key R0 right", 1, params.mRightKeys);
+        assertEquals("1 key R0 [1]", 0, params.getColumnPos(0));
+        assertEquals("1 key R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key R0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // [1] ___|
+    public void testLayout1KeyR1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
+        assertEquals("1 key R1 columns", 1, params.mNumColumns);
+        assertEquals("1 key R1 rows", 1, params.mNumRows);
+        assertEquals("1 key R1 left", 0, params.mLeftKeys);
+        assertEquals("1 key R1 right", 1, params.mRightKeys);
+        assertEquals("1 key R1 [1]", 0, params.getColumnPos(0));
+        assertEquals("1 key R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key R1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // [1] ___ ___|
+    public void testLayout1KeyR2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
+        assertEquals("1 key R2 columns", 1, params.mNumColumns);
+        assertEquals("1 key R2 rows", 1, params.mNumRows);
+        assertEquals("1 key R2 left", 0, params.mLeftKeys);
+        assertEquals("1 key R2 right", 1, params.mRightKeys);
+        assertEquals("1 key R2 [1]", 0, params.getColumnPos(0));
+        assertEquals("1 key R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("1 key R2 default", WIDTH * 0, params.getDefaultKeyCoordX());
     }
 
     // [1] [2]
-    public void testLayout2Key() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                2, MAX_COLUMNS, WIDTH, HEIGHT,
-                WIDTH * 5, WIDTH * 10);
-        assertEquals("2 key columns", 2, params.mNumColumns);
-        assertEquals("2 key rows", 1, params.mNumRows);
-        assertEquals("2 key left", 0, params.mLeftKeys);
-        assertEquals("2 key right", 2, params.mRightKeys);
-        assertEquals("2 key [1]", 0, params.getColumnPos(0));
-        assertEquals("2 key [2]", 1, params.getColumnPos(1));
-        assertEquals("2 key centering", false, params.mTopRowNeedsCentering);
-        assertEquals("2 key default", 0, params.getDefaultKeyCoordX());
+    public void testLayout2KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
+        assertEquals("2 key M0 columns", 2, params.mNumColumns);
+        assertEquals("2 key M0 rows", 1, params.mNumRows);
+        assertEquals("2 key M0 left", 0, params.mLeftKeys);
+        assertEquals("2 key M0 right", 2, params.mRightKeys);
+        assertEquals("2 key M0 [1]", 0, params.getColumnPos(0));
+        assertEquals("2 key M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("2 key M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key M0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |[1] [2]
+    public void testLayout2KeyL0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
+        assertEquals("2 key L0 columns", 2, params.mNumColumns);
+        assertEquals("2 key L0 rows", 1, params.mNumRows);
+        assertEquals("2 key L0 left", 0, params.mLeftKeys);
+        assertEquals("2 key L0 right", 2, params.mRightKeys);
+        assertEquals("2 key L0 [1]", 0, params.getColumnPos(0));
+        assertEquals("2 key L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("2 key L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [1] [2]
+    public void testLayout2KeyL1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
+        assertEquals("2 key L1 columns", 2, params.mNumColumns);
+        assertEquals("2 key L1 rows", 1, params.mNumRows);
+        assertEquals("2 key L1 left", 0, params.mLeftKeys);
+        assertEquals("2 key L1 right", 2, params.mRightKeys);
+        assertEquals("2 key L1 [1]", 0, params.getColumnPos(0));
+        assertEquals("2 key L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("2 key L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [1] [2]
+    public void testLayout2KeyL2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
+        assertEquals("2 key L2 columns", 2, params.mNumColumns);
+        assertEquals("2 key L2 rows", 1, params.mNumRows);
+        assertEquals("2 key L2 left", 0, params.mLeftKeys);
+        assertEquals("2 key L2 right", 2, params.mRightKeys);
+        assertEquals("2 key L2 [1]", 0, params.getColumnPos(0));
+        assertEquals("2 key L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("2 key L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key L2 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // [2] [1]|
+    public void testLayout2KeyR0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
+        assertEquals("2 key R0 columns", 2, params.mNumColumns);
+        assertEquals("2 key R0 rows", 1, params.mNumRows);
+        assertEquals("2 key R0 left", 1, params.mLeftKeys);
+        assertEquals("2 key R0 right", 1, params.mRightKeys);
+        assertEquals("2 key R0 [1]", 0, params.getColumnPos(0));
+        assertEquals("2 key R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("2 key R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key R0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [2] [1] ___|
+    public void testLayout2KeyR1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
+        assertEquals("2 key R1 columns", 2, params.mNumColumns);
+        assertEquals("2 key R1 rows", 1, params.mNumRows);
+        assertEquals("2 key R1 left", 1, params.mLeftKeys);
+        assertEquals("2 key R1 right", 1, params.mRightKeys);
+        assertEquals("2 key R1 [1]", 0, params.getColumnPos(0));
+        assertEquals("2 key R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("2 key R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key R1 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [1] [2] ___ ___|
+    public void testLayout2KeyR2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
+        assertEquals("2 key R2 columns", 2, params.mNumColumns);
+        assertEquals("2 key R2 rows", 1, params.mNumRows);
+        assertEquals("2 key R2 left", 0, params.mLeftKeys);
+        assertEquals("2 key R2 right", 2, params.mRightKeys);
+        assertEquals("2 key R2 [1]", 0, params.getColumnPos(0));
+        assertEquals("2 key R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("2 key R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("2 key R2 default", WIDTH * 0, params.getDefaultKeyCoordX());
     }
 
     // [3] [1] [2]
-    public void testLayout3Key() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                3, MAX_COLUMNS, WIDTH, HEIGHT,
-                WIDTH * 5, WIDTH * 10);
+    public void testLayout3KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("3 key columns", 3, params.mNumColumns);
         assertEquals("3 key rows", 1, params.mNumRows);
         assertEquals("3 key left", 1, params.mLeftKeys);
@@ -88,15 +256,104 @@
         assertEquals("3 key [1]", 0, params.getColumnPos(0));
         assertEquals("3 key [2]", 1, params.getColumnPos(1));
         assertEquals("3 key [3]", -1, params.getColumnPos(2));
-        assertEquals("3 key centering", false, params.mTopRowNeedsCentering);
-        assertEquals("3 key default", WIDTH, params.getDefaultKeyCoordX());
+        assertEquals("3 key adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // |[1] [2] [3]
+    public void testLayout3KeyL0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
+        assertEquals("3 key L0 columns", 3, params.mNumColumns);
+        assertEquals("3 key L0 rows", 1, params.mNumRows);
+        assertEquals("3 key L0 left", 0, params.mLeftKeys);
+        assertEquals("3 key L0 right", 3, params.mRightKeys);
+        assertEquals("3 key L0 [1]", 0, params.getColumnPos(0));
+        assertEquals("3 key L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("3 key L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [1] [2] [3]
+    public void testLayout3KeyL1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
+        assertEquals("3 key L1 columns", 3, params.mNumColumns);
+        assertEquals("3 key L1 rows", 1, params.mNumRows);
+        assertEquals("3 key L1 left", 0, params.mLeftKeys);
+        assertEquals("3 key L1 right", 3, params.mRightKeys);
+        assertEquals("3 key L1 [1]", 0, params.getColumnPos(0));
+        assertEquals("3 key L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("3 key L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [3] [1] [2]
+    public void testLayout3KeyL2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
+        assertEquals("3 key L2 columns", 3, params.mNumColumns);
+        assertEquals("3 key L2 rows", 1, params.mNumRows);
+        assertEquals("3 key L2 left", 1, params.mLeftKeys);
+        assertEquals("3 key L2 right", 2, params.mRightKeys);
+        assertEquals("3 key L2 [1]", 0, params.getColumnPos(0));
+        assertEquals("3 key L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("3 key L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [3] [2] [1]|
+    public void testLayout3KeyR0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
+        assertEquals("3 key R0 columns", 3, params.mNumColumns);
+        assertEquals("3 key R0 rows", 1, params.mNumRows);
+        assertEquals("3 key R0 left", 2, params.mLeftKeys);
+        assertEquals("3 key R0 right", 1, params.mRightKeys);
+        assertEquals("3 key R0 [1]", 0, params.getColumnPos(0));
+        assertEquals("3 key R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("3 key R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("3 key R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key R0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // [3] [2] [1] ___|
+    public void testLayout3KeyR1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
+        assertEquals("3 key R1 columns", 3, params.mNumColumns);
+        assertEquals("3 key R1 rows", 1, params.mNumRows);
+        assertEquals("3 key R1 left", 2, params.mLeftKeys);
+        assertEquals("3 key R1 right", 1, params.mRightKeys);
+        assertEquals("3 key R1 [1]", 0, params.getColumnPos(0));
+        assertEquals("3 key R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("3 key R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("3 key R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key R1 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // [3] [1] [2] ___ ___|
+    public void testLayout3KeyR2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
+        assertEquals("3 key R2 columns", 3, params.mNumColumns);
+        assertEquals("3 key R2 rows", 1, params.mNumRows);
+        assertEquals("3 key R2 left", 1, params.mLeftKeys);
+        assertEquals("3 key R2 right", 2, params.mRightKeys);
+        assertEquals("3 key R2 [1]", 0, params.getColumnPos(0));
+        assertEquals("3 key R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("3 key R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("3 key R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("3 key R2 default", WIDTH * 1, params.getDefaultKeyCoordX());
     }
 
     // [3] [1] [2] [4]
-    public void testLayout4Key() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                4, MAX_COLUMNS, WIDTH, HEIGHT,
-                WIDTH * 5, WIDTH * 10);
+    public void testLayout4KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("4 key columns", 4, params.mNumColumns);
         assertEquals("4 key rows", 1, params.mNumRows);
         assertEquals("4 key left", 1, params.mLeftKeys);
@@ -105,15 +362,110 @@
         assertEquals("4 key [2]", 1, params.getColumnPos(1));
         assertEquals("4 key [3]", -1, params.getColumnPos(2));
         assertEquals("4 key [4]", 2, params.getColumnPos(3));
-        assertEquals("4 key centering", false, params.mTopRowNeedsCentering);
-        assertEquals("4 key default", WIDTH, params.getDefaultKeyCoordX());
+        assertEquals("4 key adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // |[1] [2] [3] [4]
+    public void testLayout4KeyL0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
+        assertEquals("4 key L0 columns", 4, params.mNumColumns);
+        assertEquals("4 key L0 rows", 1, params.mNumRows);
+        assertEquals("4 key L0 left", 0, params.mLeftKeys);
+        assertEquals("4 key L0 right", 4, params.mRightKeys);
+        assertEquals("4 key L0 [1]", 0, params.getColumnPos(0));
+        assertEquals("4 key L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("4 key L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("4 key L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [1] [2] [3] [4]
+    public void testLayout4KeyL1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
+        assertEquals("4 key L1 columns", 4, params.mNumColumns);
+        assertEquals("4 key L1 rows", 1, params.mNumRows);
+        assertEquals("4 key L1 left", 0, params.mLeftKeys);
+        assertEquals("4 key L1 right", 4, params.mRightKeys);
+        assertEquals("4 key L1 [1]", 0, params.getColumnPos(0));
+        assertEquals("4 key L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("4 key L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("4 key L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [3] [1] [2] [4]
+    public void testLayout4KeyL2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
+        assertEquals("4 key L2 columns", 4, params.mNumColumns);
+        assertEquals("4 key L2 rows", 1, params.mNumRows);
+        assertEquals("4 key L2 left", 1, params.mLeftKeys);
+        assertEquals("4 key L2 right", 3, params.mRightKeys);
+        assertEquals("4 key L2 [1]", 0, params.getColumnPos(0));
+        assertEquals("4 key L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("4 key L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("4 key L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [4] [3] [2] [1]|
+    public void testLayout4KeyR0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
+        assertEquals("4 key R0 columns", 4, params.mNumColumns);
+        assertEquals("4 key R0 rows", 1, params.mNumRows);
+        assertEquals("4 key R0 left", 3, params.mLeftKeys);
+        assertEquals("4 key R0 right", 1, params.mRightKeys);
+        assertEquals("4 key R0 [1]", 0, params.getColumnPos(0));
+        assertEquals("4 key R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("4 key R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("4 key R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("4 key R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key R0 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // [4] [3] [2] [1] ___|
+    public void testLayout4KeyR1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
+        assertEquals("4 key R1 columns", 4, params.mNumColumns);
+        assertEquals("4 key R1 rows", 1, params.mNumRows);
+        assertEquals("4 key R1 left", 3, params.mLeftKeys);
+        assertEquals("4 key R1 right", 1, params.mRightKeys);
+        assertEquals("4 key R1 [1]", 0, params.getColumnPos(0));
+        assertEquals("4 key R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("4 key R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("4 key R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("4 key R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key R1 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // [4] [3] [1] [2] ___ ___|
+    public void testLayout4KeyR2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
+        assertEquals("4 key R2 columns", 4, params.mNumColumns);
+        assertEquals("4 key R2 rows", 1, params.mNumRows);
+        assertEquals("4 key R2 left", 2, params.mLeftKeys);
+        assertEquals("4 key R2 right", 2, params.mRightKeys);
+        assertEquals("4 key R2 [1]", 0, params.getColumnPos(0));
+        assertEquals("4 key R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("4 key R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("4 key R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("4 key R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("4 key R2 default", WIDTH * 2, params.getDefaultKeyCoordX());
     }
 
     // [5] [3] [1] [2] [4]
-    public void testLayout5Key() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                5, MAX_COLUMNS, WIDTH, HEIGHT,
-                WIDTH * 5, WIDTH * 10);
+    public void testLayout5KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("5 key columns", 5, params.mNumColumns);
         assertEquals("5 key rows", 1, params.mNumRows);
         assertEquals("5 key left", 2, params.mLeftKeys);
@@ -123,190 +475,942 @@
         assertEquals("5 key [3]", -1, params.getColumnPos(2));
         assertEquals("5 key [4]", 2, params.getColumnPos(3));
         assertEquals("5 key [5]", -2, params.getColumnPos(4));
-        assertEquals("5 key centering", false, params.mTopRowNeedsCentering);
+        assertEquals("5 key adjust", 0, params.mTopRowAdjustment);
         assertEquals("5 key default", WIDTH * 2, params.getDefaultKeyCoordX());
     }
 
-    //         [6]
-    // [5] [3] [1] [2] [4]
-    public void testLayout6Key() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                6, MAX_COLUMNS, WIDTH, HEIGHT,
-                WIDTH * 5, WIDTH * 10);
-        assertEquals("6 key columns", 5, params.mNumColumns);
+    // |[1] [2] [3] [4] [5]
+    public void testLayout5KeyL0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
+        assertEquals("5 key L0 columns", 5, params.mNumColumns);
+        assertEquals("5 key L0 rows", 1, params.mNumRows);
+        assertEquals("5 key L0 left", 0, params.mLeftKeys);
+        assertEquals("5 key L0 right", 5, params.mRightKeys);
+        assertEquals("5 key L0 [1]", 0, params.getColumnPos(0));
+        assertEquals("5 key L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("5 key L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("5 key L0 [5]", 4, params.getColumnPos(4));
+        assertEquals("5 key L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [1] [2] [3] [4] [5]
+    public void testLayout5KeyL1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
+        assertEquals("5 key L1 columns", 5, params.mNumColumns);
+        assertEquals("5 key L1 rows", 1, params.mNumRows);
+        assertEquals("5 key L1 left", 0, params.mLeftKeys);
+        assertEquals("5 key L1 right", 5, params.mRightKeys);
+        assertEquals("5 key L1 [1]", 0, params.getColumnPos(0));
+        assertEquals("5 key L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("5 key L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("5 key L1 [5]", 4, params.getColumnPos(4));
+        assertEquals("5 key L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [3] [1] [2] [4] [5]
+    public void testLayout5KeyL2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
+        assertEquals("5 key L2 columns", 5, params.mNumColumns);
+        assertEquals("5 key L2 rows", 1, params.mNumRows);
+        assertEquals("5 key L2 left", 1, params.mLeftKeys);
+        assertEquals("5 key L2 right", 4, params.mRightKeys);
+        assertEquals("5 key L2 [1]", 0, params.getColumnPos(0));
+        assertEquals("5 key L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("5 key L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("5 key L2 [5]", 3, params.getColumnPos(4));
+        assertEquals("5 key L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [5] [4] [3] [2] [1]|
+    public void testLayout5KeyR0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
+        assertEquals("5 key R0 columns", 5, params.mNumColumns);
+        assertEquals("5 key R0 rows", 1, params.mNumRows);
+        assertEquals("5 key R0 left", 4, params.mLeftKeys);
+        assertEquals("5 key R0 right", 1, params.mRightKeys);
+        assertEquals("5 key R0 [1]", 0, params.getColumnPos(0));
+        assertEquals("5 key R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("5 key R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("5 key R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("5 key R0 [5]", -4, params.getColumnPos(4));
+        assertEquals("5 key R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key R0 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    // [5] [4] [3] [2] [1] ___|
+    public void testLayout5KeyR1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
+        assertEquals("5 key R1 columns", 5, params.mNumColumns);
+        assertEquals("5 key R1 rows", 1, params.mNumRows);
+        assertEquals("5 key R1 left", 4, params.mLeftKeys);
+        assertEquals("5 key R1 right", 1, params.mRightKeys);
+        assertEquals("5 key R1 [1]", 0, params.getColumnPos(0));
+        assertEquals("5 key R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("5 key R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("5 key R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("5 key R1 [5]", -4, params.getColumnPos(4));
+        assertEquals("5 key R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key R1 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    // [5] [4] [3] [1] [2] ___ ___|
+    public void testLayout5KeyR2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
+        assertEquals("5 key R2 columns", 5, params.mNumColumns);
+        assertEquals("5 key R2 rows", 1, params.mNumRows);
+        assertEquals("5 key R2 left", 3, params.mLeftKeys);
+        assertEquals("5 key R2 right", 2, params.mRightKeys);
+        assertEquals("5 key R2 [1]", 0, params.getColumnPos(0));
+        assertEquals("5 key R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("5 key R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("5 key R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("5 key R2 [5]", -3, params.getColumnPos(4));
+        assertEquals("5 key R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("5 key R2 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // [6] [4] [5]
+    // [3] [1] [2]
+    public void testLayout6KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
+        assertEquals("6 key columns", 3, params.mNumColumns);
         assertEquals("6 key rows", 2, params.mNumRows);
-        assertEquals("6 key left", 2, params.mLeftKeys);
-        assertEquals("6 key right", 3, params.mRightKeys);
+        assertEquals("6 key left", 1, params.mLeftKeys);
+        assertEquals("6 key right", 2, params.mRightKeys);
         assertEquals("6 key [1]", 0, params.getColumnPos(0));
         assertEquals("6 key [2]", 1, params.getColumnPos(1));
         assertEquals("6 key [3]", -1, params.getColumnPos(2));
-        assertEquals("6 key [4]", 2, params.getColumnPos(3));
-        assertEquals("6 key [5]", -2, params.getColumnPos(4));
-        assertEquals("6 key [6]", 0, params.getColumnPos(5));
-        assertEquals("6 key centering", false, params.mTopRowNeedsCentering);
-        assertEquals("6 key default", WIDTH * 2, params.getDefaultKeyCoordX());
+        assertEquals("6 key [4]", 0, params.getColumnPos(3));
+        assertEquals("6 key [5]", 1, params.getColumnPos(4));
+        assertEquals("6 key [6]", -1, params.getColumnPos(5));
+        assertEquals("6 key adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key default", WIDTH * 1, params.getDefaultKeyCoordX());
     }
 
-    //       [6] [7]
-    // [5] [3] [1] [2] [4]
-    public void testLayout7Key() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                7, MAX_COLUMNS, WIDTH, HEIGHT,
-                WIDTH * 5, WIDTH * 10);
-        assertEquals("7 key columns", 5, params.mNumColumns);
+    // |[4] [5] [6]
+    // |[1] [2] [3]
+    public void testLayout6KeyL0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
+        assertEquals("6 key L0 columns", 3, params.mNumColumns);
+        assertEquals("6 key L0 rows", 2, params.mNumRows);
+        assertEquals("6 key L0 left", 0, params.mLeftKeys);
+        assertEquals("6 key L0 right", 3, params.mRightKeys);
+        assertEquals("6 key L0 [1]", 0, params.getColumnPos(0));
+        assertEquals("6 key L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("6 key L0 [4]", 0, params.getColumnPos(3));
+        assertEquals("6 key L0 [5]", 1, params.getColumnPos(4));
+        assertEquals("6 key L0 [6]", 2, params.getColumnPos(5));
+        assertEquals("6 key L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [4] [5] [6]
+    // |___ [1] [2] [3]
+    public void testLayout6KeyL1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
+        assertEquals("6 key L1 columns", 3, params.mNumColumns);
+        assertEquals("6 key L1 rows", 2, params.mNumRows);
+        assertEquals("6 key L1 left", 0, params.mLeftKeys);
+        assertEquals("6 key L1 right", 3, params.mRightKeys);
+        assertEquals("6 key L1 [1]", 0, params.getColumnPos(0));
+        assertEquals("6 key L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("6 key L1 [4]", 0, params.getColumnPos(3));
+        assertEquals("6 key L1 [5]", 1, params.getColumnPos(4));
+        assertEquals("6 key L1 [6]", 2, params.getColumnPos(5));
+        assertEquals("6 key L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [6] [4] [5]
+    // |___ ___ [3] [1] [2]
+    public void testLayout6KeyL2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
+        assertEquals("6 key L2 columns", 3, params.mNumColumns);
+        assertEquals("6 key L2 rows", 2, params.mNumRows);
+        assertEquals("6 key L2 left", 1, params.mLeftKeys);
+        assertEquals("6 key L2 right", 2, params.mRightKeys);
+        assertEquals("6 key L2 [1]", 0, params.getColumnPos(0));
+        assertEquals("6 key L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("6 key L2 [4]", 0, params.getColumnPos(3));
+        assertEquals("6 key L2 [5]", 1, params.getColumnPos(4));
+        assertEquals("6 key L2 [6]", -1, params.getColumnPos(5));
+        assertEquals("6 key L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [6] [5] [4]|
+    // [3] [2] [1]|
+    public void testLayout6KeyR0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
+        assertEquals("6 key R0 columns", 3, params.mNumColumns);
+        assertEquals("6 key R0 rows", 2, params.mNumRows);
+        assertEquals("6 key R0 left", 2, params.mLeftKeys);
+        assertEquals("6 key R0 right", 1, params.mRightKeys);
+        assertEquals("6 key R0 [1]", 0, params.getColumnPos(0));
+        assertEquals("6 key R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("6 key R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("6 key R0 [4]", 0, params.getColumnPos(3));
+        assertEquals("6 key R0 [5]", -1, params.getColumnPos(4));
+        assertEquals("6 key R0 [6]", -2, params.getColumnPos(5));
+        assertEquals("6 key R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key R0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // [6] [5] [4] ___|
+    // [3] [2] [1] ___|
+    public void testLayout6KeyR1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
+        assertEquals("6 key R1 columns", 3, params.mNumColumns);
+        assertEquals("6 key R1 rows", 2, params.mNumRows);
+        assertEquals("6 key R1 left", 2, params.mLeftKeys);
+        assertEquals("6 key R1 right", 1, params.mRightKeys);
+        assertEquals("6 key R1 [1]", 0, params.getColumnPos(0));
+        assertEquals("6 key R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("6 key R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("6 key R1 [4]", 0, params.getColumnPos(3));
+        assertEquals("6 key R1 [5]", -1, params.getColumnPos(4));
+        assertEquals("6 key R1 [6]", -2, params.getColumnPos(5));
+        assertEquals("6 key R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key R1 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // [6] [4] [5] ___ ___|
+    // [3] [1] [2] ___ ___|
+    public void testLayout6KeyR2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
+        assertEquals("6 key R2 columns", 3, params.mNumColumns);
+        assertEquals("6 key R2 rows", 2, params.mNumRows);
+        assertEquals("6 key R2 left", 1, params.mLeftKeys);
+        assertEquals("6 key R2 right", 2, params.mRightKeys);
+        assertEquals("6 key R2 [1]", 0, params.getColumnPos(0));
+        assertEquals("6 key R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("6 key R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("6 key R2 [4]", 0, params.getColumnPos(3));
+        assertEquals("6 key R2 [5]", 1, params.getColumnPos(4));
+        assertEquals("6 key R2 [6]", -1, params.getColumnPos(5));
+        assertEquals("6 key R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("6 key R2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //   [7] [5] [6]
+    // [3] [1] [2] [4]
+    public void testLayout7KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
+        assertEquals("7 key columns", 4, params.mNumColumns);
         assertEquals("7 key rows", 2, params.mNumRows);
-        assertEquals("7 key left", 2, params.mLeftKeys);
+        assertEquals("7 key left", 1, params.mLeftKeys);
         assertEquals("7 key right", 3, params.mRightKeys);
         assertEquals("7 key [1]", 0, params.getColumnPos(0));
         assertEquals("7 key [2]", 1, params.getColumnPos(1));
         assertEquals("7 key [3]", -1, params.getColumnPos(2));
         assertEquals("7 key [4]", 2, params.getColumnPos(3));
-        assertEquals("7 key [5]", -2, params.getColumnPos(4));
-        assertEquals("7 key [6]", 0, params.getColumnPos(5));
-        assertEquals("7 key [7]", 1, params.getColumnPos(6));
-        assertEquals("7 key centering", true, params.mTopRowNeedsCentering);
-        assertEquals("7 key default", WIDTH * 2, params.getDefaultKeyCoordX());
+        assertEquals("7 key [5]", 0, params.getColumnPos(4));
+        assertEquals("7 key [6]", 1, params.getColumnPos(5));
+        assertEquals("7 key [7]", -1, params.getColumnPos(6));
+        assertEquals("7 key adjust", 1, params.mTopRowAdjustment);
+        assertEquals("7 key default", WIDTH * 1, params.getDefaultKeyCoordX());
     }
 
-    //     [8] [6] [7]
-    // [5] [3] [1] [2] [4]
-    public void testLayout8Key() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                8, MAX_COLUMNS, WIDTH, HEIGHT,
-                WIDTH * 5, WIDTH * 10);
-        assertEquals("8 key columns", 5, params.mNumColumns);
-        assertEquals("8 key rows", 2, params.mNumRows);
-        assertEquals("8 key left", 2, params.mLeftKeys);
-        assertEquals("8 key right", 3, params.mRightKeys);
-        assertEquals("8 key [1]", 0, params.getColumnPos(0));
-        assertEquals("8 key [2]", 1, params.getColumnPos(1));
-        assertEquals("8 key [3]", -1, params.getColumnPos(2));
-        assertEquals("8 key [4]", 2, params.getColumnPos(3));
-        assertEquals("8 key [5]", -2, params.getColumnPos(4));
-        assertEquals("8 key [6]", 0, params.getColumnPos(5));
-        assertEquals("8 key [7]", 1, params.getColumnPos(6));
-        assertEquals("8 key [8]", -1, params.getColumnPos(7));
-        assertEquals("8 key centering", false, params.mTopRowNeedsCentering);
-        assertEquals("8 key default", WIDTH * 2, params.getDefaultKeyCoordX());
+    // |[5] [6] [7]
+    // |[1] [2] [3] [4]
+    public void testLayout7KeyL0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
+        assertEquals("7 key L0 columns", 4, params.mNumColumns);
+        assertEquals("7 key L0 rows", 2, params.mNumRows);
+        assertEquals("7 key L0 left", 0, params.mLeftKeys);
+        assertEquals("7 key L0 right", 4, params.mRightKeys);
+        assertEquals("7 key L0 [1]", 0, params.getColumnPos(0));
+        assertEquals("7 key L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("7 key L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("7 key L0 [5]", 0, params.getColumnPos(4));
+        assertEquals("7 key L0 [6]", 1, params.getColumnPos(5));
+        assertEquals("7 key L0 [7]", 2, params.getColumnPos(6));
+        assertEquals("7 key L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [5] [6] [7]
+    // |___ [1] [2] [3] [4]
+    public void testLayout7KeyL1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
+        assertEquals("7 key L1 columns", 4, params.mNumColumns);
+        assertEquals("7 key L1 rows", 2, params.mNumRows);
+        assertEquals("7 key L1 left", 0, params.mLeftKeys);
+        assertEquals("7 key L1 right", 4, params.mRightKeys);
+        assertEquals("7 key L1 [1]", 0, params.getColumnPos(0));
+        assertEquals("7 key L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("7 key L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("7 key L1 [5]", 0, params.getColumnPos(4));
+        assertEquals("7 key L1 [6]", 1, params.getColumnPos(5));
+        assertEquals("7 key L1 [7]", 2, params.getColumnPos(6));
+        assertEquals("7 key L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___   [7] [5] [6]
+    // |___ ___ [3] [1] [2] [4]
+    public void testLayout7KeyL2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
+        assertEquals("7 key L2 columns", 4, params.mNumColumns);
+        assertEquals("7 key L2 rows", 2, params.mNumRows);
+        assertEquals("7 key L2 left", 1, params.mLeftKeys);
+        assertEquals("7 key L2 right", 3, params.mRightKeys);
+        assertEquals("7 key L2 [1]", 0, params.getColumnPos(0));
+        assertEquals("7 key L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("7 key L2 [5]", 0, params.getColumnPos(4));
+        assertEquals("7 key L2 [6]", 1, params.getColumnPos(5));
+        assertEquals("7 key L2 [7]", -1, params.getColumnPos(6));
+        assertEquals("7 key L2 adjust", 1, params.mTopRowAdjustment);
+        assertEquals("7 key L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //     [7] [6] [5]|
+    // [4] [3] [2] [1]|
+    public void testLayout7KeyR0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
+        assertEquals("7 key R0 columns", 4, params.mNumColumns);
+        assertEquals("7 key R0 rows", 2, params.mNumRows);
+        assertEquals("7 key R0 left", 3, params.mLeftKeys);
+        assertEquals("7 key R0 right", 1, params.mRightKeys);
+        assertEquals("7 key R0 [1]", 0, params.getColumnPos(0));
+        assertEquals("7 key R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("7 key R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("7 key R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("7 key R0 [5]", 0, params.getColumnPos(4));
+        assertEquals("7 key R0 [6]", -1, params.getColumnPos(5));
+        assertEquals("7 key R0 [7]", -2, params.getColumnPos(6));
+        assertEquals("7 key R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key R0 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    //     [7] [6] [5] ___|
+    // [4] [3] [2] [1] ___|
+    public void testLayout7KeyR1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
+        assertEquals("7 key R1 columns", 4, params.mNumColumns);
+        assertEquals("7 key R1 rows", 2, params.mNumRows);
+        assertEquals("7 key R1 left", 3, params.mLeftKeys);
+        assertEquals("7 key R1 right", 1, params.mRightKeys);
+        assertEquals("7 key R1 [1]", 0, params.getColumnPos(0));
+        assertEquals("7 key R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("7 key R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("7 key R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("7 key R1 [5]", 0, params.getColumnPos(4));
+        assertEquals("7 key R1 [6]", -1, params.getColumnPos(5));
+        assertEquals("7 key R1 [7]", -2, params.getColumnPos(6));
+        assertEquals("7 key R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key R1 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    //   [7] [5] [6]   ___ ___|
+    // [4] [3] [1] [2] ___ ___|
+    public void testLayout7KeyR2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
+        assertEquals("7 key R2 columns", 4, params.mNumColumns);
+        assertEquals("7 key R2 rows", 2, params.mNumRows);
+        assertEquals("7 key R2 left", 2, params.mLeftKeys);
+        assertEquals("7 key R2 right", 2, params.mRightKeys);
+        assertEquals("7 key R2 [1]", 0, params.getColumnPos(0));
+        assertEquals("7 key R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("7 key R2 [5]", 0, params.getColumnPos(4));
+        assertEquals("7 key R2 [6]", 1, params.getColumnPos(5));
+        assertEquals("7 key R2 [7]", -1, params.getColumnPos(6));
+        assertEquals("7 key R2 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("7 key R2 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // [7] [6] [5] [3] [1] [2] [4] ___|
+    public void testLayout7KeyR3Max7() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, 7, WIDTH,
+                HEIGHT, XPOS_R3, KEYBOARD_WIDTH);
+        assertEquals("7 key R2 columns", 7, params.mNumColumns);
+        assertEquals("7 key R2 rows", 1, params.mNumRows);
+        assertEquals("7 key R2 left", 4, params.mLeftKeys);
+        assertEquals("7 key R2 right", 3, params.mRightKeys);
+        assertEquals("7 key R2 [1]", 0, params.getColumnPos(0));
+        assertEquals("7 key R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("7 key R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("7 key R2 [4]", 2, params.getColumnPos(3));
+        assertEquals("7 key R2 [5]", -2, params.getColumnPos(4));
+        assertEquals("7 key R2 [6]", -3, params.getColumnPos(5));
+        assertEquals("7 key R2 [7]", -4, params.getColumnPos(6));
+        assertEquals("7 key R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("7 key R2 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    // [7] [5] [6] [8]
+    // [3] [1] [2] [4]
+    public void testLayout8KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
+        assertEquals("8 key M0 columns", 4, params.mNumColumns);
+        assertEquals("8 key M0 rows", 2, params.mNumRows);
+        assertEquals("8 key M0 left", 1, params.mLeftKeys);
+        assertEquals("8 key M0 right", 3, params.mRightKeys);
+        assertEquals("8 key M0 [1]", 0, params.getColumnPos(0));
+        assertEquals("8 key M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("8 key M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("8 key M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("8 key M0 [5]", 0, params.getColumnPos(4));
+        assertEquals("8 key M0 [6]", 1, params.getColumnPos(5));
+        assertEquals("8 key M0 [7]", -1, params.getColumnPos(6));
+        assertEquals("8 key M0 [8]", 2, params.getColumnPos(7));
+        assertEquals("8 key M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key M0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // |[5] [6] [7] [8]
+    // |[1] [2] [3] [4]
+    public void testLayout8KeyL0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
+        assertEquals("8 key L0 columns", 4, params.mNumColumns);
+        assertEquals("8 key L0 rows", 2, params.mNumRows);
+        assertEquals("8 key L0 left", 0, params.mLeftKeys);
+        assertEquals("8 key L0 right", 4, params.mRightKeys);
+        assertEquals("8 key L0 [1]", 0, params.getColumnPos(0));
+        assertEquals("8 key L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("8 key L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("8 key L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("8 key L0 [5]", 0, params.getColumnPos(4));
+        assertEquals("8 key L0 [6]", 1, params.getColumnPos(5));
+        assertEquals("8 key L0 [7]", 2, params.getColumnPos(6));
+        assertEquals("8 key L0 [8]", 3, params.getColumnPos(7));
+        assertEquals("8 key L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [5] [6] [7] [8]
+    // |___ [1] [2] [3] [4]
+    public void testLayout8KeyL1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
+        assertEquals("8 key L1 columns", 4, params.mNumColumns);
+        assertEquals("8 key L1 rows", 2, params.mNumRows);
+        assertEquals("8 key L1 left", 0, params.mLeftKeys);
+        assertEquals("8 key L1 right", 4, params.mRightKeys);
+        assertEquals("8 key L1 [1]", 0, params.getColumnPos(0));
+        assertEquals("8 key L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("8 key L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("8 key L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("8 key L1 [5]", 0, params.getColumnPos(4));
+        assertEquals("8 key L1 [6]", 1, params.getColumnPos(5));
+        assertEquals("8 key L1 [7]", 2, params.getColumnPos(6));
+        assertEquals("8 key L1 [8]", 3, params.getColumnPos(7));
+        assertEquals("8 key L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [7] [5] [6] [8]
+    // |___ ___ [3] [1] [2] [4]
+    public void testLayout8KeyL2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
+        assertEquals("8 key L2 columns", 4, params.mNumColumns);
+        assertEquals("8 key L2 rows", 2, params.mNumRows);
+        assertEquals("8 key L2 left", 1, params.mLeftKeys);
+        assertEquals("8 key L2 right", 3, params.mRightKeys);
+        assertEquals("8 key L2 [1]", 0, params.getColumnPos(0));
+        assertEquals("8 key L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("8 key L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("8 key L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("8 key L2 [5]", 0, params.getColumnPos(4));
+        assertEquals("8 key L2 [6]", 1, params.getColumnPos(5));
+        assertEquals("8 key L2 [7]", -1, params.getColumnPos(6));
+        assertEquals("8 key L2 [8]", 2, params.getColumnPos(7));
+        assertEquals("8 key L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [8] [7] [6] [5]|
+    // [4] [3] [2] [1]|
+    public void testLayout8KeyR0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
+        assertEquals("8 key R0 columns", 4, params.mNumColumns);
+        assertEquals("8 key R0 rows", 2, params.mNumRows);
+        assertEquals("8 key R0 left", 3, params.mLeftKeys);
+        assertEquals("8 key R0 right", 1, params.mRightKeys);
+        assertEquals("8 key R0 [1]", 0, params.getColumnPos(0));
+        assertEquals("8 key R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("8 key R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("8 key R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("8 key R0 [5]", 0, params.getColumnPos(4));
+        assertEquals("8 key R0 [6]", -1, params.getColumnPos(5));
+        assertEquals("8 key R0 [7]", -2, params.getColumnPos(6));
+        assertEquals("8 key R0 [8]", -3, params.getColumnPos(7));
+        assertEquals("8 key R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key R0 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // [8] [7] [6] [5] ___|
+    // [4] [3] [2] [1] ___|
+    public void testLayout8KeyR1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
+        assertEquals("8 key R1 columns", 4, params.mNumColumns);
+        assertEquals("8 key R1 rows", 2, params.mNumRows);
+        assertEquals("8 key R1 left", 3, params.mLeftKeys);
+        assertEquals("8 key R1 right", 1, params.mRightKeys);
+        assertEquals("8 key R1 [1]", 0, params.getColumnPos(0));
+        assertEquals("8 key R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("8 key R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("8 key R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("8 key R1 [5]", 0, params.getColumnPos(4));
+        assertEquals("8 key R1 [6]", -1, params.getColumnPos(5));
+        assertEquals("8 key R1 [7]", -2, params.getColumnPos(6));
+        assertEquals("8 key R1 [8]", -3, params.getColumnPos(7));
+        assertEquals("8 key R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key R1 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // [8] [7] [5] [6] ___ ___|
+    // [4] [3] [1] [2] ___ ___|
+    public void testLayout8KeyR2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
+        assertEquals("8 key R2 columns", 4, params.mNumColumns);
+        assertEquals("8 key R2 rows", 2, params.mNumRows);
+        assertEquals("8 key R2 left", 2, params.mLeftKeys);
+        assertEquals("8 key R2 right", 2, params.mRightKeys);
+        assertEquals("8 key R2 [1]", 0, params.getColumnPos(0));
+        assertEquals("8 key R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("8 key R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("8 key R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("8 key R2 [5]", 0, params.getColumnPos(4));
+        assertEquals("8 key R2 [6]", 1, params.getColumnPos(5));
+        assertEquals("8 key R2 [7]", -1, params.getColumnPos(6));
+        assertEquals("8 key R2 [8]", -2, params.getColumnPos(7));
+        assertEquals("8 key R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("8 key R2 default", WIDTH * 2, params.getDefaultKeyCoordX());
     }
 
     //   [8] [6] [7] [9]
     // [5] [3] [1] [2] [4]
-    public void testLayout9Key() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                9, MAX_COLUMNS, WIDTH, HEIGHT,
-                WIDTH * 5, WIDTH * 10);
-        assertEquals("9 key columns", 5, params.mNumColumns);
-        assertEquals("9 key rows", 2, params.mNumRows);
-        assertEquals("9 key left", 2, params.mLeftKeys);
-        assertEquals("9 key right", 3, params.mRightKeys);
-        assertEquals("9 key [1]", 0, params.getColumnPos(0));
-        assertEquals("9 key [2]", 1, params.getColumnPos(1));
-        assertEquals("9 key [3]", -1, params.getColumnPos(2));
-        assertEquals("9 key [4]", 2, params.getColumnPos(3));
-        assertEquals("9 key [5]", -2, params.getColumnPos(4));
-        assertEquals("9 key [6]", 0, params.getColumnPos(5));
-        assertEquals("9 key [7]", 1, params.getColumnPos(6));
-        assertEquals("9 key [8]", -1, params.getColumnPos(7));
-        assertEquals("9 key [9]", 2, params.getColumnPos(8));
-        assertEquals("9 key centering", true, params.mTopRowNeedsCentering);
-        assertEquals("9 key default", WIDTH * 2, params.getDefaultKeyCoordX());
+    public void testLayout9KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
+        assertEquals("9 key M0 columns", 5, params.mNumColumns);
+        assertEquals("9 key M0 rows", 2, params.mNumRows);
+        assertEquals("9 key M0 left", 2, params.mLeftKeys);
+        assertEquals("9 key M0 right", 3, params.mRightKeys);
+        assertEquals("9 key M0 [1]", 0, params.getColumnPos(0));
+        assertEquals("9 key M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("9 key M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("9 key M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("9 key M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("9 key M0 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key M0 [7]", 1, params.getColumnPos(6));
+        assertEquals("9 key M0 [8]", -1, params.getColumnPos(7));
+        assertEquals("9 key M0 [9]", 2, params.getColumnPos(8));
+        assertEquals("9 key M0 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("9 key M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
     }
 
-    // Nine keys test.  There is no key space for mini keyboard at left of the parent key.
-    //   [6] [7] [8] [9]
-    // [1] [2] [3] [4] [5]
-    public void testLayout9KeyLeft() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                9, MAX_COLUMNS, WIDTH, HEIGHT,
-                0, WIDTH * 10);
-        assertEquals("9 key left columns", 5, params.mNumColumns);
-        assertEquals("9 key left rows", 2, params.mNumRows);
-        assertEquals("9 key left left", 0, params.mLeftKeys);
-        assertEquals("9 key left right", 5, params.mRightKeys);
-        assertEquals("9 key left [1]", 0, params.getColumnPos(0));
-        assertEquals("9 key left [2]", 1, params.getColumnPos(1));
-        assertEquals("9 key left [3]", 2, params.getColumnPos(2));
-        assertEquals("9 key left [4]", 3, params.getColumnPos(3));
-        assertEquals("9 key left [5]", 4, params.getColumnPos(4));
-        assertEquals("9 key left [6]", 0, params.getColumnPos(5));
-        assertEquals("9 key left [7]", 1, params.getColumnPos(6));
-        assertEquals("9 key left [8]", 2, params.getColumnPos(7));
-        assertEquals("9 key left [9]", 3, params.getColumnPos(8));
-        assertEquals("9 key left centering", true, params.mTopRowNeedsCentering);
-        assertEquals("9 key left default", 0, params.getDefaultKeyCoordX());
+    // |[6] [7] [8] [9]
+    // |[1] [2] [3] [4] [5]
+    public void testLayout9KeyL0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
+        assertEquals("9 key L0 columns", 5, params.mNumColumns);
+        assertEquals("9 key L0 rows", 2, params.mNumRows);
+        assertEquals("9 key L0 left", 0, params.mLeftKeys);
+        assertEquals("9 key L0 right", 5, params.mRightKeys);
+        assertEquals("9 key L0 [1]", 0, params.getColumnPos(0));
+        assertEquals("9 key L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("9 key L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("9 key L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("9 key L0 [5]", 4, params.getColumnPos(4));
+        assertEquals("9 key L0 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key L0 [7]", 1, params.getColumnPos(6));
+        assertEquals("9 key L0 [8]", 2, params.getColumnPos(7));
+        assertEquals("9 key L0 [9]", 3, params.getColumnPos(8));
+        assertEquals("9 key L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("9 key L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
     }
 
-    // Nine keys test.  There is only one key space for mini keyboard at left of the parent key.
-    //   [8] [6] [7] [9]
-    // [3] [1] [2] [4] [5]
-    public void testLayout9KeyNearLeft() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                9, MAX_COLUMNS, WIDTH, HEIGHT,
-                WIDTH, WIDTH * 10);
-        assertEquals("9 key near left columns", 5, params.mNumColumns);
-        assertEquals("9 key near left rows", 2, params.mNumRows);
-        assertEquals("9 key near left left", 1, params.mLeftKeys);
-        assertEquals("9 key near left right", 4, params.mRightKeys);
-        assertEquals("9 key near left [1]", 0, params.getColumnPos(0));
-        assertEquals("9 key near left [2]", 1, params.getColumnPos(1));
-        assertEquals("9 key near left [3]", -1, params.getColumnPos(2));
-        assertEquals("9 key near left [4]", 2, params.getColumnPos(3));
-        assertEquals("9 key near left [5]", 3, params.getColumnPos(4));
-        assertEquals("9 key near left [6]", 0, params.getColumnPos(5));
-        assertEquals("9 key near left [7]", 1, params.getColumnPos(6));
-        assertEquals("9 key near left [8]", -1, params.getColumnPos(7));
-        assertEquals("9 key near left [9]", 2, params.getColumnPos(8));
-        assertEquals("9 key near left centering", true, params.mTopRowNeedsCentering);
-        assertEquals("9 key near left default", WIDTH, params.getDefaultKeyCoordX());
+    // |___ [6] [7] [8] [9]
+    // |___ [1] [2] [3] [4] [5]
+    public void testLayout9KeyL1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
+        assertEquals("9 key L1 columns", 5, params.mNumColumns);
+        assertEquals("9 key L1 rows", 2, params.mNumRows);
+        assertEquals("9 key L1 left", 0, params.mLeftKeys);
+        assertEquals("9 key L1 right", 5, params.mRightKeys);
+        assertEquals("9 key L1 [1]", 0, params.getColumnPos(0));
+        assertEquals("9 key L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("9 key L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("9 key L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("9 key L1 [5]", 4, params.getColumnPos(4));
+        assertEquals("9 key L1 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key L1 [7]", 1, params.getColumnPos(6));
+        assertEquals("9 key L1 [8]", 2, params.getColumnPos(7));
+        assertEquals("9 key L1 [9]", 3, params.getColumnPos(8));
+        assertEquals("9 key L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("9 key L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___   [8] [6] [7] [9]
+    // |___ ___ [3] [1] [2] [4] [5]
+    public void testLayout9KeyL2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
+        assertEquals("9 key L2 columns", 5, params.mNumColumns);
+        assertEquals("9 key L2 rows", 2, params.mNumRows);
+        assertEquals("9 key L2 left", 1, params.mLeftKeys);
+        assertEquals("9 key L2 right", 4, params.mRightKeys);
+        assertEquals("9 key L2 [1]", 0, params.getColumnPos(0));
+        assertEquals("9 key L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("9 key L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("9 key L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("9 key L2 [5]", 3, params.getColumnPos(4));
+        assertEquals("9 key L2 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key L2 [7]", 1, params.getColumnPos(6));
+        assertEquals("9 key L2 [8]", -1, params.getColumnPos(7));
+        assertEquals("9 key L2 [9]", 2, params.getColumnPos(8));
+        assertEquals("9 key L2 adjust", 1, params.mTopRowAdjustment);
+        assertEquals("9 key L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    //     [9] [8] [7] [6]|
+    // [5] [4] [3] [2] [1]|
+    public void testLayout9KeyR0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
+        assertEquals("9 key R0 columns", 5, params.mNumColumns);
+        assertEquals("9 key R0 rows", 2, params.mNumRows);
+        assertEquals("9 key R0 left", 4, params.mLeftKeys);
+        assertEquals("9 key R0 right", 1, params.mRightKeys);
+        assertEquals("9 key R0 [1]", 0, params.getColumnPos(0));
+        assertEquals("9 key R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("9 key R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("9 key R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("9 key R0 [5]", -4, params.getColumnPos(4));
+        assertEquals("9 key R0 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key R0 [7]", -1, params.getColumnPos(6));
+        assertEquals("9 key R0 [8]", -2, params.getColumnPos(7));
+        assertEquals("9 key R0 [9]", -3, params.getColumnPos(8));
+        assertEquals("9 key R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("9 key R0 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    //     [9] [8] [7] [6] ___|
+    // [5] [4] [3] [2] [1] ___|
+    public void testLayout9KeyR1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
+        assertEquals("9 key R1 columns", 5, params.mNumColumns);
+        assertEquals("9 key R1 rows", 2, params.mNumRows);
+        assertEquals("9 key R1 left", 4, params.mLeftKeys);
+        assertEquals("9 key R1 right", 1, params.mRightKeys);
+        assertEquals("9 key R1 [1]", 0, params.getColumnPos(0));
+        assertEquals("9 key R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("9 key R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("9 key R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("9 key R1 [5]", -4, params.getColumnPos(4));
+        assertEquals("9 key R1 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key R1 [7]", -1, params.getColumnPos(6));
+        assertEquals("9 key R1 [8]", -2, params.getColumnPos(7));
+        assertEquals("9 key R1 [9]", -3, params.getColumnPos(8));
+        assertEquals("9 key R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("9 key R1 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    //   [9] [8] [6] [7]   ___ ___|
+    // [5] [4] [3] [1] [2] ___ ___|
+    public void testLayout9KeyR2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
+        assertEquals("9 key R2 columns", 5, params.mNumColumns);
+        assertEquals("9 key R2 rows", 2, params.mNumRows);
+        assertEquals("9 key R2 left", 3, params.mLeftKeys);
+        assertEquals("9 key R2 right", 2, params.mRightKeys);
+        assertEquals("9 key R2 [1]", 0, params.getColumnPos(0));
+        assertEquals("9 key R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("9 key R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("9 key R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("9 key R2 [5]", -3, params.getColumnPos(4));
+        assertEquals("9 key R2 [6]", 0, params.getColumnPos(5));
+        assertEquals("9 key R2 [7]", 1, params.getColumnPos(6));
+        assertEquals("9 key R2 [8]", -1, params.getColumnPos(7));
+        assertEquals("9 key R2 [9]", -2, params.getColumnPos(8));
+        assertEquals("9 key R2 adjust", -1, params.mTopRowAdjustment);
+        assertEquals("9 key R2 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    // [A] [8] [6] [7] [9]
+    // [5] [3] [1] [2] [4]
+    public void testLayout10KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
+        assertEquals("10 key M0 columns", 5, params.mNumColumns);
+        assertEquals("10 key M0 rows", 2, params.mNumRows);
+        assertEquals("10 key M0 left", 2, params.mLeftKeys);
+        assertEquals("10 key M0 right", 3, params.mRightKeys);
+        assertEquals("10 key M0 [1]", 0, params.getColumnPos(0));
+        assertEquals("10 key M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("10 key M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("10 key M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("10 key M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("10 key M0 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key M0 [7]", 1, params.getColumnPos(6));
+        assertEquals("10 key M0 [8]", -1, params.getColumnPos(7));
+        assertEquals("10 key M0 [9]", 2, params.getColumnPos(8));
+        assertEquals("10 key M0 [A]", -2, params.getColumnPos(9));
+        assertEquals("10 key M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
+    }
+
+    // |[6] [7] [8] [9] [A]
+    // |[1] [2] [3] [4] [5]
+    public void testLayout10KeyL0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
+        assertEquals("10 key L0 columns", 5, params.mNumColumns);
+        assertEquals("10 key L0 rows", 2, params.mNumRows);
+        assertEquals("10 key L0 left", 0, params.mLeftKeys);
+        assertEquals("10 key L0 right", 5, params.mRightKeys);
+        assertEquals("10 key L0 [1]", 0, params.getColumnPos(0));
+        assertEquals("10 key L0 [2]", 1, params.getColumnPos(1));
+        assertEquals("10 key L0 [3]", 2, params.getColumnPos(2));
+        assertEquals("10 key L0 [4]", 3, params.getColumnPos(3));
+        assertEquals("10 key L0 [5]", 4, params.getColumnPos(4));
+        assertEquals("10 key L0 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key L0 [7]", 1, params.getColumnPos(6));
+        assertEquals("10 key L0 [8]", 2, params.getColumnPos(7));
+        assertEquals("10 key L0 [9]", 3, params.getColumnPos(8));
+        assertEquals("10 key L0 [A]", 4, params.getColumnPos(9));
+        assertEquals("10 key L0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key L0 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ [6] [7] [8] [9] [A]
+    // |___ [1] [2] [3] [4] [5]
+    public void testLayout10KeyL1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
+        assertEquals("10 key L1 columns", 5, params.mNumColumns);
+        assertEquals("10 key L1 rows", 2, params.mNumRows);
+        assertEquals("10 key L1 left", 0, params.mLeftKeys);
+        assertEquals("10 key L1 right", 5, params.mRightKeys);
+        assertEquals("10 key L1 [1]", 0, params.getColumnPos(0));
+        assertEquals("10 key L1 [2]", 1, params.getColumnPos(1));
+        assertEquals("10 key L1 [3]", 2, params.getColumnPos(2));
+        assertEquals("10 key L1 [4]", 3, params.getColumnPos(3));
+        assertEquals("10 key L1 [5]", 4, params.getColumnPos(4));
+        assertEquals("10 key L1 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key L1 [7]", 1, params.getColumnPos(6));
+        assertEquals("10 key L1 [8]", 2, params.getColumnPos(7));
+        assertEquals("10 key L1 [9]", 3, params.getColumnPos(8));
+        assertEquals("10 key L1 [A]", 4, params.getColumnPos(9));
+        assertEquals("10 key L1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key L1 default", WIDTH * 0, params.getDefaultKeyCoordX());
+    }
+
+    // |___ ___ [8] [6] [7] [9] [A]
+    // |___ ___ [3] [1] [2] [4] [5]
+    public void testLayout10KeyL2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
+        assertEquals("10 key L2 columns", 5, params.mNumColumns);
+        assertEquals("10 key L2 rows", 2, params.mNumRows);
+        assertEquals("10 key L2 left", 1, params.mLeftKeys);
+        assertEquals("10 key L2 right", 4, params.mRightKeys);
+        assertEquals("10 key L2 [1]", 0, params.getColumnPos(0));
+        assertEquals("10 key L2 [2]", 1, params.getColumnPos(1));
+        assertEquals("10 key L2 [3]", -1, params.getColumnPos(2));
+        assertEquals("10 key L2 [4]", 2, params.getColumnPos(3));
+        assertEquals("10 key L2 [5]", 3, params.getColumnPos(4));
+        assertEquals("10 key L2 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key L2 [7]", 1, params.getColumnPos(6));
+        assertEquals("10 key L2 [8]", -1, params.getColumnPos(7));
+        assertEquals("10 key L2 [9]", 2, params.getColumnPos(8));
+        assertEquals("10 key L2 [A]", 3, params.getColumnPos(9));
+        assertEquals("10 key L2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key L2 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [A] [9] [8] [7] [6]|
+    // [5] [4] [3] [2] [1]|
+    public void testLayout10KeyR0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
+        assertEquals("10 key R0 columns", 5, params.mNumColumns);
+        assertEquals("10 key R0 rows", 2, params.mNumRows);
+        assertEquals("10 key R0 left", 4, params.mLeftKeys);
+        assertEquals("10 key R0 right", 1, params.mRightKeys);
+        assertEquals("10 key R0 [1]", 0, params.getColumnPos(0));
+        assertEquals("10 key R0 [2]", -1, params.getColumnPos(1));
+        assertEquals("10 key R0 [3]", -2, params.getColumnPos(2));
+        assertEquals("10 key R0 [4]", -3, params.getColumnPos(3));
+        assertEquals("10 key R0 [5]", -4, params.getColumnPos(4));
+        assertEquals("10 key R0 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key R0 [7]", -1, params.getColumnPos(6));
+        assertEquals("10 key R0 [8]", -2, params.getColumnPos(7));
+        assertEquals("10 key R0 [9]", -3, params.getColumnPos(8));
+        assertEquals("10 key R0 [A]", -4, params.getColumnPos(9));
+        assertEquals("10 key R0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key R0 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    // [A] [9] [8] [7] [6] ___|
+    // [5] [4] [3] [2] [1] ___|
+    public void testLayout10KeyR1() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
+        assertEquals("10 key R1 columns", 5, params.mNumColumns);
+        assertEquals("10 key R1 rows", 2, params.mNumRows);
+        assertEquals("10 key R1 left", 4, params.mLeftKeys);
+        assertEquals("10 key R1 right", 1, params.mRightKeys);
+        assertEquals("10 key R1 [1]", 0, params.getColumnPos(0));
+        assertEquals("10 key R1 [2]", -1, params.getColumnPos(1));
+        assertEquals("10 key R1 [3]", -2, params.getColumnPos(2));
+        assertEquals("10 key R1 [4]", -3, params.getColumnPos(3));
+        assertEquals("10 key R1 [5]", -4, params.getColumnPos(4));
+        assertEquals("10 key R1 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key R1 [7]", -1, params.getColumnPos(6));
+        assertEquals("10 key R1 [8]", -2, params.getColumnPos(7));
+        assertEquals("10 key R1 [9]", -3, params.getColumnPos(8));
+        assertEquals("10 key R1 [A]", -4, params.getColumnPos(9));
+        assertEquals("10 key R1 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key R1 default", WIDTH * 4, params.getDefaultKeyCoordX());
+    }
+
+    // [A] [9] [8] [6] [7] ___ ___|
+    // [5] [4] [3] [1] [2] ___ ___|
+    public void testLayout10KeyR2() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
+        assertEquals("10 key R2 columns", 5, params.mNumColumns);
+        assertEquals("10 key R2 rows", 2, params.mNumRows);
+        assertEquals("10 key R2 left", 3, params.mLeftKeys);
+        assertEquals("10 key R2 right", 2, params.mRightKeys);
+        assertEquals("10 key R2 [1]", 0, params.getColumnPos(0));
+        assertEquals("10 key R2 [2]", 1, params.getColumnPos(1));
+        assertEquals("10 key R2 [3]", -1, params.getColumnPos(2));
+        assertEquals("10 key R2 [4]", -2, params.getColumnPos(3));
+        assertEquals("10 key R2 [5]", -3, params.getColumnPos(4));
+        assertEquals("10 key R2 [6]", 0, params.getColumnPos(5));
+        assertEquals("10 key R2 [7]", 1, params.getColumnPos(6));
+        assertEquals("10 key R2 [8]", -1, params.getColumnPos(7));
+        assertEquals("10 key R2 [9]", -2, params.getColumnPos(8));
+        assertEquals("10 key R2 [A]", -3, params.getColumnPos(9));
+        assertEquals("10 key R2 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("10 key R2 default", WIDTH * 3, params.getDefaultKeyCoordX());
+    }
+
+    //   [B] [9] [A]
+    // [7] [5] [6] [8]
+    // [3] [1] [2] [4]
+    public void testLayout11KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(11, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
+        assertEquals("11 key M0 columns", 4, params.mNumColumns);
+        assertEquals("11 key M0 rows", 3, params.mNumRows);
+        assertEquals("11 key M0 left", 1, params.mLeftKeys);
+        assertEquals("11 key M0 right", 3, params.mRightKeys);
+        assertEquals("11 key M0 [1]", 0, params.getColumnPos(0));
+        assertEquals("11 key M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("11 key M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("11 key M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("11 key M0 [5]", 0, params.getColumnPos(4));
+        assertEquals("11 key M0 [6]", 1, params.getColumnPos(5));
+        assertEquals("11 key M0 [7]", -1, params.getColumnPos(6));
+        assertEquals("11 key M0 [8]", 2, params.getColumnPos(7));
+        assertEquals("11 key M0 [9]", 0, params.getColumnPos(8));
+        assertEquals("11 key M0 [A]", 1, params.getColumnPos(9));
+        assertEquals("11 key M0 [B]", -1, params.getColumnPos(10));
+        assertEquals("11 key M0 adjust", 1, params.mTopRowAdjustment);
+        assertEquals("11 key M0 default", WIDTH * 1, params.getDefaultKeyCoordX());
+    }
+
+    // [B] [9] [A] [C]
+    // [7] [5] [6] [8]
+    // [3] [1] [2] [4]
+    public void testLayout12KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(12, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
+        assertEquals("12 key M0 columns", 4, params.mNumColumns);
+        assertEquals("12 key M0 rows", 3, params.mNumRows);
+        assertEquals("12 key M0 left", 1, params.mLeftKeys);
+        assertEquals("12 key M0 right", 3, params.mRightKeys);
+        assertEquals("12 key M0 [1]", 0, params.getColumnPos(0));
+        assertEquals("12 key M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("12 key M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("12 key M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("12 key M0 [5]", 0, params.getColumnPos(4));
+        assertEquals("12 key M0 [6]", 1, params.getColumnPos(5));
+        assertEquals("12 key M0 [7]", -1, params.getColumnPos(6));
+        assertEquals("12 key M0 [8]", 2, params.getColumnPos(7));
+        assertEquals("12 key M0 [9]", 0, params.getColumnPos(8));
+        assertEquals("12 key M0 [A]", 1, params.getColumnPos(9));
+        assertEquals("12 key M0 [B]", -1, params.getColumnPos(10));
+        assertEquals("12 key M0 [C]", 2, params.getColumnPos(11));
+        assertEquals("12 key M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("12 key M0 default", WIDTH * 1, params.getDefaultKeyCoordX());
     }
 
 
-    // Nine keys test.  There is no key space for mini keyboard at right of the parent key.
-    //   [9] [8] [7] [6]
-    // [5] [4] [3] [2] [1]
-    public void testLayout9KeyRight() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                9, MAX_COLUMNS, WIDTH, HEIGHT,
-                WIDTH * 9, WIDTH * 10);
-        assertEquals("9 key right columns", 5, params.mNumColumns);
-        assertEquals("9 key right rows", 2, params.mNumRows);
-        assertEquals("9 key right left", 4, params.mLeftKeys);
-        assertEquals("9 key right right", 1, params.mRightKeys);
-        assertEquals("9 key right [1]", 0, params.getColumnPos(0));
-        assertEquals("9 key right [2]", -1, params.getColumnPos(1));
-        assertEquals("9 key right [3]", -2, params.getColumnPos(2));
-        assertEquals("9 key right [4]", -3, params.getColumnPos(3));
-        assertEquals("9 key right [5]", -4, params.getColumnPos(4));
-        assertEquals("9 key right [6]", 0, params.getColumnPos(5));
-        assertEquals("9 key right [7]", -1, params.getColumnPos(6));
-        assertEquals("9 key right [8]", -2, params.getColumnPos(7));
-        assertEquals("9 key right [9]", -3, params.getColumnPos(8));
-        assertEquals("9 key right centering", true, params.mTopRowNeedsCentering);
-        assertEquals("9 key right default", WIDTH * 4, params.getDefaultKeyCoordX());
-    }
-
-    // Nine keys test.  There is only one key space for mini keyboard at right of the parent key.
-    //   [9] [8] [6] [7]
-    // [5] [4] [3] [1] [2]
-    public void testLayout9KeyNearRight() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
-                9, MAX_COLUMNS, WIDTH, HEIGHT,
-                WIDTH * 8, WIDTH * 10);
-        assertEquals("9 key near right columns", 5, params.mNumColumns);
-        assertEquals("9 key near right rows", 2, params.mNumRows);
-        assertEquals("9 key near right left", 3, params.mLeftKeys);
-        assertEquals("9 key near right right", 2, params.mRightKeys);
-        assertEquals("9 key near right [1]", 0, params.getColumnPos(0));
-        assertEquals("9 key near right [2]", 1, params.getColumnPos(1));
-        assertEquals("9 key near right [3]", -1, params.getColumnPos(2));
-        assertEquals("9 key near right [4]", -2, params.getColumnPos(3));
-        assertEquals("9 key near right [5]", -3, params.getColumnPos(4));
-        assertEquals("9 key near right [6]", 0, params.getColumnPos(5));
-        assertEquals("9 key near right [7]", 1, params.getColumnPos(6));
-        assertEquals("9 key near right [8]", -1, params.getColumnPos(7));
-        assertEquals("9 key near right [9]", -2, params.getColumnPos(8));
-        assertEquals("9 key near right centering", true, params.mTopRowNeedsCentering);
-        assertEquals("9 key near right default", WIDTH * 3, params.getDefaultKeyCoordX());
+    //     [D] [B] [C]
+    // [A] [8] [6] [7] [9]
+    // [5] [3] [1] [2] [4]
+    public void testLayout13KeyM0() {
+        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(13, MAX_COLUMNS, WIDTH,
+                HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
+        assertEquals("13 key M0 columns", 5, params.mNumColumns);
+        assertEquals("13 key M0 rows", 3, params.mNumRows);
+        assertEquals("13 key M0 left", 2, params.mLeftKeys);
+        assertEquals("13 key M0 right", 3, params.mRightKeys);
+        assertEquals("13 key M0 [1]", 0, params.getColumnPos(0));
+        assertEquals("13 key M0 [2]", 1, params.getColumnPos(1));
+        assertEquals("13 key M0 [3]", -1, params.getColumnPos(2));
+        assertEquals("13 key M0 [4]", 2, params.getColumnPos(3));
+        assertEquals("13 key M0 [5]", -2, params.getColumnPos(4));
+        assertEquals("13 key M0 [6]", 0, params.getColumnPos(5));
+        assertEquals("13 key M0 [7]", 1, params.getColumnPos(6));
+        assertEquals("13 key M0 [8]", -1, params.getColumnPos(7));
+        assertEquals("13 key M0 [9]", 2, params.getColumnPos(8));
+        assertEquals("13 key M0 [A]", -2, params.getColumnPos(9));
+        assertEquals("13 key M0 [B]", 0, params.getColumnPos(10));
+        assertEquals("13 key M0 [C]", 1, params.getColumnPos(11));
+        assertEquals("13 key M0 [D]", -1, params.getColumnPos(12));
+        assertEquals("13 key M0 adjust", 0, params.mTopRowAdjustment);
+        assertEquals("13 key M0 default", WIDTH * 2, params.getDefaultKeyCoordX());
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/SuggestHelper.java b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
index 1d0a5b7..5d1ab70 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestHelper.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
@@ -55,7 +55,7 @@
         mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL);
         mKeyDetector.setKeyboard(mKeyboard, 0, 0);
         mKeyDetector.setProximityCorrectionEnabled(true);
-        mKeyDetector.setProximityThreshold(KeyDetector.getMostCommonKeyWidth(mKeyboard));
+        mKeyDetector.setProximityThreshold(mKeyboard.getMostCommonKeyWidth());
     }
 
     public void setCorrectionMode(int correctionMode) {