Make Keyboard object immutable except shift state

This is the first step to implement suggestions pane as mini keyboard.

Bug: 5023981
Change-Id: I90ffbde0fda19b4be68add449310997b56bf6904
diff --git a/java/proguard.flags b/java/proguard.flags
index 7ce6f41..44416ec 100644
--- a/java/proguard.flags
+++ b/java/proguard.flags
@@ -30,3 +30,7 @@
 -keep class com.android.inputmethod.latin.SettingsActivity {
   *;
 }
+
+-keep class com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder$MiniKeyboardLayoutParams {
+  <init>(...);
+}
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index a4aa0c1..8bc7e43 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -27,6 +27,7 @@
 import com.android.inputmethod.keyboard.internal.KeyStyles;
 import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle;
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.keyboard.internal.KeyboardParser;
 import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException;
 import com.android.inputmethod.keyboard.internal.PopupCharactersParser;
@@ -190,11 +191,11 @@
     /**
      * This constructor is being used only for key in popup mini keyboard.
      */
-    public Key(Resources res, Keyboard keyboard, CharSequence popupCharacter, int x, int y,
+    public Key(Resources res, KeyboardParams params, CharSequence popupCharacter, int x, int y,
             int width, int height, int edgeFlags) {
-        mHeight = height - keyboard.getVerticalGap();
-        mHorizontalGap = keyboard.getHorizontalGap();
-        mVerticalGap = keyboard.getVerticalGap();
+        mHeight = height - params.mVerticalGap;
+        mHorizontalGap = params.mHorizontalGap;
+        mVerticalGap = params.mVerticalGap;
         mVisualInsetsLeft = mVisualInsetsRight = 0;
         mWidth = width - mHorizontalGap;
         mEdgeFlags = edgeFlags;
@@ -209,8 +210,8 @@
         mLabel = PopupCharactersParser.getLabel(popupSpecification);
         mOutputText = PopupCharactersParser.getOutputText(popupSpecification);
         final int code = PopupCharactersParser.getCode(res, popupSpecification);
-        mCode = keyboard.isRtlKeyboard() ? getRtlParenthesisCode(code) : code;
-        mIcon = keyboard.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification));
+        mCode = params.mIsRtlKeyboard ? getRtlParenthesisCode(code) : code;
+        mIcon = params.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification));
         // Horizontal gap is divided equally to both sides of the key.
         mX = x + mHorizontalGap / 2;
         mY = y;
@@ -220,16 +221,15 @@
      * Create a key with the given top-left coordinate and extract its attributes from the XML
      * parser.
      * @param res resources associated with the caller's context
-     * @param row the row that this key belongs to. The row must already be attached to
-     * a {@link Keyboard}.
+     * @param params the keyboard building parameters.
+     * @param row the row that this key belongs to.
      * @param x the x coordinate of the top-left
      * @param y the y coordinate of the top-left
      * @param parser the XML parser containing the attributes for this key
      * @param keyStyles active key styles set
      */
-    public Key(Resources res, Row row, int x, int y, XmlResourceParser parser,
-            KeyStyles keyStyles) {
-        final Keyboard keyboard = row.getKeyboard();
+    public Key(Resources res, KeyboardParams params, Row row, int x, int y,
+            XmlResourceParser parser, KeyStyles keyStyles) {
 
         final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard);
@@ -237,14 +237,14 @@
         try {
             mHeight = KeyboardParser.getDimensionOrFraction(keyboardAttr,
                     R.styleable.Keyboard_rowHeight,
-                    keyboard.getKeyboardHeight(), row.mDefaultHeight) - keyboard.getVerticalGap();
+                    params.mHeight, row.mRowHeight) - params.mVerticalGap;
             mHorizontalGap = KeyboardParser.getDimensionOrFraction(keyboardAttr,
                     R.styleable.Keyboard_horizontalGap,
-                    keyboard.getDisplayWidth(), keyboard.getHorizontalGap());
-            mVerticalGap = keyboard.getVerticalGap();
+                    params.mWidth, params.mHorizontalGap);
+            mVerticalGap = params.mVerticalGap;
             keyWidth = KeyboardParser.getDimensionOrFraction(keyboardAttr,
                     R.styleable.Keyboard_keyWidth,
-                    keyboard.getDisplayWidth(), row.mDefaultWidth);
+                    params.mWidth, row.mDefaultKeyWidth);
         } finally {
             keyboardAttr.recycle();
         }
@@ -262,7 +262,7 @@
                 style = keyStyles.getEmptyKeyStyle();
             }
 
-            final int keyboardWidth = keyboard.getDisplayWidth();
+            final int keyboardWidth = params.mOccupiedWidth;
             int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr,
                     R.styleable.Keyboard_Key_keyXPos, keyboardWidth, x);
             if (keyXPos < 0) {
@@ -293,13 +293,13 @@
 
             CharSequence[] popupCharacters = style.getTextArray(
                     keyAttr, R.styleable.Keyboard_Key_popupCharacters);
-            if (keyboard.mId.mPasswordInput) {
+            if (params.mId.mPasswordInput) {
                 popupCharacters = PopupCharactersParser.filterOut(
                         res, popupCharacters, PopupCharactersParser.NON_ASCII_FILTER);
             }
             // In Arabic symbol layouts, we'd like to keep digits in popup characters regardless of
             // config_digit_popup_characters_enabled.
-            if (keyboard.mId.isAlphabetKeyboard() && !res.getBoolean(
+            if (params.mId.isAlphabetKeyboard() && !res.getBoolean(
                     R.bool.config_digit_popup_characters_enabled)) {
                 mPopupCharacters = PopupCharactersParser.filterOut(
                         res, popupCharacters, PopupCharactersParser.DIGIT_FILTER);
@@ -308,7 +308,7 @@
             }
             mMaxPopupColumn = style.getInt(keyboardAttr,
                     R.styleable.Keyboard_Key_maxPopupKeyboardColumn,
-                    keyboard.getMaxPopupKeyboardColumn());
+                    params.mMaxPopupColumn);
 
             mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false);
             mFunctional = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isFunctional, false);
@@ -316,7 +316,7 @@
             mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true);
             mEdgeFlags = 0;
 
-            final KeyboardIconsSet iconsSet = keyboard.mIconsSet;
+            final KeyboardIconsSet iconsSet = params.mIconsSet;
             mVisualInsetsLeft = KeyboardParser.getDimensionOrFraction(keyAttr,
                     R.styleable.Keyboard_Key_visualInsetsLeft, keyboardWidth, 0);
             mVisualInsetsRight = KeyboardParser.getDimensionOrFraction(keyAttr,
@@ -331,7 +331,7 @@
                     KeyboardIconsSet.ICON_UNDEFINED);
             if (shiftedIconId != KeyboardIconsSet.ICON_UNDEFINED) {
                 final Drawable shiftedIcon = iconsSet.getIcon(shiftedIconId);
-                keyboard.addShiftedIcon(this, shiftedIcon);
+                params.addShiftedIcon(this, shiftedIcon);
             }
             mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
 
@@ -344,15 +344,12 @@
                     Keyboard.CODE_UNSPECIFIED);
             if (code == Keyboard.CODE_UNSPECIFIED && !TextUtils.isEmpty(mLabel)) {
                 final int firstChar = mLabel.charAt(0);
-                mCode = keyboard.isRtlKeyboard() ? getRtlParenthesisCode(firstChar) : firstChar;
+                mCode = params.mIsRtlKeyboard ? getRtlParenthesisCode(firstChar) : firstChar;
             } else if (code != Keyboard.CODE_UNSPECIFIED) {
                 mCode = code;
             } else {
                 mCode = Keyboard.CODE_DUMMY;
             }
