Merge "Allow use of assets for data in the dictionary content provider."
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 70f1d48..305bb4f 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -48,7 +48,7 @@
     <string name="bigram_suggestion" msgid="1323347224043514969">"Προτάσεις bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Χρήση προηγούμενης λέξης για τη βελτίωση πρότασης"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Αποθηκεύτηκε"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"Μετάβαση"</string>
+    <string name="label_go_key" msgid="1635148082137219148">"Μετ."</string>
     <string name="label_next_key" msgid="362972844525672568">"Επόμενο"</string>
     <string name="label_done_key" msgid="2441578748772529288">"Τέλος"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Αποστολή"</string>
diff --git a/java/res/values-fr/donottranslate-altchars.xml b/java/res/values-fr/donottranslate-altchars.xml
index e01f63f..ae9292f 100644
--- a/java/res/values-fr/donottranslate-altchars.xml
+++ b/java/res/values-fr/donottranslate-altchars.xml
@@ -18,11 +18,11 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">1,à,â,æ,á,ä,ã,å,ā,ª</string>
-    <string name="alternates_for_e">3,é,è,ê,ë,ę,ė,ē</string>
-    <string name="alternates_for_i">8,î,ï,ì,í,į,ī</string>
-    <string name="alternates_for_o">9,ô,œ,ö,ò,ó,õ,ø,ō,º</string>
-    <string name="alternates_for_u">7,û,ù,ü,ú,ū</string>
+    <string name="alternates_for_a">à,â,1,æ,á,ä,ã,å,ā,ª</string>
+    <string name="alternates_for_e">é,è,ê,ë,3,ę,ė,ē</string>
+    <string name="alternates_for_i">î,8,ï,ì,í,į,ī</string>
+    <string name="alternates_for_o">ô,œ,9,ö,ò,ó,õ,ø,ō,º</string>
+    <string name="alternates_for_u">ù,û,7,ü,ú,ū</string>
     <string name="alternates_for_c">ç,ć,č</string>
     <string name="alternates_for_y">6,ÿ</string>
     <string name="alternates_for_q"></string>
diff --git a/java/res/values-hdpi/config.xml b/java/res/values-hdpi/config.xml
new file mode 100644
index 0000000..7333e94
--- /dev/null
+++ b/java/res/values-hdpi/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+    <!--  Screen metrics for logging. 0 = "mdpi", 1 = "hdpi", 2 = "xlarge" -->
+    <integer name="log_screen_metrics">1</integer>
+</resources>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index e52ad2e..0efb45b 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -48,7 +48,7 @@
     <string name="bigram_suggestion" msgid="1323347224043514969">"Digram-suggesties"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Vorig woord gebruiken om suggestie te verbeteren"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: opgeslagen"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"Beginnen"</string>
+    <string name="label_go_key" msgid="1635148082137219148">"Start"</string>
     <string name="label_next_key" msgid="362972844525672568">"Volgende"</string>
     <string name="label_done_key" msgid="2441578748772529288">"Gereed"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Verzenden"</string>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index a0b5df3..c9639a8 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -48,7 +48,7 @@
     <string name="bigram_suggestion" msgid="1323347224043514969">"Sugestii pentru cuvinte de două litere"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Utilizaţi cuvântul anterior pentru a îmbunătăţi sugestia"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: salvat"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"Accesaţi"</string>
+    <string name="label_go_key" msgid="1635148082137219148">"OK"</string>
     <string name="label_next_key" msgid="362972844525672568">"Înainte"</string>
     <string name="label_done_key" msgid="2441578748772529288">"Terminat"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Trimiteţi"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index ce9abf8..5906b58 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -48,7 +48,7 @@
     <string name="bigram_suggestion" msgid="1323347224043514969">"Mga Suhestiyon na Bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Gamitin ang nakaraang salita upang pahusayin ang suhestiyon"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Na-save"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"Pumunta"</string>
+    <string name="label_go_key" msgid="1635148082137219148">"Punta"</string>
     <string name="label_next_key" msgid="362972844525672568">"Susunod"</string>
     <string name="label_done_key" msgid="2441578748772529288">"Tapos na"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Ipadala"</string>
diff --git a/java/res/values-xlarge/config.xml b/java/res/values-xlarge/config.xml
index ed8dc3e..2c125dc 100644
--- a/java/res/values-xlarge/config.xml
+++ b/java/res/values-xlarge/config.xml
@@ -43,4 +43,6 @@
     <string name="config_default_keyboard_theme_id" translatable="false">5</string>
     <string name="config_text_size_of_language_on_spacebar" translatable="false">medium</string>
     <integer name="config_max_popup_keyboard_column">5</integer>
+    <!--  Screen metrics for logging. 0 = "mdpi", 1 = "hdpi", 2 = "xlarge" -->
+    <integer name="log_screen_metrics">2</integer>
 </resources>
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 32fb5bf..64e2c67 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -63,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">
@@ -76,4 +76,6 @@
              will be subject to auto-correction. -->
         <item>0</item>
     </string-array>
+    <!--  Screen metrics for logging. 0 = "mdpi", 1 = "hdpi", 2 = "xlarge" -->
+    <integer name="log_screen_metrics">0</integer>
 </resources>
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 3b923e1..95ec931 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -16,36 +16,35 @@
 
 package com.android.inputmethod.keyboard;
 
+import android.util.Log;
+
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 
-public abstract class KeyDetector {
-    public static final int NOT_A_KEY = -1;
+public class KeyDetector {
+    private static final String TAG = KeyDetector.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
     public static final int NOT_A_CODE = -1;
+    public static final int NOT_A_KEY = -1;
 
-    protected Keyboard mKeyboard;
+    private Keyboard mKeyboard;
+    private int mCorrectionX;
+    private int mCorrectionY;
+    private boolean mProximityCorrectOn;
+    private int mProximityThresholdSquare;
 
-    private Key[] mKeys;
+    // working area
+    private static final int MAX_NEARBY_KEYS = 12;
+    private final int[] mDistances = new int[MAX_NEARBY_KEYS];
+    private final int[] mIndices = new int[MAX_NEARBY_KEYS];
 
-    protected int mCorrectionX;
-
-    protected int mCorrectionY;
-
-    protected boolean mProximityCorrectOn;
-
-    protected int mProximityThresholdSquare;
-
-    public Key[] setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
+    public void setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
         if (keyboard == null)
             throw new NullPointerException();
         mCorrectionX = (int)correctionX;
         mCorrectionY = (int)correctionY;
         mKeyboard = keyboard;
-        List<Key> keys = mKeyboard.getKeys();
-        Key[] array = keys.toArray(new Key[keys.size()]);
-        mKeys = array;
-        return array;
     }
 
     protected int getTouchX(int x) {
@@ -56,11 +55,11 @@
         return y + mCorrectionY;
     }
 
-    protected Key[] getKeys() {
-        if (mKeys == null)
+    protected List<Key> getKeys() {
+        if (mKeyboard == null)
             throw new IllegalStateException("keyboard isn't set");
         // mKeyboard is guaranteed not to be null at setKeybaord() method if mKeys is not null
-        return mKeys;
+        return mKeyboard.getKeys();
     }
 
     public void setProximityCorrectionEnabled(boolean enabled) {
@@ -76,6 +75,17 @@
     }
 
     /**
+     * Computes maximum size of the array that can contain all nearby key indices returned by
+     * {@link #getKeyIndexAndNearbyCodes}.
+     *
+     * @return Returns maximum size of the array that can contain all nearby key indices returned
+     *         by {@link #getKeyIndexAndNearbyCodes}.
+     */
+    protected int getMaxNearbyKeys() {
+        return MAX_NEARBY_KEYS;
+    }
+
+    /**
      * Allocates array that can hold all key indices returned by {@link #getKeyIndexAndNearbyCodes}
      * method. The maximum size of the array should be computed by {@link #getMaxNearbyKeys}.
      *
@@ -89,14 +99,60 @@
         return codes;
     }
 
+    private void initializeNearbyKeys() {
+        Arrays.fill(mDistances, Integer.MAX_VALUE);
+        Arrays.fill(mIndices, NOT_A_KEY);
+    }
+
     /**
-     * Computes maximum size of the array that can contain all nearby key indices returned by
-     * {@link #getKeyIndexAndNearbyCodes}.
+     * Insert the key into nearby keys buffer and sort nearby keys by ascending order of distance.
      *
-     * @return Returns maximum size of the array that can contain all nearby key indices returned
-     *         by {@link #getKeyIndexAndNearbyCodes}.
+     * @param keyIndex index of the key.
+     * @param distance distance between the key's edge and user touched point.
+     * @return order of the key in the nearby buffer, 0 if it is the nearest key.
      */
-    abstract protected int getMaxNearbyKeys();
+    private int sortNearbyKeys(int keyIndex, int distance) {
+        final int[] distances = mDistances;
+        final int[] indices = mIndices;
+        for (int insertPos = 0; insertPos < distances.length; insertPos++) {
+            if (distance < distances[insertPos]) {
+                final int nextPos = insertPos + 1;
+                if (nextPos < distances.length) {
+                    System.arraycopy(distances, insertPos, distances, nextPos,
+                            distances.length - nextPos);
+                    System.arraycopy(indices, insertPos, indices, nextPos,
+                            indices.length - nextPos);
+                }
+                distances[insertPos] = distance;
+                indices[insertPos] = keyIndex;
+                return insertPos;
+            }
+        }
+        return distances.length;
+    }
+
+    private void getNearbyKeyCodes(final int[] allCodes) {
+        final List<Key> keys = getKeys();
+        final int[] indices = mIndices;
+
+        // allCodes[0] should always have the key code even if it is a non-letter key.
+        if (indices[0] == NOT_A_KEY) {
+            allCodes[0] = NOT_A_CODE;
+            return;
+        }
+
+        int numCodes = 0;
+        for (int j = 0; j < indices.length && numCodes < allCodes.length; j++) {
+            final int index = indices[j];
+            if (index == NOT_A_KEY)
+                break;
+            final int code = keys.get(index).mCode;
+            // filter out a non-letter key from nearby keys
+            if (code < Keyboard.CODE_SPACE)
+                continue;
+            allCodes[numCodes++] = code;
+        }
+    }
 
     /**
      * Finds all possible nearby key indices around a touch event point and returns the nearest key
@@ -109,32 +165,34 @@
      * @param allCodes All nearby key code except functional key are returned in this array
      * @return The nearest key index
      */
-    abstract public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes);
+    public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
+        final List<Key> keys = getKeys();
+        final int touchX = getTouchX(x);
+        final int touchY = getTouchY(y);
 
-    /**
-     * 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;
+        initializeNearbyKeys();
+        int primaryIndex = NOT_A_KEY;
+        for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) {
+            final Key key = keys.get(index);
+            final boolean isInside = key.isInside(touchX, touchY);
+            final int distance = key.squaredDistanceToEdge(touchX, touchY);
+            if (isInside || (mProximityCorrectOn && distance < mProximityThresholdSquare)) {
+                final int insertedPosition = sortNearbyKeys(index, distance);
+                if (insertedPosition == 0 && isInside)
+                    primaryIndex = index;
             }
         }
-        return mostCommonWidth + keyboard.getHorizontalGap();
+
+        if (allCodes != null && allCodes.length > 0) {
+            getNearbyKeyCodes(allCodes);
+            if (DEBUG) {
+                Log.d(TAG, "x=" + x + " y=" + y
+                        + " primary="
+                        + (primaryIndex == NOT_A_KEY ? "none" : keys.get(primaryIndex).mCode)
+                        + " codes=" + Arrays.toString(allCodes));
+            }
+        }
+
+        return primaryIndex;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index f720334..fdfe282 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -128,6 +128,8 @@
     /** Height of keyboard */
     private int mKeyboardHeight;
 
+    private int mMostCommonKeyWidth = 0;
+
     public final KeyboardId mId;
 
     // Variables for pre-computing nearest keys.
@@ -405,6 +407,41 @@
         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() {
+        if (mMostCommonKeyWidth == 0) {
+            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;
+                }
+            }
+            mMostCommonKeyWidth = mostCommonWidth;
+        }
+        return mMostCommonKeyWidth;
+    }
+
+    /**
+     * Return true if spacebar needs showing preview even when "popup on keypress" is off.
+     * @param keyIndex index of the pressing key
+     * @return true if spacebar needs showing preview
+     */
+    public boolean needSpacebarPreview(int keyIndex) {
+        return false;
+    }
+
     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 dee1913..3550dcb 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -18,7 +18,6 @@
 
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SubtypeSwitcher;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -106,7 +105,6 @@
 
     // Main keyboard
     private Keyboard mKeyboard;