-            if (mCode == Keyboard.CODE_SHIFT) {
-                keyboard.addShiftKey(this);
-            }
         } finally {
             keyAttr.recycle();
         }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 6d25025..53d46a3 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -57,7 +57,7 @@
         mCorrectionX = (int)correctionX;
         mCorrectionY = (int)correctionY;
         mKeyboard = keyboard;
-        final int threshold = keyboard.getMostCommonKeyWidth();
+        final int threshold = keyboard.mMostCommonKeyWidth;
         mProximityThresholdSquare = threshold * threshold;
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index ae5bc95..809c949 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -16,23 +16,14 @@
 
 package com.android.inputmethod.keyboard;
 
-import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.keyboard.internal.KeyboardParser;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.keyboard.internal.KeyboardShiftState;
-import com.android.inputmethod.latin.R;
 
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -56,8 +47,6 @@
  * </pre>
  */
 public class Keyboard {
-    private static final String TAG = Keyboard.class.getSimpleName();
-
     public static final int EDGE_LEFT = 0x01;
     public static final int EDGE_RIGHT = 0x02;
     public static final int EDGE_TOP = 0x04;
@@ -98,209 +87,77 @@
     public final KeyboardId mId;
 
     /** Total height of the keyboard, including the padding and keys */
-    private int mTotalHeight;
+    public final int mOccupiedHeight;
+    /** Total width of the keyboard, including the padding and keys */
+    public final int mOccupiedWidth;
 
-    /**
-     * Total width (minimum width) of the keyboard, including left side gaps and keys, but not any
-     * gaps on the right side.
-     */
-    private int mMinWidth;
-
-    /** Horizontal gap default for all rows */
-    private int mHorizontalGap;
-
-    /** Default key width */
-    private int mDefaultKeyWidth;
+    public final int mHeight;
+    public final int mWidth;
 
     /** Default row height */
-    private int mDefaultRowHeight;
+    public final int mDefaultRowHeight;
 
     /** Default gap between rows */
-    private int mVerticalGap;
+    public final int mVerticalGap;
+
+    public final int mMostCommonKeyWidth;
 
     /** Popup keyboard template */
-    private int mPopupKeyboardResId;
+    public final int mPopupKeyboardResId;
 
     /** Maximum column for popup keyboard */
-    private int mMaxPopupColumn;
+    public final int mMaxPopupColumn;
 
     /** True if Right-To-Left keyboard */
-    private boolean mIsRtlKeyboard;
+    public final boolean mIsRtlKeyboard;
 
-    /** List of keys in this keyboard */
-    private final List<Key> mKeys = new ArrayList<Key>();
-    /** List of shift keys in this keyboard and its icons and state */
-    private final List<Key> mShiftKeys = new ArrayList<Key>();
-    private final Map<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>();
-    private final Map<Key, Drawable> mUnshiftedIcons = new HashMap<Key, Drawable>();
-    private final Set<Key> mShiftLockKeys = new HashSet<Key>();
-    public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
-
-
-    /** Width of the screen available to fit the keyboard */
-    private final int mDisplayWidth;
-
-    /** Height of the screen */
-    private final int mDisplayHeight;
-
-    /** Height of keyboard */
-    private int mKeyboardHeight;
-
-    private int mMostCommonKeyWidth = 0;
+    /** List of keys and icons in this keyboard */
+    public final List<Key> mKeys;
+    public final List<Key> mShiftKeys;
+    public final Set<Key> mShiftLockKeys;
+    public final Map<Key, Drawable> mShiftedIcons;
+    public final Map<Key, Drawable> mUnshiftedIcons;
+    public final KeyboardIconsSet mIconsSet;
 
     private final KeyboardShiftState mShiftState = new KeyboardShiftState();
 
-    // Variables for pre-computing nearest keys.
-
-    // TODO: Change GRID_WIDTH and GRID_HEIGHT to private.
-    public final int GRID_WIDTH;
-    public final int GRID_HEIGHT;
-
     private final ProximityInfo mProximityInfo;
 
-    /**
-     * Creates a keyboard from the given xml key layout file.
-     * @param context the application or service context
-     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
-     * @param id keyboard identifier
-     * @param width keyboard width
-     */
+    public Keyboard(KeyboardParams params) {
+        mId = params.mId;
+        mOccupiedHeight = params.mOccupiedHeight;
+        mOccupiedWidth = params.mOccupiedWidth;
+        mHeight = params.mHeight;
+        mWidth = params.mWidth;
+        mMostCommonKeyWidth = params.mMostCommonKeyWidth;
+        mIsRtlKeyboard = params.mIsRtlKeyboard;
+        mPopupKeyboardResId = params.mPopupKeyboardResId;
+        mMaxPopupColumn = params.mMaxPopupColumn;
 
-    public Keyboard(Context context, int xmlLayoutResId, KeyboardId id, int width) {
-        final Resources res = context.getResources();
-        GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
-        GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
+        mDefaultRowHeight = params.mDefaultRowHeight;
+        mVerticalGap = params.mVerticalGap;
 
-        final int horizontalEdgesPadding = (int)res.getDimension(
-                R.dimen.keyboard_horizontal_edges_padding);
-        mDisplayWidth = width - horizontalEdgesPadding * 2;
-        // TODO: Adjust the height by referring to the height of area available for drawing as well.
-        mDisplayHeight = res.getDisplayMetrics().heightPixels;
+        mKeys = Collections.unmodifiableList(params.mKeys);
+        mShiftKeys = Collections.unmodifiableList(params.mShiftKeys);
+        mShiftLockKeys = Collections.unmodifiableSet(params.mShiftLockKeys);
+        mShiftedIcons = Collections.unmodifiableMap(params.mShiftedIcons);
+        mUnshiftedIcons = Collections.unmodifiableMap(params.mUnshiftedIcons);
+        mIconsSet = params.mIconsSet;
 
-        mHorizontalGap = 0;
-        setKeyWidth(mDisplayWidth / 10);
-        mVerticalGap = 0;
-        mDefaultRowHeight = mDefaultKeyWidth;
-        mId = id;
-        loadKeyboard(context, xmlLayoutResId);
         mProximityInfo = new ProximityInfo(
-                GRID_WIDTH, GRID_HEIGHT, getMinWidth(), getHeight(), getKeyWidth(), mKeys);
+                params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
+                mMostCommonKeyWidth, mKeys);
     }
 
     public int getProximityInfo() {
         return mProximityInfo.getNativeProximityInfo();
     }
 
+    // TODO: Access mKeys directly
     public List<Key> getKeys() {
         return mKeys;
     }
 
-    public int getHorizontalGap() {
-        return mHorizontalGap;
-    }
-
-    public void setHorizontalGap(int gap) {
-        mHorizontalGap = gap;
-    }
-
-    public int getVerticalGap() {
-        return mVerticalGap;
-    }
-
-    public void setVerticalGap(int gap) {
-        mVerticalGap = gap;
-    }
-
-    public int getRowHeight() {
-        return mDefaultRowHeight;
-    }
-
-    public void setRowHeight(int height) {
-        mDefaultRowHeight = height;
-    }
-
-    public int getKeyWidth() {
-        return mDefaultKeyWidth;
-    }
-
-    public void setKeyWidth(int width) {
-        mDefaultKeyWidth = width;
-    }
-
-    /**
-     * Returns the total height of the keyboard
-     * @return the total height of the keyboard
-     */
-    public int getHeight() {
-        return mTotalHeight;
-    }
-
-    public void setHeight(int height) {
-        mTotalHeight = height;
-    }
-
-    public int getMinWidth() {
-        return mMinWidth;
-    }
-
-    public void setMinWidth(int minWidth) {
-        mMinWidth = minWidth;
-    }
-
-    public int getDisplayHeight() {
-        return mDisplayHeight;
-    }
-
-    public int getDisplayWidth() {
-        return mDisplayWidth;
-    }
-
-    public int getKeyboardHeight() {
-        return mKeyboardHeight;
-    }
-
-    public void setKeyboardHeight(int height) {
-        mKeyboardHeight = height;
-    }
-
-    public boolean isRtlKeyboard() {
-        return mIsRtlKeyboard;
-    }
-
-    public void setRtlKeyboard(boolean isRtl) {
-        mIsRtlKeyboard = isRtl;
-    }
-
-    public int getPopupKeyboardResId() {
-        return mPopupKeyboardResId;
-    }
-
-    public void setPopupKeyboardResId(int resId) {
-        mPopupKeyboardResId = resId;
-    }
-
-    public int getMaxPopupKeyboardColumn() {
-        return mMaxPopupColumn;
-    }
-
-    public void setMaxPopupKeyboardColumn(int column) {
-        mMaxPopupColumn = column;
-    }
-
-    public void addShiftKey(Key key) {
-        if (key == null) return;
-        mShiftKeys.add(key);
-        if (key.mSticky) {
-            mShiftLockKeys.add(key);
-        }
-    }
-
-    public void addShiftedIcon(Key key, Drawable icon) {
-        if (key == null) return;
-        mUnshiftedIcons.put(key, key.getIcon());
-        mShiftedIcons.put(key, icon);
-    }
-
     public boolean hasShiftLockKey() {
         return !mShiftLockKeys.isEmpty();
     }
@@ -391,46 +248,4 @@
     public int[] getNearestKeys(int x, int y) {
         return mProximityInfo.getNearestKeys(x, y);
     }
-
-    /**
-     * 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.mHorizontalGap;
-                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;
-    }
-
-    private void loadKeyboard(Context context, int xmlLayoutResId) {
-        try {
-            KeyboardParser parser = new KeyboardParser(this, context);
-            parser.parseKeyboard(xmlLayoutResId);
-            // mMinWidth is the width of this keyboard which is maximum width of row.
-            mMinWidth = parser.getMaxRowWidth();
-            mTotalHeight = parser.getTotalHeight();
-        } catch (XmlPullParserException e) {
-            Log.w(TAG, "keyboard XML parse error: " + e);
-            throw new IllegalArgumentException(e);
-        } catch (IOException e) {
-            Log.w(TAG, "keyboard XML parse error: " + e);
-            throw new RuntimeException(e);
-        }
-    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index 9299c6c..d0a2f86 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -42,8 +42,6 @@
     public static final int F2KEY_MODE_SHORTCUT_IME = 2;
     public static final int F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS = 3;
 
-    private static final int MINI_KEYBOARD_ID_MARKER = -1;
-
     public final Locale mLocale;
     public final int mOrientation;
     public final int mWidth;
@@ -110,9 +108,9 @@
         });
     }
 
-    public KeyboardId cloneAsMiniKeyboard() {
-        return new KeyboardId("mini popup keyboard", MINI_KEYBOARD_ID_MARKER, mLocale, mOrientation,
-                mWidth, mMode, mAttribute, false, F2KEY_MODE_NONE, false, false, false);
+    public KeyboardId cloneWithNewXml(String xmlName, int xmlId) {
+        return new KeyboardId(xmlName, xmlId, mLocale, mOrientation, mWidth, mMode, mAttribute,
+                false, F2KEY_MODE_NONE, false, false, false);
     }
 
     public KeyboardId cloneWithNewGeometry(int orientation, int width) {
@@ -127,10 +125,6 @@
         return mXmlId;
     }
 
-    public boolean isMiniKeyboard() {
-        return mXmlId == MINI_KEYBOARD_ID_MARKER;
-    }
-
     public boolean isAlphabetKeyboard() {
         return mXmlId == R.xml.kbd_qwerty;
     }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 17fdd0c..f45e810 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -29,7 +29,6 @@
 import android.view.inputmethod.EditorInfo;
 
 import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
-import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
 import com.android.inputmethod.keyboard.internal.ModifierKeyState;
 import com.android.inputmethod.keyboard.internal.ShiftKeyState;
 import com.android.inputmethod.latin.LatinIME;
@@ -270,14 +269,17 @@
         if (keyboard == null) {
             final Locale savedLocale = Utils.setSystemLocale(
                     mResources, mSubtypeSwitcher.getInputLocale());
-
-            keyboard = new LatinKeyboard(mThemeContext, id, id.mWidth);
+            try {
+                keyboard = new LatinKeyboard.Builder(mThemeContext).load(id).build();
+            } finally {
+                Utils.setSystemLocale(mResources, savedLocale);
+            }
             mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard));
-            if (DEBUG_CACHE)
+
+            if (DEBUG_CACHE) {
                 Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
                         + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
-
-            Utils.setSystemLocale(mResources, savedLocale);
+            }
         } else if (DEBUG_CACHE) {
             Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id);
         }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index e237258..0fb5109 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -357,7 +357,7 @@
         mDirtyRect.set(0, 0, getWidth(), getHeight());
         mBufferNeedsUpdate = true;
         invalidateAllKeys();
-        final int keyHeight = keyboard.getRowHeight() - keyboard.getVerticalGap();
+        final int keyHeight = keyboard.mDefaultRowHeight - keyboard.mVerticalGap;
         mKeyDrawParams.updateKeyHeight(keyHeight);
         mKeyPreviewDrawParams.updateKeyHeight(keyHeight);
     }
@@ -396,7 +396,7 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (mKeyboard != null) {
             // The main keyboard expands to the display width.
-            final int height = mKeyboard.getKeyboardHeight() + getPaddingTop() + getPaddingBottom();
+            final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
             setMeasuredDimension(widthMeasureSpec, height);
         } else {
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index 3c27129..9a13608 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -31,13 +31,14 @@
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
+import com.android.inputmethod.keyboard.internal.KeyboardParser;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SubtypeSwitcher;
 
 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
@@ -73,32 +74,16 @@
     private static final String SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "small";
     private static final String MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "medium";
 
-    public LatinKeyboard(Context context, KeyboardId id, int width) {
-        super(context, id.getXmlId(), id, width);
+    private LatinKeyboard(Context context, LatinKeyboardParams params) {
+        super(params);
         mRes = context.getResources();
         mTheme = context.getTheme();
 
-        final List<Key> keys = getKeys();
-        int spaceKeyIndex = -1;
-        int shortcutKeyIndex = -1;
-        final int keyCount = keys.size();
-        for (int index = 0; index < keyCount; index++) {
-            // For now, assuming there are up to one space key and one shortcut key respectively.
-            switch (keys.get(index).mCode) {
-            case CODE_SPACE:
-                spaceKeyIndex = index;
-                break;
-            case CODE_SHORTCUT:
-                shortcutKeyIndex = index;
-                break;
-            }
-        }
-
         // The index of space key is available only after Keyboard constructor has finished.
-        mSpaceKey = (spaceKeyIndex >= 0) ? keys.get(spaceKeyIndex) : null;
+        mSpaceKey = params.mSpaceKey;
         mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon() : null;
 
-        mShortcutKey = (shortcutKeyIndex >= 0) ? keys.get(shortcutKeyIndex) : null;
+        mShortcutKey = params.mShortcutKey;
         mEnabledShortcutIcon = (mShortcutKey != null) ? mShortcutKey.getIcon() : null;
 
         final TypedArray a = context.obtainStyledAttributes(
@@ -114,6 +99,42 @@
         a.recycle();
     }
 
+    private static class LatinKeyboardParams extends KeyboardParams {
+        public Key mSpaceKey = null;
+        public Key mShortcutKey = null;
+
+        @Override
+        public void onAddKey(Key key) {
+            super.onAddKey(key);
+
+            switch (key.mCode) {
+            case Keyboard.CODE_SPACE:
+                mSpaceKey = key;
+                break;
+            case Keyboard.CODE_SHORTCUT:
+                mShortcutKey = key;
+                break;
+            }
+        }
+    }
+
+    public static class Builder extends KeyboardParser<LatinKeyboardParams> {
+        public Builder(Context context) {
+            super(context, new LatinKeyboardParams());
+        }
+
+        @Override
+        public Builder load(KeyboardId id) {
+            super.load(id);
+            return this;
+        }
+
+        @Override
+        public LatinKeyboard build() {
+            return new LatinKeyboard(mContext, mParams);
+        }
+    }
+
     public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboardView view) {
         mSpacebarTextFadeFactor = fadeFactor;
         updateSpacebarForLocale(false);
@@ -294,8 +315,8 @@
     @Override
     public int[] getNearestKeys(int x, int y) {
         // Avoid dead pixels at edges of the keyboard
-        return super.getNearestKeys(Math.max(0, Math.min(x, getMinWidth() - 1)),
-                Math.max(0, Math.min(y, getHeight() - 1)));
+        return super.getNearestKeys(Math.max(0, Math.min(x, mOccupiedWidth - 1)),
+                Math.max(0, Math.min(y, mOccupiedHeight - 1)));
     }
 
     public static int getTextSizeFromTheme(Theme theme, int style, int defValue) {
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
index b397ca7..abf28c7 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
@@ -289,7 +289,7 @@
         super.setKeyboard(keyboard);
         mKeyDetector.setKeyboard(
                 keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
-        mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth());
+        mKeyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth);
         PointerTracker.setKeyDetector(mKeyDetector);
         mPopupPanelCache.clear();
     }
@@ -360,7 +360,7 @@
                 (PopupMiniKeyboardView)container.findViewById(R.id.mini_keyboard_view);
         final Keyboard parentKeyboard = getKeyboard();
         final Keyboard miniKeyboard = new MiniKeyboardBuilder(
-                this, parentKeyboard.getPopupKeyboardResId(), parentKey, parentKeyboard).build();
+                this, parentKeyboard.mPopupKeyboardResId, parentKey, parentKeyboard).build();
         miniKeyboardView.setKeyboard(miniKeyboard);
 
         container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index b78fd94..0409677 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -69,7 +69,7 @@
     public void setKeyboard(Keyboard newKeyboard) {
         super.setKeyboard(newKeyboard);
         // One-seventh of the keyboard width seems like a reasonable threshold
-        final int jumpThreshold = newKeyboard.getMinWidth() / 7;
+        final int jumpThreshold = newKeyboard.mOccupiedWidth / 7;
         mJumpThresholdSquare = jumpThreshold * jumpThreshold;
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
index 95e3275..7f5339d 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
@@ -16,30 +16,14 @@
 
 package com.android.inputmethod.keyboard;
 
-import android.content.Context;
+import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder.MiniKeyboardLayoutParams;
 
 public class MiniKeyboard extends Keyboard {
-    private int mDefaultKeyCoordX;
+    private final int mDefaultKeyCoordX;
 
-    public MiniKeyboard(Context context, int xmlLayoutResId, Keyboard parentKeyboard) {
-        super(context, xmlLayoutResId, parentKeyboard.mId.cloneAsMiniKeyboard(),
-                parentKeyboard.getMinWidth());
-        // HACK: Current mini keyboard design totally relies on the 9-patch padding about horizontal
-        // and vertical key spacing. To keep the visual of mini keyboard as is, these hacks are
-        // needed to keep having the same horizontal and vertical key spacing.
-        setHorizontalGap(0);
-        setVerticalGap(parentKeyboard.getVerticalGap() / 2);
-
-        // TODO: When we have correctly padded key background 9-patch drawables for mini keyboard,
-        // revert the above hacks and uncomment the following lines.
-        //setHorizontalGap(parentKeyboard.getHorizontalGap());
-        //setVerticalGap(parentKeyboard.getVerticalGap());
-
-        setRtlKeyboard(parentKeyboard.isRtlKeyboard());
-    }
-
-    public void setDefaultCoordX(int pos) {
-        mDefaultKeyCoordX = pos;
+    public MiniKeyboard(MiniKeyboardLayoutParams params) {
+        super(params);
+        mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2;
     }
 
     public int getDefaultCoordX() {
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index b25754d..3d8a9cf 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -286,7 +286,7 @@
         mKeyDetector = keyDetector;
         mKeyboard = keyDetector.getKeyboard();
         mKeys = mKeyboard.getKeys();
-        final int keyQuarterWidth = mKeyboard.getKeyWidth() / 4;
+        final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4;
         mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
index 2741ee8..dfaaa70 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
@@ -108,8 +108,8 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final Keyboard keyboard = getKeyboard();
         if (keyboard != null) {
-            final int width = keyboard.getMinWidth() + getPaddingLeft() + getPaddingRight();
-            final int height = keyboard.getKeyboardHeight() + getPaddingTop() + getPaddingBottom();
+            final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
+            final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
             setMeasuredDimension(width, height);
         } else {
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -170,9 +170,9 @@
         final int miniKeyboardLeft = pointX - miniKeyboard.getDefaultCoordX()
                 + parentKeyboardView.getPaddingLeft();
         final int x = Math.max(0, Math.min(miniKeyboardLeft,
-                parentKeyboardView.getWidth() - miniKeyboard.getMinWidth()))
+                parentKeyboardView.getWidth() - miniKeyboard.mOccupiedWidth))
                 - container.getPaddingLeft() + mCoordinates[0];
-        final int y = pointY - parentKeyboard.getVerticalGap()
+        final int y = pointY - parentKeyboard.mVerticalGap
                 - (container.getMeasuredHeight() - container.getPaddingBottom())
                 + parentKeyboardView.getPaddingTop() + mCoordinates[1];
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
index 58d7420..4ccaa72 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
@@ -19,6 +19,7 @@
 import android.graphics.drawable.Drawable;
 
 import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardId;
 
 import java.util.ArrayList;
@@ -31,8 +32,8 @@
 public class KeyboardParams {
     public KeyboardId mId;
 
-    public int mTotalHeight;
-    public int mTotalWidth;
+    public int mOccupiedHeight;
+    public int mOccupiedWidth;
 
     public int mHeight;
     public int mWidth;
@@ -61,40 +62,34 @@
     public final Map<Key, Drawable> mUnshiftedIcons = new HashMap<Key, Drawable>();
     public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
 
-    public void addShiftKey(Key key) {
-        if (key == null) return;
-        mShiftKeys.add(key);
-        if (key.mSticky) {
-            mShiftLockKeys.add(key);
+    public int mMostCommonKeyWidth = 0;
+
+    public void onAddKey(Key key) {
+        mKeys.add(key);
+        updateHistogram(key);
+        if (key.mCode == Keyboard.CODE_SHIFT) {
+            mShiftKeys.add(key);
+            if (key.mSticky) {
+                mShiftLockKeys.add(key);
+            }
         }
     }
 
     public void addShiftedIcon(Key key, Drawable icon) {
-        if (key == null) return;
         mUnshiftedIcons.put(key, key.getIcon());
         mShiftedIcons.put(key, icon);
     }
 
-    /**
-     * Compute the most common key width in order to use it as proximity key detection threshold.
-     *
-     * @return The most common key width in the keyboard
-     */
-    public int getMostCommonKeyWidth() {
-        final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
-        int maxCount = 0;
-        int mostCommonWidth = 0;
-        for (final Key key : mKeys) {
-            final Integer width = key.mWidth + key.mHorizontalGap;
-            Integer count = histogram.get(width);
-            if (count == null)
-                count = 0;
-            histogram.put(width, ++count);
-            if (count > maxCount) {
-                maxCount = count;
-                mostCommonWidth = width;
-            }
+    private int mMaxCount = 0;
+    private final Map<Integer, Integer> mHistogram = new HashMap<Integer, Integer>();
+
+    private void updateHistogram(Key key) {
+        final Integer width = key.mWidth + key.mHorizontalGap;
+        final int count = (mHistogram.containsKey(width) ? mHistogram.get(width) : 0) + 1;
+        mHistogram.put(width, count);
+        if (count > mMaxCount) {
+            mMaxCount = count;
+            mMostCommonKeyWidth = width;
         }
-        return mostCommonWidth;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
index e3238c3..42e290f 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
@@ -20,6 +20,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
 import android.util.Xml;
@@ -107,7 +108,7 @@
  * </pre>
  */
 
-public class KeyboardParser {
+public class KeyboardParser<KP extends KeyboardParams> {
     private static final String TAG = KeyboardParser.class.getSimpleName();
     private static final boolean DEBUG = false;
 
@@ -123,40 +124,52 @@
     private static final String TAG_DEFAULT = "default";
     public static final String TAG_KEY_STYLE = "key-style";
 
-    private final Keyboard mKeyboard;
-    private final Context mContext;
-    private final Resources mResources;
+    protected final KP mParams;
+    protected final Context mContext;
+    protected final Resources mResources;
+    private final DisplayMetrics mDisplayMetrics;
 
-    private int mKeyboardTopPadding;
-    private int mKeyboardBottomPadding;
-    private int mHorizontalEdgesPadding;
     private int mCurrentX = 0;
     private int mCurrentY = 0;
-    private int mMaxRowWidth = 0;
-    private int mTotalHeight = 0;
     private Row mCurrentRow = null;
     private boolean mLeftEdge;
     private Key mRightEdgeKey = null;
     private final KeyStyles mKeyStyles = new KeyStyles();
 
-    public KeyboardParser(Keyboard keyboard, Context context) {
-        mKeyboard = keyboard;
+    public KeyboardParser(Context context, KP params) {
         mContext = context;
         final Resources res = context.getResources();
         mResources = res;
-        mHorizontalEdgesPadding = (int)res.getDimension(R.dimen.keyboard_horizontal_edges_padding);
+        mDisplayMetrics = res.getDisplayMetrics();
+
+        mParams = params;
+        mParams.mHorizontalEdgesPadding = (int)res.getDimension(
+                R.dimen.keyboard_horizontal_edges_padding);
+
+        mParams.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
+        mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
     }
 
-    public int getMaxRowWidth() {
-        return mMaxRowWidth;
+    public KeyboardParser<KP> load(KeyboardId id) {
+        mParams.mId = id;
+        try {
+            parseKeyboard(id.getXmlId());
+        } catch (XmlPullParserException e) {
+            Log.w(TAG, "keyboard XML parse error: " + e);
+            throw new IllegalArgumentException(e);
+        } catch (IOException e) {
+            Log.w(TAG, "keyboard XML parse error: " + e);
+            throw new RuntimeException(e);
+        }
+        return this;
     }
 
-    public int getTotalHeight() {
-        return mTotalHeight;
+    public Keyboard build() {
+        return new Keyboard(mParams);
     }
 
-    public void parseKeyboard(int resId) throws XmlPullParserException, IOException {
-        if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mKeyboard.mId));
+    private void parseKeyboard(int resId) throws XmlPullParserException, IOException {
+        if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId));
         final XmlResourceParser parser = mResources.getXml(resId);
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -165,7 +178,7 @@
                 if (TAG_KEYBOARD.equals(tag)) {
                     parseKeyboardAttributes(parser);
                     startKeyboard();
-                    parseKeyboardContent(parser, mKeyboard.getKeys());
+                    parseKeyboardContent(parser, false);
                     break;
                 } else {
                     throw new IllegalStartTag(parser, TAG_KEYBOARD);
@@ -196,15 +209,14 @@
     }
 
     private void parseKeyboardAttributes(XmlResourceParser parser) {
-        final Keyboard keyboard = mKeyboard;
-        final int displayWidth = keyboard.getDisplayWidth();
+        final int displayWidth = mDisplayMetrics.widthPixels;
         final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
                 Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
                 R.style.Keyboard);
         final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard_Key);
         try {
-            final int displayHeight = keyboard.getDisplayHeight();
+            final int displayHeight = mDisplayMetrics.heightPixels;
             final int keyboardHeight = (int)keyboardAttr.getDimension(
                     R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
             final int maxKeyboardHeight = getDimensionOrFraction(keyboardAttr,
@@ -219,37 +231,41 @@
             }
             // Keyboard height will not exceed maxKeyboardHeight and will not be less than
             // minKeyboardHeight.
-            final int height = Math.max(
+            mParams.mOccupiedHeight = Math.max(
                     Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
+            mParams.mOccupiedWidth = mParams.mId.mWidth;
+            mParams.mTopPadding = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_keyboardTopPadding, mParams.mOccupiedHeight, 0);
+            mParams.mBottomPadding = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_keyboardBottomPadding, mParams.mOccupiedHeight, 0);
 
-            keyboard.setKeyboardHeight(height);
-            keyboard.setRtlKeyboard(keyboardAttr.getBoolean(
-                    R.styleable.Keyboard_isRtlKeyboard, false));
-            keyboard.setKeyWidth(getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_keyWidth, displayWidth, displayWidth / 10));
-            keyboard.setRowHeight(getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_rowHeight, height, 50));
-            keyboard.setHorizontalGap(getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_horizontalGap, displayWidth, 0));
-            keyboard.setVerticalGap(getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_verticalGap, height, 0));
-            keyboard.setPopupKeyboardResId(keyboardAttr.getResourceId(
-                    R.styleable.Keyboard_popupKeyboardTemplate, 0));
-            keyboard.setMaxPopupKeyboardColumn(keyAttr.getInt(
-                    R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5));
+            final int height = mParams.mOccupiedHeight;
+            final int width = mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding * 2
+                    - mParams.mHorizontalCenterPadding;
+            mParams.mHeight = height;
+            mParams.mWidth = width;
+            mParams.mDefaultKeyWidth = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_keyWidth, width, width / 10);
+            mParams.mDefaultRowHeight = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_rowHeight, height, height / 4);
+            mParams.mHorizontalGap = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_horizontalGap, width, 0);
+            mParams.mVerticalGap = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_verticalGap, height, 0);
 
-            mKeyboard.mIconsSet.loadIcons(keyboardAttr);
-            mKeyboardTopPadding = getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_keyboardTopPadding, height, 0);
-            mKeyboardBottomPadding = getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_keyboardBottomPadding, height, 0);
+            mParams.mIsRtlKeyboard = keyboardAttr.getBoolean(R.styleable.Keyboard_isRtlKeyboard, false);
+            mParams.mPopupKeyboardResId = keyboardAttr.getResourceId(
+                    R.styleable.Keyboard_popupKeyboardTemplate, 0);
+            mParams.mMaxPopupColumn = keyAttr.getInt(R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5);
+
+            mParams.mIconsSet.loadIcons(keyboardAttr);
         } finally {
             keyAttr.recycle();
             keyboardAttr.recycle();
         }
     }
 
-    private void parseKeyboardContent(XmlResourceParser parser, List<Key> keys)
+    private void parseKeyboardContent(XmlResourceParser parser, boolean skip)
             throws XmlPullParserException, IOException {
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -258,22 +274,22 @@
                 if (TAG_ROW.equals(tag)) {
                     Row row = parseRowAttributes(parser);
                     if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW));
-                    if (keys != null)
+                    if (!skip)
                         startRow(row);
-                    parseRowContent(parser, row, keys);
+                    parseRowContent(parser, row, skip);
                 } else if (TAG_INCLUDE.equals(tag)) {
-                    parseIncludeKeyboardContent(parser, keys);
+                    parseIncludeKeyboardContent(parser, skip);
                 } else if (TAG_SWITCH.equals(tag)) {
-                    parseSwitchKeyboardContent(parser, keys);
+                    parseSwitchKeyboardContent(parser, skip);
                 } else if (TAG_KEY_STYLE.equals(tag)) {
-                    parseKeyStyle(parser, keys);
+                    parseKeyStyle(parser, skip);
                 } else {
                     throw new IllegalStartTag(parser, TAG_ROW);
                 }
             } else if (event == XmlPullParser.END_TAG) {
                 final String tag = parser.getName();
                 if (TAG_KEYBOARD.equals(tag)) {
-                    endKeyboard(mKeyboard.getVerticalGap());
+                    endKeyboard();
                     break;
                 } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
                         || TAG_MERGE.equals(tag)) {
@@ -296,28 +312,28 @@
                 throw new IllegalAttribute(parser, "horizontalGap");
             if (a.hasValue(R.styleable.Keyboard_verticalGap))
                 throw new IllegalAttribute(parser, "verticalGap");
-            return new Row(mResources, mKeyboard, parser);
+            return new Row(mResources, mParams, parser);
         } finally {
             a.recycle();
         }
     }
 