-    private Key[] mKeys;
 
     // Key preview popup
     private boolean mInForeground;
@@ -147,7 +145,7 @@
     // Accessibility
     private boolean mIsAccessibilityEnabled;
 
-    protected KeyDetector mKeyDetector = new ProximityKeyDetector();
+    protected KeyDetector mKeyDetector = new KeyDetector();
 
     // Swipe gesture detector
     private GestureDetector mGestureDetector;
@@ -225,7 +223,7 @@
 
         public void popupPreview(long delay, int keyIndex, PointerTracker tracker) {
             removeMessages(MSG_POPUP_PREVIEW);
-            if (mPreviewText.getVisibility() == VISIBLE) {
+            if (mPreviewText.getVisibility() == VISIBLE || delay == 0) {
                 // Show right away, if it's already visible and finger is moving around
                 showKey(keyIndex, tracker);
             } else {
@@ -493,15 +491,15 @@
         mHandler.cancelPopupPreview();
         mKeyboard = keyboard;
         LatinImeLogger.onSetKeyboard(keyboard);
-        mKeys = mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
+        mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
                 -getPaddingTop() + mVerticalCorrection);
         for (PointerTracker tracker : mPointerTrackers) {
-            tracker.setKeyboard(keyboard, mKeys, mKeyHysteresisDistance);
+            tracker.setKeyboard(keyboard, mKeyHysteresisDistance);
         }
         requestLayout();
         mKeyboardChanged = true;
         invalidateAllKeys();
-        mKeyDetector.setProximityThreshold(KeyDetector.getMostCommonKeyWidth(keyboard));
+        mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth());
         mMiniKeyboardCache.clear();
     }
 
@@ -638,154 +636,32 @@
         }
         final Canvas canvas = mCanvas;
         canvas.clipRect(mDirtyRect, Op.REPLACE);
+        canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
 
         if (mKeyboard == null) return;
 
-        final Paint paint = mPaint;
-        final Drawable keyBackground = mKeyBackground;
-        final Rect padding = mPadding;
-        final int kbdPaddingLeft = getPaddingLeft();
-        final int kbdPaddingTop = getPaddingTop();
-        final Key[] keys = mKeys;
-        final boolean isManualTemporaryUpperCase = mKeyboard.isManualTemporaryUpperCase();
-        final boolean drawSingleKey = (mInvalidatedKey != null
-                && mInvalidatedKeyRect.contains(mDirtyRect));
-
-        canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
-        final int keyCount = keys.length;
-        for (int i = 0; i < keyCount; i++) {
-            final Key key = keys[i];
-            if (drawSingleKey && key != mInvalidatedKey) {
-                continue;
+        if (mInvalidatedKey != null && mInvalidatedKeyRect.contains(mDirtyRect)) {
+            // Draw a single key.
+            onBufferDrawKey(canvas, mInvalidatedKey);
+        } else {
+            // Draw all keys.
+            for (final Key key : mKeyboard.getKeys()) {
+                onBufferDrawKey(canvas, key);
             }
-            int[] drawableState = key.getCurrentDrawableState();
-            keyBackground.setState(drawableState);
-
-            // Switch the character to uppercase if shift is pressed
-            String label = key.mLabel == null? null : adjustCase(key.mLabel).toString();
-
-            final int keyDrawX = key.mX + key.mVisualInsetsLeft;
-            final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
-            final Rect bounds = keyBackground.getBounds();
-            if (keyDrawWidth != bounds.right || key.mHeight != bounds.bottom) {
-                keyBackground.setBounds(0, 0, keyDrawWidth, key.mHeight);
-            }
-            canvas.translate(keyDrawX + kbdPaddingLeft, key.mY + kbdPaddingTop);
-            keyBackground.draw(canvas);
-
-            final int rowHeight = padding.top + key.mHeight;
-            // Draw key label
-            if (label != null) {
-                // For characters, use large font. For labels like "Done", use small font.
-                final int labelSize = getLabelSizeAndSetPaint(label, key.mLabelOption, paint);
-                final int labelCharHeight = getLabelCharHeight(labelSize, paint);
-
-                // Vertical label text alignment.
-                final float baseline;
-                if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_BOTTOM) != 0) {
-                    baseline = key.mHeight -
-                            + labelCharHeight * KEY_LABEL_VERTICAL_PADDING_FACTOR;
-                    if (DEBUG_SHOW_ALIGN)
-                        drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
-                                new Paint());
-                } else { // Align center
-                    final float centerY = (key.mHeight + padding.top - padding.bottom) / 2;
-                    baseline = centerY
-                            + labelCharHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER;
-                    if (DEBUG_SHOW_ALIGN)
-                        drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
-                                new Paint());
-                }
-                // Horizontal label text alignment
-                final int positionX;
-                if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
-                    positionX = mKeyLabelHorizontalPadding + padding.left;
-                    paint.setTextAlign(Align.LEFT);
-                    if (DEBUG_SHOW_ALIGN)
-                        drawVerticalLine(canvas, positionX, rowHeight, 0xc0800080, new Paint());
-                } else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
-                    positionX = keyDrawWidth - mKeyLabelHorizontalPadding - padding.right;
-                    paint.setTextAlign(Align.RIGHT);
-                    if (DEBUG_SHOW_ALIGN)
-                        drawVerticalLine(canvas, positionX, rowHeight, 0xc0808000, new Paint());
-                } else {
-                    positionX = (keyDrawWidth + padding.left - padding.right) / 2;
-                    paint.setTextAlign(Align.CENTER);
-                    if (DEBUG_SHOW_ALIGN) {
-                        if (label.length() > 1)
-                            drawVerticalLine(canvas, positionX, rowHeight, 0xc0008080, new Paint());
-                    }
-                }
-                if (key.mManualTemporaryUpperCaseHintIcon != null && isManualTemporaryUpperCase) {
-                    paint.setColor(mKeyTextColorDisabled);
-                } else {
-                    paint.setColor(mKeyTextColor);
-                }
-                if (key.mEnabled) {
-                    // Set a drop shadow for the text
-                    paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor);
-                } else {
-                    // Make label invisible
-                    paint.setColor(Color.TRANSPARENT);
-                }
-                canvas.drawText(label, positionX, baseline, paint);
-                // Turn off drop shadow
-                paint.setShadowLayer(0, 0, 0, 0);
-            }
-            // Draw key icon
-            final Drawable icon = key.getIcon();
-            if (key.mLabel == null && icon != null) {
-                final int drawableWidth = icon.getIntrinsicWidth();
-                final int drawableHeight = icon.getIntrinsicHeight();
-                final int drawableX;
-                final int drawableY = (
-                        key.mHeight + padding.top - padding.bottom - drawableHeight) / 2;
-                if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
-                    drawableX = padding.left + mKeyLabelHorizontalPadding;
-                    if (DEBUG_SHOW_ALIGN)
-                        drawVerticalLine(canvas, drawableX, rowHeight, 0xc0800080, new Paint());
-                } else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
-                    drawableX = keyDrawWidth - padding.right - mKeyLabelHorizontalPadding
-                            - drawableWidth;
-                    if (DEBUG_SHOW_ALIGN)
-                        drawVerticalLine(canvas, drawableX + drawableWidth, rowHeight,
-                                0xc0808000, new Paint());
-                } else { // Align center
-                    drawableX = (keyDrawWidth + padding.left - padding.right - drawableWidth) / 2;
-                    if (DEBUG_SHOW_ALIGN)
-                        drawVerticalLine(canvas, drawableX + drawableWidth / 2, rowHeight,
-                                0xc0008080, new Paint());
-                }
-                drawIcon(canvas, icon, drawableX, drawableY, drawableWidth, drawableHeight);
-                if (DEBUG_SHOW_ALIGN)
-                    drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
-                            0x80c00000, new Paint());
-            }
-            if (key.mHintIcon != null) {
-                final int drawableWidth = keyDrawWidth;
-                final int drawableHeight = key.mHeight;
-                final int drawableX = 0;
-                final int drawableY = HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL;
-                Drawable hintIcon = (isManualTemporaryUpperCase
-                        && key.mManualTemporaryUpperCaseHintIcon != null)
-                        ? key.mManualTemporaryUpperCaseHintIcon : key.mHintIcon;
-                drawIcon(canvas, hintIcon, drawableX, drawableY, drawableWidth, drawableHeight);
-                if (DEBUG_SHOW_ALIGN)
-                    drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
-                            0x80c0c000, new Paint());
-            }
-            canvas.translate(-keyDrawX - kbdPaddingLeft, -key.mY - kbdPaddingTop);
         }
 