-    private void parseRowContent(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseRowContent(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_KEY.equals(tag)) {
-                    parseKey(parser, row, keys);
+                    parseKey(parser, row, skip);
                 } else if (TAG_SPACER.equals(tag)) {
-                    parseSpacer(parser, row, keys);
+                    parseSpacer(parser, row, skip);
                 } else if (TAG_INCLUDE.equals(tag)) {
-                    parseIncludeRowContent(parser, row, keys);
+                    parseIncludeRowContent(parser, row, skip);
                 } else if (TAG_SWITCH.equals(tag)) {
-                    parseSwitchRowContent(parser, row, keys);
+                    parseSwitchRowContent(parser, row, skip);
                 } else if (TAG_KEY_STYLE.equals(tag)) {
-                    parseKeyStyle(parser, keys);
+                    parseKeyStyle(parser, skip);
                 } else {
                     throw new IllegalStartTag(parser, TAG_KEY);
                 }
@@ -325,7 +341,7 @@
                 final String tag = parser.getName();
                 if (TAG_ROW.equals(tag)) {
                     if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW));
-                    if (keys != null)
+                    if (!skip)
                         endRow();
                     break;
                 } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
@@ -341,24 +357,24 @@
         }
     }
 
-    private void parseKey(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseKey(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
-        if (keys == null) {
+        if (skip) {
             checkEndTag(TAG_KEY, parser);
         } else {
-            Key key = new Key(mResources, row, mCurrentX, mCurrentY, parser, mKeyStyles);
+            Key key = new Key(mResources, mParams, row, mCurrentX, mCurrentY, parser, mKeyStyles);
             if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d popupCharacters=%s />",
                     TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode,
                     Arrays.toString(key.mPopupCharacters)));
             checkEndTag(TAG_KEY, parser);
-            keys.add(key);
+            mParams.onAddKey(key);
             endKey(key);
         }
     }
 