-        // TODO: Move this function to ProximityInfo for getting rid of public declarations for
+        // TODO: Move this function to ProximityInfo for getting rid of
+        // public declarations for
         // GRID_WIDTH and GRID_HEIGHT
         if (DEBUG_KEYBOARD_GRID) {
             Paint p = new Paint();
             p.setStyle(Paint.Style.STROKE);
             p.setStrokeWidth(1.0f);
             p.setColor(0x800000c0);
-            int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1) / mKeyboard.GRID_WIDTH;
-            int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1) / mKeyboard.GRID_HEIGHT;
+            int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1)
+                    / mKeyboard.GRID_WIDTH;
+            int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1)
+                    / mKeyboard.GRID_HEIGHT;
             for (int i = 0; i <= mKeyboard.GRID_WIDTH; i++)
                 canvas.drawLine(i * cw, 0, i * cw, ch * mKeyboard.GRID_HEIGHT, p);
             for (int i = 0; i <= mKeyboard.GRID_HEIGHT; i++)
@@ -794,8 +670,8 @@
 
         // Overlay a dark rectangle to dim the keyboard
         if (mMiniKeyboardView != null) {
-            paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
-            canvas.drawRect(0, 0, width, height, paint);
+            mPaint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
+            canvas.drawRect(0, 0, width, height, mPaint);
         }
 
         mInvalidatedKey = null;
@@ -803,6 +679,134 @@
         mDirtyRect.setEmpty();
     }
 