-    private void parseSpacer(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseSpacer(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
-        if (keys == null) {
+        if (skip) {
             checkEndTag(TAG_SPACER, parser);
         } else {
             if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER));
@@ -366,9 +382,9 @@
                     R.styleable.Keyboard);
             if (keyboardAttr.hasValue(R.styleable.Keyboard_horizontalGap))
                 throw new IllegalAttribute(parser, "horizontalGap");
-            final int keyboardWidth = mKeyboard.getDisplayWidth();
+            final int keyboardWidth = mParams.mWidth;
             final int keyWidth = getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_keyWidth,
-                    keyboardWidth, row.mDefaultWidth);
+                    keyboardWidth, row.mDefaultKeyWidth);
             keyboardAttr.recycle();
 
             final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
@@ -385,19 +401,19 @@
         }
     }
 
-    private void parseIncludeKeyboardContent(XmlResourceParser parser, List<Key> keys)
+    private void parseIncludeKeyboardContent(XmlResourceParser parser, boolean skip)
             throws XmlPullParserException, IOException {
-        parseIncludeInternal(parser, null, keys);
+        parseIncludeInternal(parser, null, skip);
     }
 
-    private void parseIncludeRowContent(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseIncludeRowContent(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
-        parseIncludeInternal(parser, row, keys);
+        parseIncludeInternal(parser, row, skip);
     }
 
-    private void parseIncludeInternal(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseIncludeInternal(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
-        if (keys == null) {
+        if (skip) {
             checkEndTag(TAG_INCLUDE, parser);
         } else {
             final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
@@ -411,11 +427,11 @@
                 throw new ParseException("No keyboardLayout attribute in <include/>", parser);
             if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />",
                     TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout)));
-            parseMerge(mResources.getLayout(keyboardLayout), row, keys);
+            parseMerge(mResources.getLayout(keyboardLayout), row, skip);
         }
     }
 
-    private void parseMerge(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseMerge(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -423,9 +439,9 @@
                 final String tag = parser.getName();
                 if (TAG_MERGE.equals(tag)) {
                     if (row == null) {
-                        parseKeyboardContent(parser, keys);
+                        parseKeyboardContent(parser, skip);
                     } else {
-                        parseRowContent(parser, row, keys);
+                        parseRowContent(parser, row, skip);
                     }
                     break;
                 } else {
@@ -436,28 +452,28 @@
         }
     }
 
-    private void parseSwitchKeyboardContent(XmlResourceParser parser, List<Key> keys)
+    private void parseSwitchKeyboardContent(XmlResourceParser parser, boolean skip)
             throws XmlPullParserException, IOException {
-        parseSwitchInternal(parser, null, keys);
+        parseSwitchInternal(parser, null, skip);
     }
 
-    private void parseSwitchRowContent(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseSwitchRowContent(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
-        parseSwitchInternal(parser, row, keys);
+        parseSwitchInternal(parser, row, skip);
     }
 
-    private void parseSwitchInternal(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseSwitchInternal(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
-        if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mKeyboard.mId));
+        if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mParams.mId));
         boolean selected = false;
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_CASE.equals(tag)) {
-                    selected |= parseCase(parser, row, selected ? null : keys);
+                    selected |= parseCase(parser, row, selected ? true : skip);
                 } else if (TAG_DEFAULT.equals(tag)) {
-                    selected |= parseDefault(parser, row, selected ? null : keys);
+                    selected |= parseDefault(parser, row, selected ? true : skip);
                 } else {
                     throw new IllegalStartTag(parser, TAG_KEY);
                 }