+    private void onBufferDrawKey(final Canvas canvas, final Key key) {
+        final Paint paint = mPaint;
+        final Drawable keyBackground = mKeyBackground;
+        final Rect padding = mPadding;
+        final int kbdPaddingLeft = getPaddingLeft();
+        final int kbdPaddingTop = getPaddingTop();
+        final int keyDrawX = key.mX + key.mVisualInsetsLeft;
+        final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
+        final int rowHeight = padding.top + key.mHeight;
+        final boolean isManualTemporaryUpperCase = mKeyboard.isManualTemporaryUpperCase();
+
+        canvas.translate(keyDrawX + kbdPaddingLeft, key.mY + kbdPaddingTop);
+
+        // Draw key background.
+        final int[] drawableState = key.getCurrentDrawableState();
+        keyBackground.setState(drawableState);
+        final Rect bounds = keyBackground.getBounds();
+        if (keyDrawWidth != bounds.right || key.mHeight != bounds.bottom) {
+            keyBackground.setBounds(0, 0, keyDrawWidth, key.mHeight);
+        }
+        keyBackground.draw(canvas);
+
+        // Draw key label.
+        if (key.mLabel != null) {
+            // Switch the character to uppercase if shift is pressed
+            final String label = key.mLabel == null ? null : adjustCase(key.mLabel).toString();
+            // For characters, use large font. For labels like "Done", use small font.
+            final int labelSize = getLabelSizeAndSetPaint(label, key.mLabelOption, paint);
+            final int labelCharHeight = getLabelCharHeight(labelSize, paint);
+
+            // Vertical label text alignment.
+            final float baseline;
+            if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_BOTTOM) != 0) {
+                baseline = key.mHeight - labelCharHeight * KEY_LABEL_VERTICAL_PADDING_FACTOR;
+                if (DEBUG_SHOW_ALIGN)
+                    drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
+                            new Paint());
+            } else { // Align center
+                final float centerY = (key.mHeight + padding.top - padding.bottom) / 2;
+                baseline = centerY + labelCharHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER;
+                if (DEBUG_SHOW_ALIGN)
+                    drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
+                            new Paint());
+            }
+            // Horizontal label text alignment
+            final int positionX;
+            if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
+                positionX = mKeyLabelHorizontalPadding + padding.left;
+                paint.setTextAlign(Align.LEFT);
+                if (DEBUG_SHOW_ALIGN)
+                    drawVerticalLine(canvas, positionX, rowHeight, 0xc0800080, new Paint());
+            } else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
+                positionX = keyDrawWidth - mKeyLabelHorizontalPadding - padding.right;
+                paint.setTextAlign(Align.RIGHT);
+                if (DEBUG_SHOW_ALIGN)
+                    drawVerticalLine(canvas, positionX, rowHeight, 0xc0808000, new Paint());
+            } else {
+                positionX = (keyDrawWidth + padding.left - padding.right) / 2;
+                paint.setTextAlign(Align.CENTER);
+                if (DEBUG_SHOW_ALIGN) {
+                    if (label.length() > 1)
+                        drawVerticalLine(canvas, positionX, rowHeight, 0xc0008080, new Paint());
+                }
+            }
+            if (key.mManualTemporaryUpperCaseHintIcon != null && isManualTemporaryUpperCase) {
+                paint.setColor(mKeyTextColorDisabled);
+            } else {
+                paint.setColor(mKeyTextColor);
+            }
+            if (key.mEnabled) {
+                // Set a drop shadow for the text
+                paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor);
+            } else {
+                // Make label invisible
+                paint.setColor(Color.TRANSPARENT);
+            }
+            canvas.drawText(label, positionX, baseline, paint);
+            // Turn off drop shadow
+            paint.setShadowLayer(0, 0, 0, 0);
+        }
+
+        // Draw key icon.
+        final Drawable icon = key.getIcon();
+        if (key.mLabel == null && icon != null) {
+            final int drawableWidth = icon.getIntrinsicWidth();
+            final int drawableHeight = icon.getIntrinsicHeight();
+            final int drawableX;
+            final int drawableY = (key.mHeight + padding.top - padding.bottom - drawableHeight) / 2;
+            if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
+                drawableX = padding.left + mKeyLabelHorizontalPadding;
+                if (DEBUG_SHOW_ALIGN)
+                    drawVerticalLine(canvas, drawableX, rowHeight, 0xc0800080, new Paint());
+            } else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
+                drawableX = keyDrawWidth - padding.right - mKeyLabelHorizontalPadding
+                        - drawableWidth;
+                if (DEBUG_SHOW_ALIGN)
+                    drawVerticalLine(canvas, drawableX + drawableWidth, rowHeight,
+                            0xc0808000, new Paint());
+            } else { // Align center
+                drawableX = (keyDrawWidth + padding.left - padding.right - drawableWidth) / 2;
+                if (DEBUG_SHOW_ALIGN)
+                    drawVerticalLine(canvas, drawableX + drawableWidth / 2, rowHeight,
+                            0xc0008080, new Paint());
+            }
+            drawIcon(canvas, icon, drawableX, drawableY, drawableWidth, drawableHeight);
+            if (DEBUG_SHOW_ALIGN)
+                drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
+                        0x80c00000, new Paint());
+        }
+
+        // Draw hint icon.
+        if (key.mHintIcon != null) {
+            final int drawableWidth = keyDrawWidth;
+            final int drawableHeight = key.mHeight;
+            final int drawableX = 0;
+            final int drawableY = HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL;
+            Drawable hintIcon = (isManualTemporaryUpperCase
+                    && key.mManualTemporaryUpperCaseHintIcon != null)
+                    ? key.mManualTemporaryUpperCaseHintIcon : key.mHintIcon;
+            drawIcon(canvas, hintIcon, drawableX, drawableY, drawableWidth, drawableHeight);
+            if (DEBUG_SHOW_ALIGN)
+                drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
+                        0x80c0c000, new Paint());
+        }
+
+        canvas.translate(-keyDrawX - kbdPaddingLeft, -key.mY - kbdPaddingTop);
+    }
+
     public int getLabelSizeAndSetPaint(CharSequence label, int keyLabelOption, Paint paint) {
         // For characters, use large font. For labels like "Done", use small font.
         final int labelSize;
@@ -884,15 +888,7 @@
     public void showPreview(int keyIndex, PointerTracker tracker) {
         int oldKeyIndex = mOldPreviewKeyIndex;
         mOldPreviewKeyIndex = keyIndex;
-        // We should re-draw popup preview when 1) we need to hide the preview, 2) we will show
-        // the space key preview and 3) pointer moves off the space key to other letter key, we
-        // should hide the preview of the previous key.
-        final boolean hidePreviewOrShowSpaceKeyPreview = (tracker == null)
-                || (SubtypeSwitcher.getInstance().useSpacebarLanguageSwitcher()
-                        && SubtypeSwitcher.getInstance().needsToDisplayLanguage()
-                        && (tracker.isSpaceKey(keyIndex) || tracker.isSpaceKey(oldKeyIndex)));
-        // If key changed and preview is on or the key is space (language switch is enabled)
-        if (oldKeyIndex != keyIndex && (mShowPreview || (hidePreviewOrShowSpaceKeyPreview))) {
+        if ((mShowPreview && oldKeyIndex != keyIndex) || mKeyboard.needSpacebarPreview(keyIndex)) {
             if (keyIndex == KeyDetector.NOT_A_KEY) {
                 mHandler.cancelPopupPreview();
                 mHandler.dismissPreview(mDelayAfterPreview);
@@ -1179,15 +1175,14 @@
 
     private PointerTracker getPointerTracker(final int id) {
         final ArrayList<PointerTracker> pointers = mPointerTrackers;
-        final Key[] keys = mKeys;
         final KeyboardActionListener listener = mKeyboardActionListener;
 
         // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
         for (int i = pointers.size(); i <= id; i++) {
             final PointerTracker tracker =
                 new PointerTracker(i, mHandler, mKeyDetector, this, getResources());
-            if (keys != null)
-                tracker.setKeyboard(mKeyboard, keys, mKeyHysteresisDistance);
+            if (mKeyboard != null)
+                tracker.setKeyboard(mKeyboard, mKeyHysteresisDistance);
             if (listener != null)
                 tracker.setOnKeyboardActionListener(listener);
             pointers.add(tracker);
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index 9d9793e..3a5dbd2 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -26,6 +26,9 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.graphics.Paint.Align;
 import android.graphics.PorterDuff;
@@ -33,15 +36,18 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 
+import java.lang.ref.SoftReference;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 
 // TODO: We should remove this class
 public class LatinKeyboard extends Keyboard {
-    public static final int OPACITY_FULLY_OPAQUE = 255;
     private static final int SPACE_LED_LENGTH_PERCENT = 80;
 
     private final Context mContext;
+    private final SubtypeSwitcher mSubtypeSwitcher = SubtypeSwitcher.getInstance();
 
     /* Space key and its icons, drawables and colors. */
     private final Key mSpaceKey;
@@ -59,14 +65,18 @@
     private int mSpaceDragLastDiff;
     private boolean mCurrentlyInSpace;
     private SlidingLocaleDrawable mSlidingLocaleIcon;
+    private final HashMap<Integer, SoftReference<BitmapDrawable>> mSpaceDrawableCache =
+            new HashMap<Integer, SoftReference<BitmapDrawable>>();
 
     /* Shortcut key and its icons if available */
     private final Key mShortcutKey;
     private final Drawable mEnabledShortcutIcon;
     private final Drawable mDisabledShortcutIcon;
 
-    private static final float SPACEBAR_DRAG_THRESHOLD = 0.8f;
-    // Minimum width of space key preview (proportional to keyboard width)
+    // Minimum width of spacebar dragging to trigger the language switch (represented by the number
+    // of the most common key width of this keyboard).
+    private static final int SPACEBAR_DRAG_WIDTH = 3;
+    // Minimum width of space key preview (proportional to keyboard width).
     private static final float SPACEBAR_POPUP_MIN_RATIO = 0.4f;
     // Height in space key the language name will be drawn. (proportional to space key height)
     public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f;
@@ -137,6 +147,12 @@
         return newColor;
     }
 
+    private static ColorFilter getSpacebarDrawableFilter(float fadeFactor) {
+        final ColorMatrix colorMatrix = new ColorMatrix();
+        colorMatrix.setScale(1, 1, 1, fadeFactor);
+        return new ColorMatrixColorFilter(colorMatrix);
+    }
+
     public void updateShortcutKey(boolean available, LatinKeyboardView view) {
         if (mShortcutKey == null)
             return;
@@ -157,19 +173,14 @@
     private void updateSpacebarForLocale(boolean isAutoCorrection) {
         if (mSpaceKey == null)
             return;
-        final Resources res = mContext.getResources();
         // If application locales are explicitly selected.
-        if (SubtypeSwitcher.getInstance().needsToDisplayLanguage()) {
-            mSpaceKey.setIcon(new BitmapDrawable(res,
-                    drawSpacebar(OPACITY_FULLY_OPAQUE, isAutoCorrection)));
+        if (mSubtypeSwitcher.needsToDisplayLanguage()) {
+            mSpaceKey.setIcon(getSpaceDrawable(
+                    mSubtypeSwitcher.getInputLocale(), isAutoCorrection));
+        } else if (isAutoCorrection) {
+            mSpaceKey.setIcon(getSpaceDrawable(null, true));
         } else {
-            // sym_keyboard_space_led can be shared with Black and White symbol themes.
-            if (isAutoCorrection) {
-                mSpaceKey.setIcon(new BitmapDrawable(res,
-                        drawSpacebar(OPACITY_FULLY_OPAQUE, isAutoCorrection)));
-            } else {
-                mSpaceKey.setIcon(mSpaceIcon);
-            }
+            mSpaceKey.setIcon(mSpaceIcon);
         }
     }
 
@@ -223,19 +234,31 @@
         return language;
     }
 
-    private Bitmap drawSpacebar(int opacity, boolean isAutoCorrection) {
+    private BitmapDrawable getSpaceDrawable(Locale locale, boolean isAutoCorrection) {
+        final Integer hashCode = Arrays.hashCode(
+                new Object[] { locale, isAutoCorrection, mSpacebarTextFadeFactor });
+        final SoftReference<BitmapDrawable> ref = mSpaceDrawableCache.get(hashCode);
+        BitmapDrawable drawable = (ref == null) ? null : ref.get();
+        if (drawable == null) {
+            drawable = new BitmapDrawable(mContext.getResources(), drawSpacebar(
+                    locale, isAutoCorrection, mSpacebarTextFadeFactor));
+            mSpaceDrawableCache.put(hashCode, new SoftReference<BitmapDrawable>(drawable));
+        }
+        return drawable;
+    }
+
+    private Bitmap drawSpacebar(Locale inputLocale, boolean isAutoCorrection,
+            float textFadeFactor) {
         final int width = mSpaceKey.mWidth;
         final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : mSpaceKey.mHeight;
         final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         final Canvas canvas = new Canvas(buffer);
         final Resources res = mContext.getResources();
-        canvas.drawColor(res.getColor(android.R.color.transparent), PorterDuff.Mode.CLEAR);
+        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
 
-        SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
         // If application locales are explicitly selected.
-        if (subtypeSwitcher.needsToDisplayLanguage()) {
+        if (inputLocale != null) {
             final Paint paint = new Paint();
-            paint.setAlpha(opacity);
             paint.setAntiAlias(true);
             paint.setTextAlign(Align.CENTER);
 
@@ -253,7 +276,7 @@
             }
 
             final boolean allowVariableTextSize = true;
-            final String language = layoutSpacebar(paint, subtypeSwitcher.getInputLocale(),
+            final String language = layoutSpacebar(paint, inputLocale,
                     mButtonArrowLeftIcon, mButtonArrowRightIcon, width, height,
                     getTextSizeFromTheme(mContext.getTheme(), textStyle, defaultTextSize),
                     allowVariableTextSize);
@@ -265,14 +288,16 @@
             final float textHeight = -paint.ascent() + descent;
             final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE
                     : height / 2 + textHeight / 2;
-            paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, mSpacebarTextFadeFactor));
+            paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, textFadeFactor));
             canvas.drawText(language, width / 2, baseline - descent - 1, paint);
-            paint.setColor(getSpacebarTextColor(mSpacebarTextColor, mSpacebarTextFadeFactor));
+            paint.setColor(getSpacebarTextColor(mSpacebarTextColor, textFadeFactor));
             canvas.drawText(language, width / 2, baseline - descent, paint);
 
             // Put arrows that are already layed out on either side of the text
-            if (subtypeSwitcher.useSpacebarLanguageSwitcher()
-                    && subtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
+            if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()
+                    && mSubtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
+                mButtonArrowLeftIcon.setColorFilter(getSpacebarDrawableFilter(textFadeFactor));
+                mButtonArrowRightIcon.setColorFilter(getSpacebarDrawableFilter(textFadeFactor));
                 mButtonArrowLeftIcon.draw(canvas);
                 mButtonArrowRightIcon.draw(canvas);
             }
@@ -316,9 +341,25 @@
         mSpaceKey.getPreviewIcon().invalidateSelf();
     }
 
+    // This method is called when "popup on keypress" is off.
+    @Override
+    public boolean needSpacebarPreview(int keyIndex) {
+        if (!mSubtypeSwitcher.useSpacebarLanguageSwitcher())
+            return false;
+        // Dismiss key preview.
+        if (keyIndex == KeyDetector.NOT_A_KEY)
+            return true;
+        // Key is not a spacebar.
+        if (keyIndex != mSpaceKeyIndexArray[0])
+            return false;
+        // The language switcher will be displayed only when the dragging distance is greater
+        // than average key width of this keyboard.
+        return Math.abs(mSpaceDragLastDiff) > getMostCommonKeyWidth();
+    }
+
     public int getLanguageChangeDirection() {
         if (mSpaceKey == null || SubtypeSwitcher.getInstance().getEnabledKeyboardLocaleCount() <= 1
-                || Math.abs(mSpaceDragLastDiff) < mSpaceKey.mWidth * SPACEBAR_DRAG_THRESHOLD) {
+                || Math.abs(mSpaceDragLastDiff) < getMostCommonKeyWidth() * SPACEBAR_DRAG_WIDTH) {
             return 0; // No change
         }
         return mSpaceDragLastDiff > 0 ? 1 : -1;
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/MiniKeyboardKeyDetector.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
index a8750d3..c4459f6 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
@@ -16,9 +16,9 @@
 
 package com.android.inputmethod.keyboard;
 
-public class MiniKeyboardKeyDetector extends KeyDetector {
-    private static final int MAX_NEARBY_KEYS = 1;
+import java.util.List;
 
+public class MiniKeyboardKeyDetector extends KeyDetector {
     private final int mSlideAllowanceSquare;
     private final int mSlideAllowanceSquareTop;
 
@@ -31,20 +31,21 @@
 
     @Override
     protected int getMaxNearbyKeys() {
-        return MAX_NEARBY_KEYS;
+        // No nearby key will be returned.
+        return 1;
     }
 
     @Override
     public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
-        final Key[] keys = getKeys();
+        final List<Key> keys = getKeys();
         final int touchX = getTouchX(x);
         final int touchY = getTouchY(y);
 
         int nearestIndex = NOT_A_KEY;
         int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
-        final int keyCount = keys.length;
+        final int keyCount = keys.size();
         for (int index = 0; index < keyCount; index++) {
-            final int dist = keys[index].squaredDistanceToEdge(touchX, touchY);
+            final int dist = keys.get(index).squaredDistanceToEdge(touchX, touchY);
             if (dist < nearestDist) {
                 nearestIndex = index;
                 nearestDist = dist;
@@ -52,7 +53,7 @@
         }
 
         if (allCodes != null && nearestIndex != NOT_A_KEY)
-            allCodes[0] = keys[nearestIndex].mCode;
+            allCodes[0] = keys.get(nearestIndex).mCode;
         return nearestIndex;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index add38cf..5d137b9 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -25,6 +25,7 @@
 import android.view.MotionEvent;
 
 import java.util.Arrays;
+import java.util.List;
 
 public class PointerTracker {
     private static final String TAG = PointerTracker.class.getSimpleName();
@@ -63,7 +64,7 @@
     private final int mTouchNoiseThresholdDistanceSquared;
 
     private Keyboard mKeyboard;
-    private Key[] mKeys;
+    private List<Key> mKeys;
     private int mKeyHysteresisDistanceSquared = -1;
     private int mKeyQuarterWidthSquared;
 
@@ -109,8 +110,8 @@
         public void onSwipeDown() {}
     };
 
-    public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy,
-            Resources res) {
+    public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector,
+            UIProxy proxy, Resources res) {
         if (proxy == null || handler == null || keyDetector == null)
             throw new NullPointerException();
         mPointerId = id;
@@ -197,11 +198,11 @@
         mListener.onCancelInput();
     }
 
-    public void setKeyboard(Keyboard keyboard, Key[] keys, float keyHysteresisDistance) {
-        if (keyboard == null || keys == null || keyHysteresisDistance < 0)
+    public void setKeyboard(Keyboard keyboard, float keyHysteresisDistance) {
+        if (keyboard == null || keyHysteresisDistance < 0)
             throw new IllegalArgumentException();
         mKeyboard = keyboard;
-        mKeys = keys;
+        mKeys = keyboard.getKeys();
         mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance);
         final int keyQuarterWidth = keyboard.getKeyWidth() / 4;
         mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
@@ -214,11 +215,11 @@
     }
 
     private boolean isValidKeyIndex(int keyIndex) {
-        return keyIndex >= 0 && keyIndex < mKeys.length;
+        return keyIndex >= 0 && keyIndex < mKeys.size();
     }
 
     public Key getKey(int keyIndex) {
-        return isValidKeyIndex(keyIndex) ? mKeys[keyIndex] : null;
+        return isValidKeyIndex(keyIndex) ? mKeys.get(keyIndex) : null;
     }
 
     private static boolean isModifierCode(int primaryCode) {
@@ -258,12 +259,14 @@
         mPreviousKey = keyIndex;
         if (keyIndex != oldKeyIndex) {
             if (isValidKeyIndex(oldKeyIndex)) {
-                mKeys[oldKeyIndex].onReleased();
-                mProxy.invalidateKey(mKeys[oldKeyIndex]);
+                final Key oldKey = mKeys.get(oldKeyIndex);
+                oldKey.onReleased();
+                mProxy.invalidateKey(oldKey);
             }
             if (isValidKeyIndex(keyIndex)) {
-                mKeys[keyIndex].onPressed();
-                mProxy.invalidateKey(mKeys[keyIndex]);
+                final Key newKey = mKeys.get(keyIndex);
+                newKey.onPressed();
+                mProxy.invalidateKey(newKey);
             }
         }
     }
@@ -488,9 +491,6 @@
         if (!mIsRepeatableKey) {
             detectAndSendKey(keyIndex, x, y);
         }
-
-        if (isValidKeyIndex(keyIndex))
-            mProxy.invalidateKey(mKeys[keyIndex]);
     }
 
     public void onCancelEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
@@ -508,9 +508,6 @@
         mHandler.cancelPopupPreview();
         showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY);
         mIsInSlidingKeyInput = false;
-        int keyIndex = mKeyState.getKeyIndex();
-        if (isValidKeyIndex(keyIndex))
-           mProxy.invalidateKey(mKeys[keyIndex]);
     }
 
     public void repeatKey(int keyIndex) {
@@ -539,7 +536,7 @@
         if (newKey == curKey) {
             return true;
         } else if (isValidKeyIndex(curKey)) {
-            return mKeys[curKey].squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared;
+            return mKeys.get(curKey).squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared;
         } else {
             return false;
         }
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java b/java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java
deleted file mode 100644
index c3fd198..0000000
--- a/java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2010 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.keyboard;
-
-import android.util.Log;
-
-import java.util.Arrays;
-
-public class ProximityKeyDetector extends KeyDetector {
-    private static final String TAG = ProximityKeyDetector.class.getSimpleName();
-    private static final boolean DEBUG = false;
-
-    private static final int MAX_NEARBY_KEYS = 12;
-
-    // working area
-    private final int[] mDistances = new int[MAX_NEARBY_KEYS];
-    private final int[] mIndices = new int[MAX_NEARBY_KEYS];
-
-    @Override
-    protected int getMaxNearbyKeys() {
-        return MAX_NEARBY_KEYS;
-    }
-
-    private void initializeNearbyKeys() {
-        Arrays.fill(mDistances, Integer.MAX_VALUE);
-        Arrays.fill(mIndices, NOT_A_KEY);
-    }
-
-    /**
-     * Insert the key into nearby keys buffer and sort nearby keys by ascending order of distance.
-     *
-     * @param keyIndex index of the key.
-     * @param distance distance between the key's edge and user touched point.
-     * @return order of the key in the nearby buffer, 0 if it is the nearest key.
-     */
-    private int sortNearbyKeys(int keyIndex, int distance) {
-        final int[] distances = mDistances;
-        final int[] indices = mIndices;
-        for (int insertPos = 0; insertPos < distances.length; insertPos++) {
-            if (distance < distances[insertPos]) {
-                final int nextPos = insertPos + 1;
-                if (nextPos < distances.length) {
-                    System.arraycopy(distances, insertPos, distances, nextPos,
-                            distances.length - nextPos);
-                    System.arraycopy(indices, insertPos, indices, nextPos,
-                            indices.length - nextPos);
-                }
-                distances[insertPos] = distance;
-                indices[insertPos] = keyIndex;
-                return insertPos;
-            }
-        }
-        return distances.length;
-    }
-
-    private void getNearbyKeyCodes(final int[] allCodes) {
-        final Key[] keys = getKeys();
-        final int[] indices = mIndices;
-
-        // allCodes[0] should always have the key code even if it is a non-letter key.
-        if (indices[0] == NOT_A_KEY) {
-            allCodes[0] = NOT_A_CODE;
-            return;
-        }
-
-        int numCodes = 0;
-        for (int j = 0; j < indices.length && numCodes < allCodes.length; j++) {
-            final int index = indices[j];
-            if (index == NOT_A_KEY)
-                break;
-            final int code = keys[index].mCode;
-            // filter out a non-letter key from nearby keys
-            if (code < Keyboard.CODE_SPACE)
-                continue;
-            allCodes[numCodes++] = code;
-        }
-    }
-
-    @Override
-    public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
-        final Key[] keys = getKeys();
-        final int touchX = getTouchX(x);
-        final int touchY = getTouchY(y);
-
-        initializeNearbyKeys();
-        int primaryIndex = NOT_A_KEY;
-        for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) {
-            final Key key = keys[index];
-            final boolean isInside = key.isInside(touchX, touchY);
-            final int distance = key.squaredDistanceToEdge(touchX, touchY);
-            if (isInside || (mProximityCorrectOn && distance < mProximityThresholdSquare)) {
-                final int insertedPosition = sortNearbyKeys(index, distance);
-                if (insertedPosition == 0 && isInside)
-                    primaryIndex = index;
-            }
-        }
-
-        if (allCodes != null && allCodes.length > 0) {
-            getNearbyKeyCodes(allCodes);
-            if (DEBUG) {
-                Log.d(TAG, "x=" + x + " y=" + y
-                        + " primary="
-                        + (primaryIndex == NOT_A_KEY ? "none" : keys[primaryIndex].mCode)
-                        + " codes=" + Arrays.toString(allCodes));
-            }
-        }
-
-        return primaryIndex;
-    }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java b/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java
index eee0ac6..b279c1c 100644
--- a/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java
+++ b/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java
@@ -23,6 +23,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -63,9 +64,8 @@
         mHeight = height;
         final TextPaint textPaint = new TextPaint();
         textPaint.setTextSize(getTextSizeFromTheme(android.R.style.TextAppearance_Medium, 18));
-        textPaint.setColor(android.R.color.transparent);
+        textPaint.setColor(Color.TRANSPARENT);
         textPaint.setTextAlign(Align.CENTER);
-        textPaint.setAlpha(LatinKeyboard.OPACITY_FULLY_OPAQUE);
         textPaint.setAntiAlias(true);
         mTextPaint = textPaint;
         mMiddleX = (mWidth - mBackground.getIntrinsicWidth()) / 2;
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..4a357a6 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestHelper.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
@@ -20,7 +20,6 @@
 import com.android.inputmethod.keyboard.KeyDetector;
 import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.LatinKeyboard;
-import com.android.inputmethod.keyboard.ProximityKeyDetector;
 
 import android.content.Context;
 import android.text.TextUtils;
@@ -38,7 +37,7 @@
         // (and not try to find a dictionary provider for a specified locale)
         mSuggest = new Suggest(context, dictionaryId, null);
         mKeyboard = new LatinKeyboard(context, keyboardId);
-        mKeyDetector = new ProximityKeyDetector();
+        mKeyDetector = new KeyDetector();
         init();
     }
 
@@ -46,7 +45,7 @@
             KeyboardId keyboardId) {
         mSuggest = new Suggest(dictionaryPath, startOffset, length, null);
         mKeyboard = new LatinKeyboard(context, keyboardId);
-        mKeyDetector = new ProximityKeyDetector();
+        mKeyDetector = new KeyDetector();
         init();
     }
 
@@ -55,7 +54,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) {