@@ -473,21 +489,21 @@
         }
     }
 
-    private boolean parseCase(XmlResourceParser parser, Row row, List<Key> keys)
+    private boolean parseCase(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         final boolean selected = parseCaseCondition(parser);
         if (row == null) {
             // Processing Rows.
-            parseKeyboardContent(parser, selected ? keys : null);
+            parseKeyboardContent(parser, selected ? skip : true);
         } else {
             // Processing Keys.
-            parseRowContent(parser, row, selected ? keys : null);
+            parseRowContent(parser, row, selected ? skip : true);
         }
         return selected;
     }
 
     private boolean parseCaseCondition(XmlResourceParser parser) {
-        final KeyboardId id = mKeyboard.mId;
+        final KeyboardId id = mParams.mId;
         if (id == null)
             return true;
 
@@ -596,18 +612,18 @@
         return false;
     }
 
-    private boolean parseDefault(XmlResourceParser parser, Row row, List<Key> keys)
+    private boolean parseDefault(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT));
         if (row == null) {
-            parseKeyboardContent(parser, keys);
+            parseKeyboardContent(parser, skip);
         } else {
-            parseRowContent(parser, row, keys);
+            parseRowContent(parser, row, skip);
         }
         return true;
     }
 
-    private void parseKeyStyle(XmlResourceParser parser, List<Key> keys) {
+    private void parseKeyStyle(XmlResourceParser parser, boolean skip) {
         TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard_KeyStyle);
         TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
@@ -616,7 +632,7 @@
             if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
                 throw new ParseException("<" + TAG_KEY_STYLE
                         + "/> needs styleName attribute", parser);
-            if (keys != null)
+            if (!skip)
                 mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
         } finally {
             keyStyleAttr.recycle();
@@ -632,12 +648,12 @@
     }
 
     private void startKeyboard() {
-        mCurrentY += mKeyboardTopPadding;
+        mCurrentY += mParams.mTopPadding;
     }
 
     private void startRow(Row row) {
         mCurrentX = 0;
-        setSpacer(mCurrentX, mHorizontalEdgesPadding);
+        setSpacer(mCurrentX, mParams.mHorizontalEdgesPadding);
         mCurrentRow = row;
         mLeftEdge = true;
         mRightEdgeKey = null;
@@ -650,10 +666,8 @@
             mRightEdgeKey.addEdgeFlags(Keyboard.EDGE_RIGHT);
             mRightEdgeKey = null;
         }
-        setSpacer(mCurrentX, mHorizontalEdgesPadding);
-        if (mCurrentX > mMaxRowWidth)
-            mMaxRowWidth = mCurrentX;
-        mCurrentY += mCurrentRow.mDefaultHeight;
+        setSpacer(mCurrentX, mParams.mHorizontalEdgesPadding);
+        mCurrentY += mCurrentRow.mRowHeight;
         mCurrentRow = null;
     }
 
@@ -666,9 +680,7 @@
         mRightEdgeKey = key;
     }
 
-    private void endKeyboard(int defaultVerticalGap) {
-        mCurrentY += mKeyboardBottomPadding;
-        mTotalHeight = mCurrentY - defaultVerticalGap;
+    private void endKeyboard() {
     }
 
     private void setSpacer(int keyXPos, int width) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
index 58862f8..bad7a17 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
@@ -16,8 +16,6 @@
 
 package com.android.inputmethod.keyboard.internal;
 
-import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Paint;
 import android.graphics.Rect;
 
@@ -27,26 +25,30 @@
 import com.android.inputmethod.keyboard.MiniKeyboard;
 import com.android.inputmethod.latin.R;
 
-import java.util.List;
-
-public class MiniKeyboardBuilder {
-    private final Resources mRes;
-    private final MiniKeyboard mKeyboard;
+public class MiniKeyboardBuilder extends
+        KeyboardParser<MiniKeyboardBuilder.MiniKeyboardLayoutParams> {
     private final CharSequence[] mPopupCharacters;
-    private final MiniKeyboardLayoutParams mParams;
 
-    /* package */ static class MiniKeyboardLayoutParams {
-        public final int mKeyWidth;
-        public final int mRowHeight;
-        /* package */ final int mTopRowAdjustment;
-        public final int mNumRows;
-        public final int mNumColumns;
-        public final int mLeftKeys;
-        public final int mRightKeys; // includes default key.
-        public int mTopPadding;
+    public static class MiniKeyboardLayoutParams extends KeyboardParams {
+        /* package */ int mTopRowAdjustment;
+        public int mNumRows;
+        public int mNumColumns;
+        public int mLeftKeys;
+        public int mRightKeys; // includes default key.
+
+        public MiniKeyboardLayoutParams() {
+            super();
+        }
+
+        /* package for test */ MiniKeyboardLayoutParams(int numKeys, int maxColumns, int keyWidth,
+                int rowHeight, int coordXInParent, int parentKeyboardWidth) {
+            super();
+            setParameters(
+                    numKeys, maxColumns, keyWidth, rowHeight, coordXInParent, parentKeyboardWidth);
+        }
 
         /**
-         * The object holding mini keyboard layout parameters.
+         * Set keyboard parameters of mini keyboard.
          *
          * @param numKeys number of keys in this mini keyboard.
          * @param maxColumns number of maximum columns of this mini keyboard.
@@ -54,15 +56,15 @@
          * @param rowHeight mini keyboard row height in pixel, including vertical gap.
          * @param coordXInParent coordinate x of the popup key in parent keyboard.
          * @param parentKeyboardWidth parent keyboard width in pixel.
-         * parent keyboard.
          */
-        public MiniKeyboardLayoutParams(int numKeys, int maxColumns, int keyWidth, int rowHeight,
+        public void setParameters(int numKeys, int maxColumns, int keyWidth, int rowHeight,
                 int coordXInParent, int parentKeyboardWidth) {
-            if (parentKeyboardWidth / keyWidth < maxColumns)
+            if (parentKeyboardWidth / keyWidth < maxColumns) {
                 throw new IllegalArgumentException("Keyboard is too small to hold mini keyboard: "
                         + parentKeyboardWidth + " " + keyWidth + " " + maxColumns);
-            mKeyWidth = keyWidth;
-            mRowHeight = rowHeight;
+            }
+            mDefaultKeyWidth = keyWidth;
+            mDefaultRowHeight = rowHeight;
 
             final int numRows = (numKeys + maxColumns - 1) / maxColumns;
             mNumRows = numRows;
@@ -108,6 +110,9 @@
             } else {
                 mTopRowAdjustment = -1;
             }
+
+            mWidth = mOccupiedWidth = mNumColumns * mDefaultKeyWidth;
+            mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
         }
 
         // Return key position according to column count (0 is default).
@@ -160,19 +165,19 @@
         }
 
         public int getDefaultKeyCoordX() {
-            return mLeftKeys * mKeyWidth;
+            return mLeftKeys * mDefaultKeyWidth;
         }
 
         public int getX(int n, int row) {
-            final int x = getColumnPos(n) * mKeyWidth + getDefaultKeyCoordX();
+            final int x = getColumnPos(n) * mDefaultKeyWidth + getDefaultKeyCoordX();
             if (isTopRow(row)) {
-                return x + mTopRowAdjustment * (mKeyWidth / 2);
+                return x + mTopRowAdjustment * (mDefaultKeyWidth / 2);
             }
             return x;
         }
 
         public int getY(int row) {
-            return (mNumRows - 1 - row) * mRowHeight + mTopPadding;
+            return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
         }
 
         public int getRowFlags(int row) {
@@ -185,42 +190,32 @@
         private boolean isTopRow(int rowCount) {
             return rowCount == mNumRows - 1;
         }
-
-        public void setTopPadding (int topPadding) {
-            mTopPadding = topPadding;
-        }
-
-        public int getKeyboardHeight() {
-            return mNumRows * mRowHeight + mTopPadding;
-        }
-
-        public int getKeyboardWidth() {
-            return mNumColumns * mKeyWidth;
-        }
     }
 
-    public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key parentKey,
+    public MiniKeyboardBuilder(KeyboardView view, int xmlId, Key parentKey,
             Keyboard parentKeyboard) {
-        final Context context = view.getContext();
-        mRes = context.getResources();
-        final MiniKeyboard keyboard = new MiniKeyboard(
-                context, layoutTemplateResId, parentKeyboard);
-        mKeyboard = keyboard;
+        super(view.getContext(), new MiniKeyboardLayoutParams());
+        load(parentKeyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId));
+
+        // HACK: Current mini keyboard design totally relies on the 9-patch padding about horizontal
+        // and vertical key spacing. To keep the visual of mini keyboard as is, these hacks are
+        // needed to keep having the same horizontal and vertical key spacing.
+        mParams.mHorizontalGap = 0;
+        mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2;
+        // TODO: When we have correctly padded key background 9-patch drawables for mini keyboard,
+        // revert the above hacks and uncomment the following lines.
+        //mParams.mHorizontalGap = parentKeyboard.mHorizontalGap;
+        //mParams.mVerticalGap = parentKeyboard.mVerticalGap;
+
+        mParams.mIsRtlKeyboard = parentKeyboard.mIsRtlKeyboard;
         mPopupCharacters = parentKey.mPopupCharacters;
 
-        final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, keyboard.getKeyWidth());
-        final MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
+        final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, mParams.mDefaultKeyWidth);
+        mParams.setParameters(
                 mPopupCharacters.length, parentKey.mMaxPopupColumn,
-                keyWidth, parentKeyboard.getRowHeight(),
-                parentKey.mX + (parentKey.mWidth + parentKey.mHorizontalGap) / 2 - keyWidth / 2,
+                keyWidth, parentKeyboard.mDefaultRowHeight,
+                parentKey.mX + (mParams.mDefaultKeyWidth - keyWidth) / 2,
                 view.getMeasuredWidth());
-        params.setTopPadding(keyboard.getVerticalGap());
-        mParams = params;
-
-        keyboard.setRowHeight(params.mRowHeight);
-        keyboard.setKeyboardHeight(params.getKeyboardHeight());
-        keyboard.setMinWidth(params.getKeyboardWidth());
-        keyboard.setDefaultCoordX(params.getDefaultKeyCoordX() + params.mKeyWidth / 2);
     }
 
     private static int getMaxKeyWidth(KeyboardView view, CharSequence[] popupCharacters,
@@ -249,17 +244,16 @@
         return Math.max(minKeyWidth, maxWidth + horizontalPadding);
     }
 
+    @Override
     public MiniKeyboard build() {
-        final MiniKeyboard keyboard = mKeyboard;
-        final List<Key> keys = keyboard.getKeys();
         final MiniKeyboardLayoutParams params = mParams;
         for (int n = 0; n < mPopupCharacters.length; n++) {
             final CharSequence label = mPopupCharacters[n];
             final int row = n / params.mNumColumns;
-            final Key key = new Key(mRes, keyboard, label, params.getX(n, row), params.getY(row),
-                    params.mKeyWidth, params.mRowHeight, params.getRowFlags(row));
-            keys.add(key);
+            final Key key = new Key(mResources, params, label, params.getX(n, row), params.getY(row),
+                    params.mDefaultKeyWidth, params.mDefaultRowHeight, params.getRowFlags(row));
+            params.onAddKey(key);
         }
-        return keyboard;
+        return new MiniKeyboard(params);
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/Row.java b/java/src/com/android/inputmethod/keyboard/internal/Row.java
index 34b8d91..9299cc2 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/Row.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/Row.java
@@ -31,26 +31,19 @@
  */
 public class Row {
     /** Default width of a key in this row. */
-    public final int mDefaultWidth;
+    public final int mDefaultKeyWidth;
     /** Default height of a key in this row. */
-    public final int mDefaultHeight;
+    public final int mRowHeight;
 
-    private final Keyboard mKeyboard;
-
-    public Row(Resources res, Keyboard keyboard, XmlResourceParser parser) {
-        this.mKeyboard = keyboard;
-        final int keyboardWidth = keyboard.getDisplayWidth();
-        final int keyboardHeight = keyboard.getKeyboardHeight();
+    public Row(Resources res, KeyboardParams params, XmlResourceParser parser) {
+        final int keyboardWidth = params.mWidth;
+        final int keyboardHeight = params.mHeight;
         TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard);
-        mDefaultWidth = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_keyWidth, keyboardWidth, keyboard.getKeyWidth());
-        mDefaultHeight = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_rowHeight, keyboardHeight, keyboard.getRowHeight());
+        mDefaultKeyWidth = KeyboardParser.getDimensionOrFraction(a,
+                R.styleable.Keyboard_keyWidth, keyboardWidth, params.mDefaultKeyWidth);
+        mRowHeight = KeyboardParser.getDimensionOrFraction(a,
+                R.styleable.Keyboard_rowHeight, keyboardHeight, params.mDefaultRowHeight);
         a.recycle();
     }
-
-    public Keyboard getKeyboard() {
-        return mKeyboard;
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index bdcbfbc..b2af6f9 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1685,7 +1685,7 @@
             final int rawPrimaryCode = suggestion.charAt(0);
             // Maybe apply the "bidi mirrored" conversions for parentheses
             final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard();
-            final int primaryCode = keyboard.isRtlKeyboard()
+            final int primaryCode = keyboard.mIsRtlKeyboard
                     ? Key.getRtlParenthesisCode(rawPrimaryCode) : rawPrimaryCode;
 
             final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : "";