From 4e1dab8cfaad891fe041ed8d71893186c05cef71 Mon Sep 17 00:00:00 2001
From: "Tadashi G. Takaoka" <takaoka@google.com>
Date: Sun, 18 Dec 2011 19:54:08 +0900
Subject: [PATCH] Move KeyboardBuilder and KeyboardParams classes into Keyboard
 class

This change also moves XmlParseUtils to com.android.inputmethod.latin package.

Bug: 5778201
Change-Id: I7d9faa344460753ce178ad4048e0fadb65c75614
---
 .../com/android/inputmethod/keyboard/Key.java |  30 +-
 .../inputmethod/keyboard/Keyboard.java        | 983 +++++++++++++++++-
 .../inputmethod/keyboard/KeyboardSet.java     |   8 +-
 .../inputmethod/keyboard/MiniKeyboard.java    |   6 +-
 .../inputmethod/keyboard/ProximityInfo.java   |   2 +-
 .../keyboard/internal/KeyStyles.java          |   4 +-
 .../keyboard/internal/KeyboardBuilder.java    | 833 ---------------
 .../keyboard/internal/KeyboardParams.java     | 177 ----
 .../internal => latin}/XmlParseUtils.java     |   6 +-
 .../latin/suggestions/MoreSuggestions.java    |   6 +-
 .../inputmethod/latin/SuggestHelper.java      |   6 +-
 11 files changed, 1011 insertions(+), 1050 deletions(-)
 delete mode 100644 java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
 delete mode 100644 java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
 rename java/src/com/android/inputmethod/{keyboard/internal => latin}/XmlParseUtils.java (94%)

diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 5dc02c4a7c..b7d8c4a3e5 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -27,12 +27,10 @@ import android.util.Xml;
 
 import com.android.inputmethod.keyboard.internal.KeyStyles;
 import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle;
-import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.keyboard.internal.MoreKeySpecParser;
-import com.android.inputmethod.keyboard.internal.XmlParseUtils;
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.XmlParseUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -160,19 +158,19 @@ public class Key {
         }
     }
 
-    private static int getCode(Resources res, KeyboardParams params, String moreKeySpec) {
+    private static int getCode(Resources res, Keyboard.Params params, String moreKeySpec) {
         return getRtlParenthesisCode(
                 MoreKeySpecParser.getCode(res, moreKeySpec), params.mIsRtlKeyboard);
     }
 
-    private static Drawable getIcon(KeyboardParams params, String moreKeySpec) {
+    private static Drawable getIcon(Keyboard.Params params, String moreKeySpec) {
         return params.mIconsSet.getIcon(MoreKeySpecParser.getIconId(moreKeySpec));
     }
 
     /**
      * This constructor is being used only for key in more keys keyboard.
      */
-    public Key(Resources res, KeyboardParams params, String moreKeySpec,
+    public Key(Resources res, Keyboard.Params params, String moreKeySpec,
             int x, int y, int width, int height) {
         this(params, MoreKeySpecParser.getLabel(moreKeySpec), null, getIcon(params, moreKeySpec),
                 getCode(res, params, moreKeySpec), MoreKeySpecParser.getOutputText(moreKeySpec),
@@ -182,7 +180,7 @@ public class Key {
     /**
      * This constructor is being used only for key in popup suggestions pane.
      */
-    public Key(KeyboardParams params, CharSequence label, CharSequence hintLabel, Drawable icon,
+    public Key(Keyboard.Params params, CharSequence label, CharSequence hintLabel, Drawable icon,
             int code, CharSequence outputText, int x, int y, int width, int height) {
         mHeight = height - params.mVerticalGap;
         mHorizontalGap = params.mHorizontalGap;
@@ -220,7 +218,7 @@ public class Key {
      * @param keyStyles active key styles set
      * @throws XmlPullParserException
      */
-    public Key(Resources res, KeyboardParams params, KeyboardBuilder.Row row,
+    public Key(Resources res, Keyboard.Params params, Keyboard.Builder.Row row,
             XmlPullParser parser, KeyStyles keyStyles) throws XmlPullParserException {
         final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap;
         final int keyHeight = row.mRowHeight;
@@ -272,9 +270,9 @@ public class Key {
         mActionFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags, 0);
 
         final KeyboardIconsSet iconsSet = params.mIconsSet;
-        mVisualInsetsLeft = (int) KeyboardBuilder.getDimensionOrFraction(keyAttr,
+        mVisualInsetsLeft = (int) Keyboard.Builder.getDimensionOrFraction(keyAttr,
                 R.styleable.Keyboard_Key_visualInsetsLeft, params.mBaseWidth, 0);
-        mVisualInsetsRight = (int) KeyboardBuilder.getDimensionOrFraction(keyAttr,
+        mVisualInsetsRight = (int) Keyboard.Builder.getDimensionOrFraction(keyAttr,
                 R.styleable.Keyboard_Key_visualInsetsRight, params.mBaseWidth, 0);
         mPreviewIcon = iconsSet.getIcon(style.getInt(keyAttr,
                 R.styleable.Keyboard_Key_keyIconPreview, KeyboardIconsSet.ICON_UNDEFINED));
@@ -355,19 +353,19 @@ public class Key {
         return o instanceof Key && equals((Key)o);
     }
 
-    public void markAsLeftEdge(KeyboardParams params) {
+    public void markAsLeftEdge(Keyboard.Params params) {
         mHitBox.left = params.mHorizontalEdgesPadding;
     }
 
-    public void markAsRightEdge(KeyboardParams params) {
+    public void markAsRightEdge(Keyboard.Params params) {
         mHitBox.right = params.mOccupiedWidth - params.mHorizontalEdgesPadding;
     }
 
-    public void markAsTopEdge(KeyboardParams params) {
+    public void markAsTopEdge(Keyboard.Params params) {
         mHitBox.top = params.mTopPadding;
     }
 
-    public void markAsBottomEdge(KeyboardParams params) {
+    public void markAsBottomEdge(Keyboard.Params params) {
         mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding;
     }
 
@@ -603,7 +601,7 @@ public class Key {
     }
 
     public static class Spacer extends Key {
-        public Spacer(Resources res, KeyboardParams params, KeyboardBuilder.Row row,
+        public Spacer(Resources res, Keyboard.Params params, Keyboard.Builder.Row row,
                 XmlPullParser parser, KeyStyles keyStyles) throws XmlPullParserException {
             super(res, params, row, parser, keyStyles);
         }
@@ -611,7 +609,7 @@ public class Key {
         /**
          * This constructor is being used only for divider in more keys keyboard.
          */
-        public Spacer(KeyboardParams params, Drawable icon, int x, int y, int width, int height) {
+        public Spacer(Keyboard.Params params, Drawable icon, int x, int y, int width, int height) {
             super(params, null, null, icon, Keyboard.CODE_DUMMY, null, x, y, width, height);
         }
 
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 8a4bb8ce1e..444360d9a5 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -16,15 +16,33 @@
 
 package com.android.inputmethod.keyboard;
 
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.text.TextUtils;
+import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.view.InflateException;
 
+import com.android.inputmethod.compat.EditorInfoCompatUtils;
+import com.android.inputmethod.keyboard.internal.KeyStyles;
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.keyboard.internal.KeyboardShiftState;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.XmlParseUtils;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
@@ -116,7 +134,7 @@ public class Keyboard {
     // TODO: Remove this variable.
     private final KeyboardShiftState mShiftState = new KeyboardShiftState();
 
-    public Keyboard(KeyboardParams params) {
+    public Keyboard(Params params) {
         mId = params.mId;
         mThemeId = params.mThemeId;
         mOccupiedHeight = params.mOccupiedHeight;
@@ -222,6 +240,159 @@ public class Keyboard {
         return label;
     }
 
+    public static class Params {
+        public KeyboardId mId;
+        public int mThemeId;
+
+        /** Total height and width of the keyboard, including the paddings and keys */
+        public int mOccupiedHeight;
+        public int mOccupiedWidth;
+
+        /** Base height and width of the keyboard used to calculate rows' or keys' heights and
+         *  widths
+         */
+        public int mBaseHeight;
+        public int mBaseWidth;
+
+        public int mTopPadding;
+        public int mBottomPadding;
+        public int mHorizontalEdgesPadding;
+        public int mHorizontalCenterPadding;
+
+        public int mDefaultRowHeight;
+        public int mDefaultKeyWidth;
+        public int mHorizontalGap;
+        public int mVerticalGap;
+
+        public boolean mIsRtlKeyboard;
+        public int mMoreKeysTemplate;
+        public int mMaxMiniKeyboardColumn;
+
+        public int GRID_WIDTH;
+        public int GRID_HEIGHT;
+
+        public final Set<Key> mKeys = new HashSet<Key>();
+        public final Set<Key> mShiftKeys = new HashSet<Key>();
+        public final Set<Key> mShiftLockKeys = new HashSet<Key>();
+        public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
+
+        public int mMostCommonKeyHeight = 0;
+        public int mMostCommonKeyWidth = 0;
+
+        public final TouchPositionCorrection mTouchPositionCorrection =
+                new TouchPositionCorrection();
+
+        public static class TouchPositionCorrection {
+            private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3;
+
+            public boolean mEnabled;
+            public float[] mXs;
+            public float[] mYs;
+            public float[] mRadii;
+
+            public void load(String[] data) {
+                final int dataLength = data.length;
+                if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) {
+                    if (LatinImeLogger.sDBG)
+                        throw new RuntimeException(
+                                "the size of touch position correction data is invalid");
+                    return;
+                }
+
+                final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
+                mXs = new float[length];
+                mYs = new float[length];
+                mRadii = new float[length];
+                try {
+                    for (int i = 0; i < dataLength; ++i) {
+                        final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE;
+                        final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
+                        final float value = Float.parseFloat(data[i]);
+                        if (type == 0) {
+                            mXs[index] = value;
+                        } else if (type == 1) {
+                            mYs[index] = value;
+                        } else {
+                            mRadii[index] = value;
+                        }
+                    }
+                } catch (NumberFormatException e) {
+                    if (LatinImeLogger.sDBG) {
+                        throw new RuntimeException(
+                                "the number format for touch position correction data is invalid");
+                    }
+                    mXs = null;
+                    mYs = null;
+                    mRadii = null;
+                }
+            }
+
+            public void setEnabled(boolean enabled) {
+                mEnabled = enabled;
+            }
+
+            public boolean isValid() {
+                return mEnabled && mXs != null && mYs != null && mRadii != null
+                    && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0;
+            }
+        }
+
+        protected void clearKeys() {
+            mKeys.clear();
+            mShiftKeys.clear();
+            mShiftLockKeys.clear();
+            clearHistogram();
+        }
+
+        public void onAddKey(Key key) {
+            mKeys.add(key);
+            updateHistogram(key);
+            if (key.mCode == Keyboard.CODE_SHIFT) {
+                mShiftKeys.add(key);
+                if (key.isSticky()) {
+                    mShiftLockKeys.add(key);
+                }
+            }
+        }
+
+        private int mMaxHeightCount = 0;
+        private int mMaxWidthCount = 0;
+        private final Map<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>();
+        private final Map<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>();
+
+        private void clearHistogram() {
+            mMostCommonKeyHeight = 0;
+            mMaxHeightCount = 0;
+            mHeightHistogram.clear();
+
+            mMaxWidthCount = 0;
+            mMostCommonKeyWidth = 0;
+            mWidthHistogram.clear();
+        }
+
+        private static int updateHistogramCounter(Map<Integer, Integer> histogram, Integer key) {
+            final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1;
+            histogram.put(key, count);
+            return count;
+        }
+
+        private void updateHistogram(Key key) {
+            final Integer height = key.mHeight + key.mVerticalGap;
+            final int heightCount = updateHistogramCounter(mHeightHistogram, height);
+            if (heightCount > mMaxHeightCount) {
+                mMaxHeightCount = heightCount;
+                mMostCommonKeyHeight = height;
+            }
+
+            final Integer width = key.mWidth + key.mHorizontalGap;
+            final int widthCount = updateHistogramCounter(mWidthHistogram, width);
+            if (widthCount > mMaxWidthCount) {
+                mMaxWidthCount = widthCount;
+                mMostCommonKeyWidth = width;
+            }
+        }
+    }
+
     /**
      * Returns the array of the keys that are closest to the given point.
      * @param x the x-coordinate of the point
@@ -248,4 +419,812 @@ public class Keyboard {
             return String.format("\\u04x", code);
         }
     }
+
+    /**
+     * Keyboard Building helper.
+     *
+     * This class parses Keyboard XML file and eventually build a Keyboard.
+     * The Keyboard XML file looks like:
+     * <pre>
+     *   &gt;!-- xml/keyboard.xml --&lt;
+     *   &gt;Keyboard keyboard_attributes*&lt;
+     *     &gt;!-- Keyboard Content --&lt;
+     *     &gt;Row row_attributes*&lt;
+     *       &gt;!-- Row Content --&lt;
+     *       &gt;Key key_attributes* /&lt;
+     *       &gt;Spacer horizontalGap="0.2in" /&lt;
+     *       &gt;include keyboardLayout="@xml/other_keys"&lt;
+     *       ...
+     *     &gt;/Row&lt;
+     *     &gt;include keyboardLayout="@xml/other_rows"&lt;
+     *     ...
+     *   &gt;/Keyboard&lt;
+     * </pre>
+     * The XML file which is included in other file must have &gt;merge&lt; as root element,
+     * such as:
+     * <pre>
+     *   &gt;!-- xml/other_keys.xml --&lt;
+     *   &gt;merge&lt;
+     *     &gt;Key key_attributes* /&lt;
+     *     ...
+     *   &gt;/merge&lt;
+     * </pre>
+     * and
+     * <pre>
+     *   &gt;!-- xml/other_rows.xml --&lt;
+     *   &gt;merge&lt;
+     *     &gt;Row row_attributes*&lt;
+     *       &gt;Key key_attributes* /&lt;
+     *     &gt;/Row&lt;
+     *     ...
+     *   &gt;/merge&lt;
+     * </pre>
+     * You can also use switch-case-default tags to select Rows and Keys.
+     * <pre>
+     *   &gt;switch&lt;
+     *     &gt;case case_attribute*&lt;
+     *       &gt;!-- Any valid tags at switch position --&lt;
+     *     &gt;/case&lt;
+     *     ...
+     *     &gt;default&lt;
+     *       &gt;!-- Any valid tags at switch position --&lt;
+     *     &gt;/default&lt;
+     *   &gt;/switch&lt;
+     * </pre>
+     * You can declare Key style and specify styles within Key tags.
+     * <pre>
+     *     &gt;switch&lt;
+     *       &gt;case mode="email"&lt;
+     *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
+     *           keyLabel=".com"
+     *         /&lt;
+     *       &gt;/case&lt;
+     *       &gt;case mode="url"&lt;
+     *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
+     *           keyLabel="http://"
+     *         /&lt;
+     *       &gt;/case&lt;
+     *     &gt;/switch&lt;
+     *     ...
+     *     &gt;Key keyStyle="shift-key" ... /&lt;
+     * </pre>
+     */
+
+    public static class Builder<KP extends Params> {
+        private static final String TAG = Builder.class.getSimpleName();
+        private static final boolean DEBUG = false;
+
+        // Keyboard XML Tags
+        private static final String TAG_KEYBOARD = "Keyboard";
+        private static final String TAG_ROW = "Row";
+        private static final String TAG_KEY = "Key";
+        private static final String TAG_SPACER = "Spacer";
+        private static final String TAG_INCLUDE = "include";
+        private static final String TAG_MERGE = "merge";
+        private static final String TAG_SWITCH = "switch";
+        private static final String TAG_CASE = "case";
+        private static final String TAG_DEFAULT = "default";
+        public static final String TAG_KEY_STYLE = "key-style";
+
+        private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
+        private static final int DEFAULT_KEYBOARD_ROWS = 4;
+
+        protected final KP mParams;
+        protected final Context mContext;
+        protected final Resources mResources;
+        private final DisplayMetrics mDisplayMetrics;
+
+        private int mCurrentY = 0;
+        private Row mCurrentRow = null;
+        private boolean mLeftEdge;
+        private boolean mTopEdge;
+        private Key mRightEdgeKey = null;
+        private final KeyStyles mKeyStyles = new KeyStyles();
+
+        /**
+         * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
+         * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
+         * defines.
+         */
+        public static class Row {
+            // keyWidth enum constants
+            private static final int KEYWIDTH_NOT_ENUM = 0;
+            private static final int KEYWIDTH_FILL_RIGHT = -1;
+            private static final int KEYWIDTH_FILL_BOTH = -2;
+
+            private final Params mParams;
+            /** Default width of a key in this row. */
+            public final float mDefaultKeyWidth;
+            /** Default height of a key in this row. */
+            public final int mRowHeight;
+
+            private final int mCurrentY;
+            // Will be updated by {@link Key}'s constructor.
+            private float mCurrentX;
+
+            public Row(Resources res, Params params, XmlPullParser parser, int y) {
+                mParams = params;
+                TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
+                        R.styleable.Keyboard);
+                mRowHeight = (int)Builder.getDimensionOrFraction(keyboardAttr,
+                        R.styleable.Keyboard_rowHeight,
+                        params.mBaseHeight, params.mDefaultRowHeight);
+                keyboardAttr.recycle();
+                TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
+                        R.styleable.Keyboard_Key);
+                mDefaultKeyWidth = Builder.getDimensionOrFraction(keyAttr,
+                        R.styleable.Keyboard_Key_keyWidth,
+                        params.mBaseWidth, params.mDefaultKeyWidth);
+                keyAttr.recycle();
+
+                mCurrentY = y;
+                mCurrentX = 0.0f;
+            }
+
+            public void setXPos(float keyXPos) {
+                mCurrentX = keyXPos;
+            }
+
+            public void advanceXPos(float width) {
+                mCurrentX += width;
+            }
+
+            public int getKeyY() {
+                return mCurrentY;
+            }
+
+            public float getKeyX(TypedArray keyAttr) {
+                final int widthType = Builder.getEnumValue(keyAttr,
+                        R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
+                if (widthType == KEYWIDTH_FILL_BOTH) {
+                    // If keyWidth is fillBoth, the key width should start right after the nearest
+                    // key on the left hand side.
+                    return mCurrentX;
+                }
+
+                final int keyboardRightEdge = mParams.mOccupiedWidth
+                        - mParams.mHorizontalEdgesPadding;
+                if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
+                    final float keyXPos = Builder.getDimensionOrFraction(keyAttr,
+                            R.styleable.Keyboard_Key_keyXPos, mParams.mBaseWidth, 0);
+                    if (keyXPos < 0) {
+                        // If keyXPos is negative, the actual x-coordinate will be
+                        // keyboardWidth + keyXPos.
+                        // keyXPos shouldn't be less than mCurrentX because drawable area for this
+                        // key starts at mCurrentX. Or, this key will overlaps the adjacent key on
+                        // its left hand side.
+                        return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
+                    } else {
+                        return keyXPos + mParams.mHorizontalEdgesPadding;
+                    }
+                }
+                return mCurrentX;
+            }
+
+            public float getKeyWidth(TypedArray keyAttr, float keyXPos) {
+                final int widthType = Builder.getEnumValue(keyAttr,
+                        R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
+                switch (widthType) {
+                case KEYWIDTH_FILL_RIGHT:
+                case KEYWIDTH_FILL_BOTH:
+                    final int keyboardRightEdge =
+                            mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding;
+                    // If keyWidth is fillRight, the actual key width will be determined to fill
+                    // out the area up to the right edge of the keyboard.
+                    // If keyWidth is fillBoth, the actual key width will be determined to fill out
+                    // the area between the nearest key on the left hand side and the right edge of
+                    // the keyboard.
+                    return keyboardRightEdge - keyXPos;
+                default: // KEYWIDTH_NOT_ENUM
+                    return Builder.getDimensionOrFraction(keyAttr,
+                            R.styleable.Keyboard_Key_keyWidth,
+                            mParams.mBaseWidth, mDefaultKeyWidth);
+                }
+            }
+        }
+
+        public Builder(Context context, KP params) {
+            mContext = context;
+            final Resources res = context.getResources();
+            mResources = res;
+            mDisplayMetrics = res.getDisplayMetrics();
+
+            mParams = params;
+
+            setTouchPositionCorrectionData(context, params);
+
+            params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
+            params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
+        }
+
+        private static void setTouchPositionCorrectionData(Context context, Params params) {
+            final TypedArray a = context.obtainStyledAttributes(
+                    null, R.styleable.Keyboard, R.attr.keyboardStyle, 0);
+            params.mThemeId = a.getInt(R.styleable.Keyboard_themeId, 0);
+            final int resourceId = a.getResourceId(
+                    R.styleable.Keyboard_touchPositionCorrectionData, 0);
+            a.recycle();
+            if (resourceId == 0) {
+                if (LatinImeLogger.sDBG)
+                    throw new RuntimeException("touchPositionCorrectionData is not defined");
+                return;
+            }
+
+            final String[] data = context.getResources().getStringArray(resourceId);
+            params.mTouchPositionCorrection.load(data);
+        }
+
+        public Builder<KP> load(int xmlId, KeyboardId id) {
+            mParams.mId = id;
+            final XmlResourceParser parser = mResources.getXml(xmlId);
+            try {
+                parseKeyboard(parser);
+            } 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);
+            } finally {
+                parser.close();
+            }
+            return this;
+        }
+
+        public void setTouchPositionCorrectionEnabled(boolean enabled) {
+            mParams.mTouchPositionCorrection.setEnabled(enabled);
+        }
+
+        public Keyboard build() {
+            return new Keyboard(mParams);
+        }
+
+        private void parseKeyboard(XmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId));
+            int event;
+            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                if (event == XmlPullParser.START_TAG) {
+                    final String tag = parser.getName();
+                    if (TAG_KEYBOARD.equals(tag)) {
+                        parseKeyboardAttributes(parser);
+                        startKeyboard();
+                        parseKeyboardContent(parser, false);
+                        break;
+                    } else {
+                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD);
+                    }
+                }
+            }
+        }
+
+        private void parseKeyboardAttributes(XmlPullParser parser) {
+            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 = mDisplayMetrics.heightPixels;
+                final int keyboardHeight = (int)keyboardAttr.getDimension(
+                        R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
+                final int maxKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr,
+                        R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2);
+                int minKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr,
+                        R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2);
+                if (minKeyboardHeight < 0) {
+                    // Specified fraction was negative, so it should be calculated against display
+                    // width.
+                    minKeyboardHeight = -(int)getDimensionOrFraction(keyboardAttr,
+                            R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2);
+                }
+                final Params params = mParams;
+                // Keyboard height will not exceed maxKeyboardHeight and will not be less than
+                // minKeyboardHeight.
+                params.mOccupiedHeight = Math.max(
+                        Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
+                params.mOccupiedWidth = params.mId.mWidth;
+                params.mTopPadding = (int)getDimensionOrFraction(keyboardAttr,
+                        R.styleable.Keyboard_keyboardTopPadding, params.mOccupiedHeight, 0);
+                params.mBottomPadding = (int)getDimensionOrFraction(keyboardAttr,
+                        R.styleable.Keyboard_keyboardBottomPadding, params.mOccupiedHeight, 0);
+                params.mHorizontalEdgesPadding = (int)getDimensionOrFraction(keyboardAttr,
+                        R.styleable.Keyboard_keyboardHorizontalEdgesPadding,
+                        mParams.mOccupiedWidth, 0);
+
+                params.mBaseWidth = params.mOccupiedWidth - params.mHorizontalEdgesPadding * 2
+                        - params.mHorizontalCenterPadding;
+                params.mDefaultKeyWidth = (int)getDimensionOrFraction(keyAttr,
+                        R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth,
+                        params.mBaseWidth / DEFAULT_KEYBOARD_COLUMNS);
+                params.mHorizontalGap = (int)getDimensionOrFraction(keyboardAttr,
+                        R.styleable.Keyboard_horizontalGap, params.mBaseWidth, 0);
+                params.mVerticalGap = (int)getDimensionOrFraction(keyboardAttr,
+                        R.styleable.Keyboard_verticalGap, params.mOccupiedHeight, 0);
+                params.mBaseHeight = params.mOccupiedHeight - params.mTopPadding
+                        - params.mBottomPadding + params.mVerticalGap;
+                params.mDefaultRowHeight = (int)getDimensionOrFraction(keyboardAttr,
+                        R.styleable.Keyboard_rowHeight, params.mBaseHeight,
+                        params.mBaseHeight / DEFAULT_KEYBOARD_ROWS);
+
+                params.mIsRtlKeyboard = keyboardAttr.getBoolean(
+                        R.styleable.Keyboard_isRtlKeyboard, false);
+                params.mMoreKeysTemplate = keyboardAttr.getResourceId(
+                        R.styleable.Keyboard_moreKeysTemplate, 0);
+                params.mMaxMiniKeyboardColumn = keyAttr.getInt(
+                        R.styleable.Keyboard_Key_maxMoreKeysColumn, 5);
+
+                params.mIconsSet.loadIcons(keyboardAttr);
+            } finally {
+                keyAttr.recycle();
+                keyboardAttr.recycle();
+            }
+        }
+
+        private void parseKeyboardContent(XmlPullParser parser, 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_ROW.equals(tag)) {
+                        Row row = parseRowAttributes(parser);
+                        if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW));
+                        if (!skip)
+                            startRow(row);
+                        parseRowContent(parser, row, skip);
+                    } else if (TAG_INCLUDE.equals(tag)) {
+                        parseIncludeKeyboardContent(parser, skip);
+                    } else if (TAG_SWITCH.equals(tag)) {
+                        parseSwitchKeyboardContent(parser, skip);
+                    } else if (TAG_KEY_STYLE.equals(tag)) {
+                        parseKeyStyle(parser, skip);
+                    } else {
+                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW);
+                    }
+                } else if (event == XmlPullParser.END_TAG) {
+                    final String tag = parser.getName();
+                    if (TAG_KEYBOARD.equals(tag)) {
+                        endKeyboard();
+                        break;
+                    } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
+                            || TAG_MERGE.equals(tag)) {
+                        if (DEBUG) Log.d(TAG, String.format("</%s>", tag));
+                        break;
+                    } else if (TAG_KEY_STYLE.equals(tag)) {
+                        continue;
+                    } else {
+                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW);
+                    }
+                }
+            }
+        }
+
+        private Row parseRowAttributes(XmlPullParser parser) throws XmlPullParserException {
+            final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+                    R.styleable.Keyboard);
+            try {
+                if (a.hasValue(R.styleable.Keyboard_horizontalGap))
+                    throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap");
+                if (a.hasValue(R.styleable.Keyboard_verticalGap))
+                    throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap");
+                return new Row(mResources, mParams, parser, mCurrentY);
+            } finally {
+                a.recycle();
+            }
+        }
+
+        private void parseRowContent(XmlPullParser 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, skip);
+                    } else if (TAG_SPACER.equals(tag)) {
+                        parseSpacer(parser, row, skip);
+                    } else if (TAG_INCLUDE.equals(tag)) {
+                        parseIncludeRowContent(parser, row, skip);
+                    } else if (TAG_SWITCH.equals(tag)) {
+                        parseSwitchRowContent(parser, row, skip);
+                    } else if (TAG_KEY_STYLE.equals(tag)) {
+                        parseKeyStyle(parser, skip);
+                    } else {
+                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
+                    }
+                } else if (event == XmlPullParser.END_TAG) {
+                    final String tag = parser.getName();
+                    if (TAG_ROW.equals(tag)) {
+                        if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW));
+                        if (!skip)
+                            endRow(row);
+                        break;
+                    } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
+                            || TAG_MERGE.equals(tag)) {
+                        if (DEBUG) Log.d(TAG, String.format("</%s>", tag));
+                        break;
+                    } else if (TAG_KEY_STYLE.equals(tag)) {
+                        continue;
+                    } else {
+                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
+                    }
+                }
+            }
+        }
+
+        private void parseKey(XmlPullParser parser, Row row, boolean skip)
+                throws XmlPullParserException, IOException {
+            if (skip) {
+                XmlParseUtils.checkEndTag(TAG_KEY, parser);
+            } else {
+                final Key key = new Key(mResources, mParams, row, parser, mKeyStyles);
+                if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d moreKeys=%s />",
+                        TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode,
+                        Arrays.toString(key.mMoreKeys)));
+                XmlParseUtils.checkEndTag(TAG_KEY, parser);
+                endKey(key);
+            }
+        }
+
+        private void parseSpacer(XmlPullParser parser, Row row, boolean skip)
+                throws XmlPullParserException, IOException {
+            if (skip) {
+                XmlParseUtils.checkEndTag(TAG_SPACER, parser);
+            } else {
+                final Key.Spacer spacer = new Key.Spacer(
+                        mResources, mParams, row, parser, mKeyStyles);
+                if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER));
+                XmlParseUtils.checkEndTag(TAG_SPACER, parser);
+                endKey(spacer);
+            }
+        }
+
+        private void parseIncludeKeyboardContent(XmlPullParser parser, boolean skip)
+                throws XmlPullParserException, IOException {
+            parseIncludeInternal(parser, null, skip);
+        }
+
+        private void parseIncludeRowContent(XmlPullParser parser, Row row, boolean skip)
+                throws XmlPullParserException, IOException {
+            parseIncludeInternal(parser, row, skip);
+        }
+
+        private void parseIncludeInternal(XmlPullParser parser, Row row, boolean skip)
+                throws XmlPullParserException, IOException {
+            if (skip) {
+                XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
+            } else {
+                final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+                        R.styleable.Keyboard_Include);
+                int keyboardLayout = 0;
+                try {
+                    XmlParseUtils.checkAttributeExists(a,
+                            R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
+                            TAG_INCLUDE, parser);
+                    keyboardLayout = a.getResourceId(
+                            R.styleable.Keyboard_Include_keyboardLayout, 0);
+                } finally {
+                    a.recycle();
+                }
+
+                XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
+                if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />",
+                        TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout)));
+                final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
+                try {
+                    parseMerge(parserForInclude, row, skip);
+                } finally {
+                    parserForInclude.close();
+                }
+            }
+        }
+
+        private void parseMerge(XmlPullParser 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_MERGE.equals(tag)) {
+                        if (row == null) {
+                            parseKeyboardContent(parser, skip);
+                        } else {
+                            parseRowContent(parser, row, skip);
+                        }
+                        break;
+                    } else {
+                        throw new XmlParseUtils.ParseException(
+                                "Included keyboard layout must have <merge> root element", parser);
+                    }
+                }
+            }
+        }
+
+        private void parseSwitchKeyboardContent(XmlPullParser parser, boolean skip)
+                throws XmlPullParserException, IOException {
+            parseSwitchInternal(parser, null, skip);
+        }
+
+        private void parseSwitchRowContent(XmlPullParser parser, Row row, boolean skip)
+                throws XmlPullParserException, IOException {
+            parseSwitchInternal(parser, row, skip);
+        }
+
+        private void parseSwitchInternal(XmlPullParser parser, Row row, boolean skip)
+                throws XmlPullParserException, IOException {
+            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 ? true : skip);
+                    } else if (TAG_DEFAULT.equals(tag)) {
+                        selected |= parseDefault(parser, row, selected ? true : skip);
+                    } else {
+                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
+                    }
+                } else if (event == XmlPullParser.END_TAG) {
+                    final String tag = parser.getName();
+                    if (TAG_SWITCH.equals(tag)) {
+                        if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_SWITCH));
+                        break;
+                    } else {
+                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
+                    }
+                }
+            }
+        }
+
+        private boolean parseCase(XmlPullParser parser, Row row, boolean skip)
+                throws XmlPullParserException, IOException {
+            final boolean selected = parseCaseCondition(parser);
+            if (row == null) {
+                // Processing Rows.
+                parseKeyboardContent(parser, selected ? skip : true);
+            } else {
+                // Processing Keys.
+                parseRowContent(parser, row, selected ? skip : true);
+            }
+            return selected;
+        }
+
+        private boolean parseCaseCondition(XmlPullParser parser) {
+            final KeyboardId id = mParams.mId;
+            if (id == null)
+                return true;
+
+            final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+                    R.styleable.Keyboard_Case);
+            try {
+                final boolean modeMatched = matchTypedValue(a,
+                        R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
+                final boolean navigateActionMatched = matchBoolean(a,
+                        R.styleable.Keyboard_Case_navigateAction, id.navigateAction());
+                final boolean passwordInputMatched = matchBoolean(a,
+                        R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
+                final boolean hasSettingsKeyMatched = matchBoolean(a,
+                        R.styleable.Keyboard_Case_hasSettingsKey, id.hasSettingsKey());
+                final boolean f2KeyModeMatched = matchInteger(a,
+                        R.styleable.Keyboard_Case_f2KeyMode, id.f2KeyMode());
+                final boolean clobberSettingsKeyMatched = matchBoolean(a,
+                        R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
+                final boolean shortcutKeyEnabledMatched = matchBoolean(a,
+                        R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
+                final boolean hasShortcutKeyMatched = matchBoolean(a,
+                        R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
+                // As noted at {@link KeyboardId} class, we are interested only in enum value
+                // masked by {@link android.view.inputmethod.EditorInfo#IME_MASK_ACTION} and
+                // {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. So matching
+                // this attribute with id.mImeOptions as integer value is enough for our purpose.
+                final boolean imeActionMatched = matchInteger(a,
+                        R.styleable.Keyboard_Case_imeAction, id.imeAction());
+                final boolean localeCodeMatched = matchString(a,
+                        R.styleable.Keyboard_Case_localeCode, id.mLocale.toString());
+                final boolean languageCodeMatched = matchString(a,
+                        R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
+                final boolean countryCodeMatched = matchString(a,
+                        R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
+                final boolean selected = modeMatched && navigateActionMatched
+                        && passwordInputMatched && hasSettingsKeyMatched && f2KeyModeMatched
+                        && clobberSettingsKeyMatched && shortcutKeyEnabledMatched
+                        && hasShortcutKeyMatched && imeActionMatched && localeCodeMatched
+                        && languageCodeMatched && countryCodeMatched;
+
+                if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE,
+                        textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
+                        booleanAttr(a, R.styleable.Keyboard_Case_navigateAction, "navigateAction"),
+                        booleanAttr(a, R.styleable.Keyboard_Case_passwordInput, "passwordInput"),
+                        booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, "hasSettingsKey"),
+                        textAttr(KeyboardId.f2KeyModeName(
+                                a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)), "f2KeyMode"),
+                        booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
+                                "clobberSettingsKey"),
+                        booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled,
+                                "shortcutKeyEnabled"),
+                        booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"),
+                        textAttr(EditorInfoCompatUtils.imeOptionsName(
+                                a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"),
+                        textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"),
+                        textAttr(a.getString(R.styleable.Keyboard_Case_languageCode),
+                                "languageCode"),
+                        textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), "countryCode"),
+                        Boolean.toString(selected)));
+
+                return selected;
+            } finally {
+                a.recycle();
+            }
+        }
+
+        private static boolean matchInteger(TypedArray a, int index, int value) {
+            // If <case> does not have "index" attribute, that means this <case> is wild-card for
+            // the attribute.
+            return !a.hasValue(index) || a.getInt(index, 0) == value;
+        }
+
+        private static boolean matchBoolean(TypedArray a, int index, boolean value) {
+            // If <case> does not have "index" attribute, that means this <case> is wild-card for
+            // the attribute.
+            return !a.hasValue(index) || a.getBoolean(index, false) == value;
+        }
+
+        private static boolean matchString(TypedArray a, int index, String value) {
+            // If <case> does not have "index" attribute, that means this <case> is wild-card for
+            // the attribute.
+            return !a.hasValue(index)
+                    || stringArrayContains(a.getString(index).split("\\|"), value);
+        }
+
+        private static boolean matchTypedValue(TypedArray a, int index, int intValue,
+                String strValue) {
+            // If <case> does not have "index" attribute, that means this <case> is wild-card for
+            // the attribute.
+            final TypedValue v = a.peekValue(index);
+            if (v == null)
+                return true;
+
+            if (isIntegerValue(v)) {
+                return intValue == a.getInt(index, 0);
+            } else if (isStringValue(v)) {
+                return stringArrayContains(a.getString(index).split("\\|"), strValue);
+            }
+            return false;
+        }
+
+        private static boolean stringArrayContains(String[] array, String value) {
+            for (final String elem : array) {
+                if (elem.equals(value))
+                    return true;
+            }
+            return false;
+        }
+
+        private boolean parseDefault(XmlPullParser parser, Row row, boolean skip)
+                throws XmlPullParserException, IOException {
+            if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT));
+            if (row == null) {
+                parseKeyboardContent(parser, skip);
+            } else {
+                parseRowContent(parser, row, skip);
+            }
+            return true;
+        }
+
+        private void parseKeyStyle(XmlPullParser parser, boolean skip)
+                throws XmlPullParserException {
+            TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+                    R.styleable.Keyboard_KeyStyle);
+            TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+                    R.styleable.Keyboard_Key);
+            try {
+                if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
+                    throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
+                            + "/> needs styleName attribute", parser);
+                if (!skip)
+                    mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
+            } finally {
+                keyStyleAttr.recycle();
+                keyAttrs.recycle();
+            }
+        }
+
+        private void startKeyboard() {
+            mCurrentY += mParams.mTopPadding;
+            mTopEdge = true;
+        }
+
+        private void startRow(Row row) {
+            addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
+            mCurrentRow = row;
+            mLeftEdge = true;
+            mRightEdgeKey = null;
+        }
+
+        private void endRow(Row row) {
+            if (mCurrentRow == null)
+                throw new InflateException("orphant end row tag");
+            if (mRightEdgeKey != null) {
+                mRightEdgeKey.markAsRightEdge(mParams);
+                mRightEdgeKey = null;
+            }
+            addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
+            mCurrentY += row.mRowHeight;
+            mCurrentRow = null;
+            mTopEdge = false;
+        }
+
+        private void endKey(Key key) {
+            mParams.onAddKey(key);
+            if (mLeftEdge) {
+                key.markAsLeftEdge(mParams);
+                mLeftEdge = false;
+            }
+            if (mTopEdge) {
+                key.markAsTopEdge(mParams);
+            }
+            mRightEdgeKey = key;
+        }
+
+        private void endKeyboard() {
+            // nothing to do here.
+        }
+
+        private void addEdgeSpace(float width, Row row) {
+            row.advanceXPos(width);
+            mLeftEdge = false;
+            mRightEdgeKey = null;
+        }
+
+        public static float getDimensionOrFraction(TypedArray a, int index, int base,
+                float defValue) {
+            final TypedValue value = a.peekValue(index);
+            if (value == null)
+                return defValue;
+            if (isFractionValue(value)) {
+                return a.getFraction(index, base, base, defValue);
+            } else if (isDimensionValue(value)) {
+                return a.getDimension(index, defValue);
+            }
+            return defValue;
+        }
+
+        public static int getEnumValue(TypedArray a, int index, int defValue) {
+            final TypedValue value = a.peekValue(index);
+            if (value == null)
+                return defValue;
+            if (isIntegerValue(value)) {
+                return a.getInt(index, defValue);
+            }
+            return defValue;
+        }
+
+        private static boolean isFractionValue(TypedValue v) {
+            return v.type == TypedValue.TYPE_FRACTION;
+        }
+
+        private static boolean isDimensionValue(TypedValue v) {
+            return v.type == TypedValue.TYPE_DIMENSION;
+        }
+
+        private static boolean isIntegerValue(TypedValue v) {
+            return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
+        }
+
+        private static boolean isStringValue(TypedValue v) {
+            return v.type == TypedValue.TYPE_STRING;
+        }
+
+        private static String textAttr(String value, String name) {
+            return value != null ? String.format(" %s=%s", name, value) : "";
+        }
+
+        private static String booleanAttr(TypedArray a, int index, String name) {
+            return a.hasValue(index)
+                    ? String.format(" %s=%s", name, a.getBoolean(index, false)) : "";
+        }
+    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
index f64ac84acf..a2e784c992 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
@@ -24,9 +24,6 @@ import android.util.Log;
 import android.util.Xml;
 import android.view.inputmethod.EditorInfo;
 
-import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
-import com.android.inputmethod.keyboard.internal.KeyboardParams;
-import com.android.inputmethod.keyboard.internal.XmlParseUtils;
 import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.LocaleUtils;
@@ -34,6 +31,7 @@ import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SettingsValues;
 import com.android.inputmethod.latin.SubtypeSwitcher;
 import com.android.inputmethod.latin.Utils;
+import com.android.inputmethod.latin.XmlParseUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -125,8 +123,8 @@ public class KeyboardSet {
         if (keyboard == null) {
             final Locale savedLocale = LocaleUtils.setSystemLocale(res, id.mLocale);
             try {
-                final KeyboardBuilder<KeyboardParams> builder =
-                        new KeyboardBuilder<KeyboardParams>(context, new KeyboardParams());
+                final Keyboard.Builder<Keyboard.Params> builder =
+                        new Keyboard.Builder<Keyboard.Params>(context, new Keyboard.Params());
                 builder.load(xmlId, id);
                 builder.setTouchPositionCorrectionEnabled(
                         subtypeSwitcher.currentSubtypeContainsExtraValueKey(
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
index 6781459d0b..548b5ea854 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
@@ -18,8 +18,6 @@ package com.android.inputmethod.keyboard;
 
 import android.graphics.Paint;
 
-import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
-import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.keyboard.internal.MoreKeySpecParser;
 import com.android.inputmethod.latin.R;
 
@@ -35,10 +33,10 @@ public class MiniKeyboard extends Keyboard {
         return mDefaultKeyCoordX;
     }
 
-    public static class Builder extends KeyboardBuilder<Builder.MiniKeyboardParams> {
+    public static class Builder extends Keyboard.Builder<Builder.MiniKeyboardParams> {
         private final CharSequence[] mMoreKeys;
 
-        public static class MiniKeyboardParams extends KeyboardParams {
+        public static class MiniKeyboardParams extends Keyboard.Params {
             /* package */int mTopRowAdjustment;
             public int mNumRows;
             public int mNumColumns;
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index b6178d945c..c1dae06014 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -18,7 +18,7 @@ package com.android.inputmethod.keyboard;
 
 import android.graphics.Rect;
 
-import com.android.inputmethod.keyboard.internal.KeyboardParams.TouchPositionCorrection;
+import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection;
 import com.android.inputmethod.latin.Utils;
 import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo;
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
index 4e15ecf64d..9b99dc42f2 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
@@ -19,7 +19,9 @@ package com.android.inputmethod.keyboard.internal;
 import android.content.res.TypedArray;
 import android.util.Log;
 
+import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.XmlParseUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -196,7 +198,7 @@ public class KeyStyles {
             XmlPullParser parser) throws XmlPullParserException {
         final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
         if (DEBUG) Log.d(TAG, String.format("<%s styleName=%s />",
-                KeyboardBuilder.TAG_KEY_STYLE, styleName));
+                Keyboard.Builder.TAG_KEY_STYLE, styleName));
         if (mStyles.containsKey(styleName))
             throw new XmlParseUtils.ParseException(
                     "duplicate key style declared: " + styleName, parser);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
deleted file mode 100644
index 7ef471c930..0000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ /dev/null
@@ -1,833 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.Context;
-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;
-import android.view.InflateException;
-
-import com.android.inputmethod.compat.EditorInfoCompatUtils;
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-/**
- * Keyboard Building helper.
- *
- * This class parses Keyboard XML file and eventually build a Keyboard.
- * The Keyboard XML file looks like:
- * <pre>
- *   &gt;!-- xml/keyboard.xml --&lt;
- *   &gt;Keyboard keyboard_attributes*&lt;
- *     &gt;!-- Keyboard Content --&lt;
- *     &gt;Row row_attributes*&lt;
- *       &gt;!-- Row Content --&lt;
- *       &gt;Key key_attributes* /&lt;
- *       &gt;Spacer horizontalGap="0.2in" /&lt;
- *       &gt;include keyboardLayout="@xml/other_keys"&lt;
- *       ...
- *     &gt;/Row&lt;
- *     &gt;include keyboardLayout="@xml/other_rows"&lt;
- *     ...
- *   &gt;/Keyboard&lt;
- * </pre>
- * The XML file which is included in other file must have &gt;merge&lt; as root element, such as:
- * <pre>
- *   &gt;!-- xml/other_keys.xml --&lt;
- *   &gt;merge&lt;
- *     &gt;Key key_attributes* /&lt;
- *     ...
- *   &gt;/merge&lt;
- * </pre>
- * and
- * <pre>
- *   &gt;!-- xml/other_rows.xml --&lt;
- *   &gt;merge&lt;
- *     &gt;Row row_attributes*&lt;
- *       &gt;Key key_attributes* /&lt;
- *     &gt;/Row&lt;
- *     ...
- *   &gt;/merge&lt;
- * </pre>
- * You can also use switch-case-default tags to select Rows and Keys.
- * <pre>
- *   &gt;switch&lt;
- *     &gt;case case_attribute*&lt;
- *       &gt;!-- Any valid tags at switch position --&lt;
- *     &gt;/case&lt;
- *     ...
- *     &gt;default&lt;
- *       &gt;!-- Any valid tags at switch position --&lt;
- *     &gt;/default&lt;
- *   &gt;/switch&lt;
- * </pre>
- * You can declare Key style and specify styles within Key tags.
- * <pre>
- *     &gt;switch&lt;
- *       &gt;case mode="email"&lt;
- *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
- *           keyLabel=".com"
- *         /&lt;
- *       &gt;/case&lt;
- *       &gt;case mode="url"&lt;
- *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
- *           keyLabel="http://"
- *         /&lt;
- *       &gt;/case&lt;
- *     &gt;/switch&lt;
- *     ...
- *     &gt;Key keyStyle="shift-key" ... /&lt;
- * </pre>
- */
-
-public class KeyboardBuilder<KP extends KeyboardParams> {
-    private static final String TAG = KeyboardBuilder.class.getSimpleName();
-    private static final boolean DEBUG = false;
-
-    // Keyboard XML Tags
-    private static final String TAG_KEYBOARD = "Keyboard";
-    private static final String TAG_ROW = "Row";
-    private static final String TAG_KEY = "Key";
-    private static final String TAG_SPACER = "Spacer";
-    private static final String TAG_INCLUDE = "include";
-    private static final String TAG_MERGE = "merge";
-    private static final String TAG_SWITCH = "switch";
-    private static final String TAG_CASE = "case";
-    private static final String TAG_DEFAULT = "default";
-    public static final String TAG_KEY_STYLE = "key-style";
-
-    private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
-    private static final int DEFAULT_KEYBOARD_ROWS = 4;
-
-    protected final KP mParams;
-    protected final Context mContext;
-    protected final Resources mResources;
-    private final DisplayMetrics mDisplayMetrics;
-
-    private int mCurrentY = 0;
-    private Row mCurrentRow = null;
-    private boolean mLeftEdge;
-    private boolean mTopEdge;
-    private Key mRightEdgeKey = null;
-    private final KeyStyles mKeyStyles = new KeyStyles();
-
-    /**
-     * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
-     * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
-     * defines.
-     */
-    public static class Row {
-        // keyWidth enum constants
-        private static final int KEYWIDTH_NOT_ENUM = 0;
-        private static final int KEYWIDTH_FILL_RIGHT = -1;
-        private static final int KEYWIDTH_FILL_BOTH = -2;
-
-        private final KeyboardParams mParams;
-        /** Default width of a key in this row. */
-        public final float mDefaultKeyWidth;
-        /** Default height of a key in this row. */
-        public final int mRowHeight;
-
-        private final int mCurrentY;
-        // Will be updated by {@link Key}'s constructor.
-        private float mCurrentX;
-
-        public Row(Resources res, KeyboardParams params, XmlPullParser parser, int y) {
-            mParams = params;
-            TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
-                    R.styleable.Keyboard);
-            mRowHeight = (int)KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_rowHeight, params.mBaseHeight, params.mDefaultRowHeight);
-            keyboardAttr.recycle();
-            TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
-                    R.styleable.Keyboard_Key);
-            mDefaultKeyWidth = KeyboardBuilder.getDimensionOrFraction(keyAttr,
-                    R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth, params.mDefaultKeyWidth);
-            keyAttr.recycle();
-
-            mCurrentY = y;
-            mCurrentX = 0.0f;
-        }
-
-        public void setXPos(float keyXPos) {
-            mCurrentX = keyXPos;
-        }
-
-        public void advanceXPos(float width) {
-            mCurrentX += width;
-        }
-
-        public int getKeyY() {
-            return mCurrentY;
-        }
-
-        public float getKeyX(TypedArray keyAttr) {
-            final int widthType = KeyboardBuilder.getEnumValue(keyAttr,
-                    R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
-            if (widthType == KEYWIDTH_FILL_BOTH) {
-                // If keyWidth is fillBoth, the key width should start right after the nearest key
-                // on the left hand side.
-                return mCurrentX;
-            }
-
-            final int keyboardRightEdge = mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding;
-            if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
-                final float keyXPos = KeyboardBuilder.getDimensionOrFraction(keyAttr,
-                        R.styleable.Keyboard_Key_keyXPos, mParams.mBaseWidth, 0);
-                if (keyXPos < 0) {
-                    // If keyXPos is negative, the actual x-coordinate will be
-                    // keyboardWidth + keyXPos.
-                    // keyXPos shouldn't be less than mCurrentX because drawable area for this key
-                    // starts at mCurrentX. Or, this key will overlaps the adjacent key on its left
-                    // hand side.
-                    return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
-                } else {
-                    return keyXPos + mParams.mHorizontalEdgesPadding;
-                }
-            }
-            return mCurrentX;
-        }
-
-        public float getKeyWidth(TypedArray keyAttr, float keyXPos) {
-            final int widthType = KeyboardBuilder.getEnumValue(keyAttr,
-                    R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
-            switch (widthType) {
-            case KEYWIDTH_FILL_RIGHT:
-            case KEYWIDTH_FILL_BOTH:
-                final int keyboardRightEdge =
-                        mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding;
-                // If keyWidth is fillRight, the actual key width will be determined to fill out the
-                // area up to the right edge of the keyboard.
-                // If keyWidth is fillBoth, the actual key width will be determined to fill out the
-                // area between the nearest key on the left hand side and the right edge of the
-                // keyboard.
-                return keyboardRightEdge - keyXPos;
-            default: // KEYWIDTH_NOT_ENUM
-                return KeyboardBuilder.getDimensionOrFraction(keyAttr,
-                        R.styleable.Keyboard_Key_keyWidth, mParams.mBaseWidth, mDefaultKeyWidth);
-            }
-        }
-    }
-
-    public KeyboardBuilder(Context context, KP params) {
-        mContext = context;
-        final Resources res = context.getResources();
-        mResources = res;
-        mDisplayMetrics = res.getDisplayMetrics();
-
-        mParams = params;
-
-        setTouchPositionCorrectionData(context, params);
-
-        params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
-        params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
-    }
-
-    private static void setTouchPositionCorrectionData(Context context, KeyboardParams params) {
-        final TypedArray a = context.obtainStyledAttributes(
-                null, R.styleable.Keyboard, R.attr.keyboardStyle, 0);
-        params.mThemeId = a.getInt(R.styleable.Keyboard_themeId, 0);
-        final int resourceId = a.getResourceId(R.styleable.Keyboard_touchPositionCorrectionData, 0);
-        a.recycle();
-        if (resourceId == 0) {
-            if (LatinImeLogger.sDBG)
-                throw new RuntimeException("touchPositionCorrectionData is not defined");
-            return;
-        }
-
-        final String[] data = context.getResources().getStringArray(resourceId);
-        params.mTouchPositionCorrection.load(data);
-    }
-
-    public KeyboardBuilder<KP> load(int xmlId, KeyboardId id) {
-        mParams.mId = id;
-        final XmlResourceParser parser = mResources.getXml(xmlId);
-        try {
-            parseKeyboard(parser);
-        } 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);
-        } finally {
-            parser.close();
-        }
-        return this;
-    }
-
-    public void setTouchPositionCorrectionEnabled(boolean enabled) {
-        mParams.mTouchPositionCorrection.setEnabled(enabled);
-    }
-
-    public Keyboard build() {
-        return new Keyboard(mParams);
-    }
-
-    private void parseKeyboard(XmlPullParser parser)
-            throws XmlPullParserException, IOException {
-        if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId));
-        int event;
-        while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
-            if (event == XmlPullParser.START_TAG) {
-                final String tag = parser.getName();
-                if (TAG_KEYBOARD.equals(tag)) {
-                    parseKeyboardAttributes(parser);
-                    startKeyboard();
-                    parseKeyboardContent(parser, false);
-                    break;
-                } else {
-                    throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD);
-                }
-            }
-        }
-    }
-
-    private void parseKeyboardAttributes(XmlPullParser parser) {
-        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 = mDisplayMetrics.heightPixels;
-            final int keyboardHeight = (int)keyboardAttr.getDimension(
-                    R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
-            final int maxKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2);
-            int minKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2);
-            if (minKeyboardHeight < 0) {
-                // Specified fraction was negative, so it should be calculated against display
-                // width.
-                minKeyboardHeight = -(int)getDimensionOrFraction(keyboardAttr,
-                        R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2);
-            }
-            final KeyboardParams params = mParams;
-            // Keyboard height will not exceed maxKeyboardHeight and will not be less than
-            // minKeyboardHeight.
-            params.mOccupiedHeight = Math.max(
-                    Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
-            params.mOccupiedWidth = params.mId.mWidth;
-            params.mTopPadding = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_keyboardTopPadding, params.mOccupiedHeight, 0);
-            params.mBottomPadding = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_keyboardBottomPadding, params.mOccupiedHeight, 0);
-            params.mHorizontalEdgesPadding = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_keyboardHorizontalEdgesPadding, mParams.mOccupiedWidth, 0);
-
-            params.mBaseWidth = params.mOccupiedWidth - params.mHorizontalEdgesPadding * 2
-                    - params.mHorizontalCenterPadding;
-            params.mDefaultKeyWidth = (int)getDimensionOrFraction(keyAttr,
-                    R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth,
-                    params.mBaseWidth / DEFAULT_KEYBOARD_COLUMNS);
-            params.mHorizontalGap = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_horizontalGap, params.mBaseWidth, 0);
-            params.mVerticalGap = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_verticalGap, params.mOccupiedHeight, 0);
-            params.mBaseHeight = params.mOccupiedHeight - params.mTopPadding
-                    - params.mBottomPadding + params.mVerticalGap;
-            params.mDefaultRowHeight = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_rowHeight, params.mBaseHeight,
-                    params.mBaseHeight / DEFAULT_KEYBOARD_ROWS);
-
-            params.mIsRtlKeyboard = keyboardAttr.getBoolean(
-                    R.styleable.Keyboard_isRtlKeyboard, false);
-            params.mMoreKeysTemplate = keyboardAttr.getResourceId(
-                    R.styleable.Keyboard_moreKeysTemplate, 0);
-            params.mMaxMiniKeyboardColumn = keyAttr.getInt(
-                    R.styleable.Keyboard_Key_maxMoreKeysColumn, 5);
-
-            params.mIconsSet.loadIcons(keyboardAttr);
-        } finally {
-            keyAttr.recycle();
-            keyboardAttr.recycle();
-        }
-    }
-
-    private void parseKeyboardContent(XmlPullParser parser, 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_ROW.equals(tag)) {
-                    Row row = parseRowAttributes(parser);
-                    if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW));
-                    if (!skip)
-                        startRow(row);
-                    parseRowContent(parser, row, skip);
-                } else if (TAG_INCLUDE.equals(tag)) {
-                    parseIncludeKeyboardContent(parser, skip);
-                } else if (TAG_SWITCH.equals(tag)) {
-                    parseSwitchKeyboardContent(parser, skip);
-                } else if (TAG_KEY_STYLE.equals(tag)) {
-                    parseKeyStyle(parser, skip);
-                } else {
-                    throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW);
-                }
-            } else if (event == XmlPullParser.END_TAG) {
-                final String tag = parser.getName();
-                if (TAG_KEYBOARD.equals(tag)) {
-                    endKeyboard();
-                    break;
-                } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
-                        || TAG_MERGE.equals(tag)) {
-                    if (DEBUG) Log.d(TAG, String.format("</%s>", tag));
-                    break;
-                } else if (TAG_KEY_STYLE.equals(tag)) {
-                    continue;
-                } else {
-                    throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW);
-                }
-            }
-        }
-    }
-
-    private Row parseRowAttributes(XmlPullParser parser) throws XmlPullParserException {
-        final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.Keyboard);
-        try {
-            if (a.hasValue(R.styleable.Keyboard_horizontalGap))
-                throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap");
-            if (a.hasValue(R.styleable.Keyboard_verticalGap))
-                throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap");
-            return new Row(mResources, mParams, parser, mCurrentY);
-        } finally {
-            a.recycle();
-        }
-    }
-
-    private void parseRowContent(XmlPullParser 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, skip);
-                } else if (TAG_SPACER.equals(tag)) {
-                    parseSpacer(parser, row, skip);
-                } else if (TAG_INCLUDE.equals(tag)) {
-                    parseIncludeRowContent(parser, row, skip);
-                } else if (TAG_SWITCH.equals(tag)) {
-                    parseSwitchRowContent(parser, row, skip);
-                } else if (TAG_KEY_STYLE.equals(tag)) {
-                    parseKeyStyle(parser, skip);
-                } else {
-                    throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
-                }
-            } else if (event == XmlPullParser.END_TAG) {
-                final String tag = parser.getName();
-                if (TAG_ROW.equals(tag)) {
-                    if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW));
-                    if (!skip)
-                        endRow(row);
-                    break;
-                } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
-                        || TAG_MERGE.equals(tag)) {
-                    if (DEBUG) Log.d(TAG, String.format("</%s>", tag));
-                    break;
-                } else if (TAG_KEY_STYLE.equals(tag)) {
-                    continue;
-                } else {
-                    throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
-                }
-            }
-        }
-    }
-
-    private void parseKey(XmlPullParser parser, Row row, boolean skip)
-            throws XmlPullParserException, IOException {
-        if (skip) {
-            XmlParseUtils.checkEndTag(TAG_KEY, parser);
-        } else {
-            final Key key = new Key(mResources, mParams, row, parser, mKeyStyles);
-            if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d moreKeys=%s />",
-                    TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode,
-                    Arrays.toString(key.mMoreKeys)));
-            XmlParseUtils.checkEndTag(TAG_KEY, parser);
-            endKey(key);
-        }
-    }
-
-    private void parseSpacer(XmlPullParser parser, Row row, boolean skip)
-            throws XmlPullParserException, IOException {
-        if (skip) {
-            XmlParseUtils.checkEndTag(TAG_SPACER, parser);
-        } else {
-            final Key.Spacer spacer = new Key.Spacer(mResources, mParams, row, parser, mKeyStyles);
-            if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER));
-            XmlParseUtils.checkEndTag(TAG_SPACER, parser);
-            endKey(spacer);
-        }
-    }
-
-    private void parseIncludeKeyboardContent(XmlPullParser parser, boolean skip)
-            throws XmlPullParserException, IOException {
-        parseIncludeInternal(parser, null, skip);
-    }
-
-    private void parseIncludeRowContent(XmlPullParser parser, Row row, boolean skip)
-            throws XmlPullParserException, IOException {
-        parseIncludeInternal(parser, row, skip);
-    }
-
-    private void parseIncludeInternal(XmlPullParser parser, Row row, boolean skip)
-            throws XmlPullParserException, IOException {
-        if (skip) {
-            XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
-        } else {
-            final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                    R.styleable.Keyboard_Include);
-            int keyboardLayout = 0;
-            try {
-                XmlParseUtils.checkAttributeExists(a,
-                        R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
-                        TAG_INCLUDE, parser);
-                keyboardLayout = a.getResourceId(R.styleable.Keyboard_Include_keyboardLayout, 0);
-            } finally {
-                a.recycle();
-            }
-
-            XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
-            if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />",
-                    TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout)));
-            final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
-            try {
-                parseMerge(parserForInclude, row, skip);
-            } finally {
-                parserForInclude.close();
-            }
-        }
-    }
-
-    private void parseMerge(XmlPullParser 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_MERGE.equals(tag)) {
-                    if (row == null) {
-                        parseKeyboardContent(parser, skip);
-                    } else {
-                        parseRowContent(parser, row, skip);
-                    }
-                    break;
-                } else {
-                    throw new XmlParseUtils.ParseException(
-                            "Included keyboard layout must have <merge> root element", parser);
-                }
-            }
-        }
-    }
-
-    private void parseSwitchKeyboardContent(XmlPullParser parser, boolean skip)
-            throws XmlPullParserException, IOException {
-        parseSwitchInternal(parser, null, skip);
-    }
-
-    private void parseSwitchRowContent(XmlPullParser parser, Row row, boolean skip)
-            throws XmlPullParserException, IOException {
-        parseSwitchInternal(parser, row, skip);
-    }
-
-    private void parseSwitchInternal(XmlPullParser parser, Row row, boolean skip)
-            throws XmlPullParserException, IOException {
-        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 ? true : skip);
-                } else if (TAG_DEFAULT.equals(tag)) {
-                    selected |= parseDefault(parser, row, selected ? true : skip);
-                } else {
-                    throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
-                }
-            } else if (event == XmlPullParser.END_TAG) {
-                final String tag = parser.getName();
-                if (TAG_SWITCH.equals(tag)) {
-                    if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_SWITCH));
-                    break;
-                } else {
-                    throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
-                }
-            }
-        }
-    }
-
-    private boolean parseCase(XmlPullParser parser, Row row, boolean skip)
-            throws XmlPullParserException, IOException {
-        final boolean selected = parseCaseCondition(parser);
-        if (row == null) {
-            // Processing Rows.
-            parseKeyboardContent(parser, selected ? skip : true);
-        } else {
-            // Processing Keys.
-            parseRowContent(parser, row, selected ? skip : true);
-        }
-        return selected;
-    }
-
-    private boolean parseCaseCondition(XmlPullParser parser) {
-        final KeyboardId id = mParams.mId;
-        if (id == null)
-            return true;
-
-        final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.Keyboard_Case);
-        try {
-            final boolean modeMatched = matchTypedValue(a,
-                    R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
-            final boolean navigateActionMatched = matchBoolean(a,
-                    R.styleable.Keyboard_Case_navigateAction, id.navigateAction());
-            final boolean passwordInputMatched = matchBoolean(a,
-                    R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
-            final boolean hasSettingsKeyMatched = matchBoolean(a,
-                    R.styleable.Keyboard_Case_hasSettingsKey, id.hasSettingsKey());
-            final boolean f2KeyModeMatched = matchInteger(a,
-                    R.styleable.Keyboard_Case_f2KeyMode, id.f2KeyMode());
-            final boolean clobberSettingsKeyMatched = matchBoolean(a,
-                    R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
-            final boolean shortcutKeyEnabledMatched = matchBoolean(a,
-                    R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
-            final boolean hasShortcutKeyMatched = matchBoolean(a,
-                    R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
-            // As noted at {@link KeyboardId} class, we are interested only in enum value masked by
-            // {@link android.view.inputmethod.EditorInfo#IME_MASK_ACTION} and
-            // {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. So matching
-            // this attribute with id.mImeOptions as integer value is enough for our purpose.
-            final boolean imeActionMatched = matchInteger(a,
-                    R.styleable.Keyboard_Case_imeAction, id.imeAction());
-            final boolean localeCodeMatched = matchString(a,
-                    R.styleable.Keyboard_Case_localeCode, id.mLocale.toString());
-            final boolean languageCodeMatched = matchString(a,
-                    R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
-            final boolean countryCodeMatched = matchString(a,
-                    R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
-            final boolean selected = modeMatched && navigateActionMatched && passwordInputMatched
-                    && hasSettingsKeyMatched && f2KeyModeMatched && clobberSettingsKeyMatched
-                    && shortcutKeyEnabledMatched && hasShortcutKeyMatched && imeActionMatched &&
-                    localeCodeMatched && languageCodeMatched && countryCodeMatched;
-
-            if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE,
-                    textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
-                    booleanAttr(a, R.styleable.Keyboard_Case_navigateAction, "navigateAction"),
-                    booleanAttr(a, R.styleable.Keyboard_Case_passwordInput, "passwordInput"),
-                    booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, "hasSettingsKey"),
-                    textAttr(KeyboardId.f2KeyModeName(
-                            a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)), "f2KeyMode"),
-                    booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
-                            "clobberSettingsKey"),
-                    booleanAttr(
-                            a, R.styleable.Keyboard_Case_shortcutKeyEnabled, "shortcutKeyEnabled"),
-                    booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"),
-                    textAttr(EditorInfoCompatUtils.imeOptionsName(
-                            a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"),
-                    textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"),
-                    textAttr(a.getString(R.styleable.Keyboard_Case_languageCode), "languageCode"),
-                    textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), "countryCode"),
-                    Boolean.toString(selected)));
-
-            return selected;
-        } finally {
-            a.recycle();
-        }
-    }
-
-    private static boolean matchInteger(TypedArray a, int index, int value) {
-        // If <case> does not have "index" attribute, that means this <case> is wild-card for the
-        // attribute.
-        return !a.hasValue(index) || a.getInt(index, 0) == value;
-    }
-
-    private static boolean matchBoolean(TypedArray a, int index, boolean value) {
-        // If <case> does not have "index" attribute, that means this <case> is wild-card for the
-        // attribute.
-        return !a.hasValue(index) || a.getBoolean(index, false) == value;
-    }
-
-    private static boolean matchString(TypedArray a, int index, String value) {
-        // If <case> does not have "index" attribute, that means this <case> is wild-card for the
-        // attribute.
-        return !a.hasValue(index) || stringArrayContains(a.getString(index).split("\\|"), value);
-    }
-
-    private static boolean matchTypedValue(TypedArray a, int index, int intValue, String strValue) {
-        // If <case> does not have "index" attribute, that means this <case> is wild-card for the
-        // attribute.
-        final TypedValue v = a.peekValue(index);
-        if (v == null)
-            return true;
-
-        if (isIntegerValue(v)) {
-            return intValue == a.getInt(index, 0);
-        } else if (isStringValue(v)) {
-            return stringArrayContains(a.getString(index).split("\\|"), strValue);
-        }
-        return false;
-    }
-
-    private static boolean stringArrayContains(String[] array, String value) {
-        for (final String elem : array) {
-            if (elem.equals(value))
-                return true;
-        }
-        return false;
-    }
-
-    private boolean parseDefault(XmlPullParser parser, Row row, boolean skip)
-            throws XmlPullParserException, IOException {
-        if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT));
-        if (row == null) {
-            parseKeyboardContent(parser, skip);
-        } else {
-            parseRowContent(parser, row, skip);
-        }
-        return true;
-    }
-
-    private void parseKeyStyle(XmlPullParser parser, boolean skip)
-            throws XmlPullParserException {
-        TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.Keyboard_KeyStyle);
-        TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.Keyboard_Key);
-        try {
-            if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
-                throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
-                        + "/> needs styleName attribute", parser);
-            if (!skip)
-                mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
-        } finally {
-            keyStyleAttr.recycle();
-            keyAttrs.recycle();
-        }
-    }
-
-    private void startKeyboard() {
-        mCurrentY += mParams.mTopPadding;
-        mTopEdge = true;
-    }
-
-    private void startRow(Row row) {
-        addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
-        mCurrentRow = row;
-        mLeftEdge = true;
-        mRightEdgeKey = null;
-    }
-
-    private void endRow(Row row) {
-        if (mCurrentRow == null)
-            throw new InflateException("orphant end row tag");
-        if (mRightEdgeKey != null) {
-            mRightEdgeKey.markAsRightEdge(mParams);
-            mRightEdgeKey = null;
-        }
-        addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
-        mCurrentY += row.mRowHeight;
-        mCurrentRow = null;
-        mTopEdge = false;
-    }
-
-    private void endKey(Key key) {
-        mParams.onAddKey(key);
-        if (mLeftEdge) {
-            key.markAsLeftEdge(mParams);
-            mLeftEdge = false;
-        }
-        if (mTopEdge) {
-            key.markAsTopEdge(mParams);
-        }
-        mRightEdgeKey = key;
-    }
-
-    private void endKeyboard() {
-        // nothing to do here.
-    }
-
-    private void addEdgeSpace(float width, Row row) {
-        row.advanceXPos(width);
-        mLeftEdge = false;
-        mRightEdgeKey = null;
-    }
-
-    public static float getDimensionOrFraction(TypedArray a, int index, int base, float defValue) {
-        final TypedValue value = a.peekValue(index);
-        if (value == null)
-            return defValue;
-        if (isFractionValue(value)) {
-            return a.getFraction(index, base, base, defValue);
-        } else if (isDimensionValue(value)) {
-            return a.getDimension(index, defValue);
-        }
-        return defValue;
-    }
-
-    public static int getEnumValue(TypedArray a, int index, int defValue) {
-        final TypedValue value = a.peekValue(index);
-        if (value == null)
-            return defValue;
-        if (isIntegerValue(value)) {
-            return a.getInt(index, defValue);
-        }
-        return defValue;
-    }
-
-    private static boolean isFractionValue(TypedValue v) {
-        return v.type == TypedValue.TYPE_FRACTION;
-    }
-
-    private static boolean isDimensionValue(TypedValue v) {
-        return v.type == TypedValue.TYPE_DIMENSION;
-    }
-
-    private static boolean isIntegerValue(TypedValue v) {
-        return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
-    }
-
-    private static boolean isStringValue(TypedValue v) {
-        return v.type == TypedValue.TYPE_STRING;
-    }
-
-    private static String textAttr(String value, String name) {
-        return value != null ? String.format(" %s=%s", name, value) : "";
-    }
-
-    private static String booleanAttr(TypedArray a, int index, String name) {
-        return a.hasValue(index) ? String.format(" %s=%s", name, a.getBoolean(index, false)) : "";
-    }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
deleted file mode 100644
index 3e345c42a0..0000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.latin.LatinImeLogger;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-public class KeyboardParams {
-    public KeyboardId mId;
-    public int mThemeId;
-
-    /** Total height and width of the keyboard, including the paddings and keys */
-    public int mOccupiedHeight;
-    public int mOccupiedWidth;
-
-    /** Base height and width of the keyboard used to calculate rows' or keys' heights and widths */
-    public int mBaseHeight;
-    public int mBaseWidth;
-
-    public int mTopPadding;
-    public int mBottomPadding;
-    public int mHorizontalEdgesPadding;
-    public int mHorizontalCenterPadding;
-
-    public int mDefaultRowHeight;
-    public int mDefaultKeyWidth;
-    public int mHorizontalGap;
-    public int mVerticalGap;
-
-    public boolean mIsRtlKeyboard;
-    public int mMoreKeysTemplate;
-    public int mMaxMiniKeyboardColumn;
-
-    public int GRID_WIDTH;
-    public int GRID_HEIGHT;
-
-    public final Set<Key> mKeys = new HashSet<Key>();
-    public final Set<Key> mShiftKeys = new HashSet<Key>();
-    public final Set<Key> mShiftLockKeys = new HashSet<Key>();
-    public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
-
-    public int mMostCommonKeyHeight = 0;
-    public int mMostCommonKeyWidth = 0;
-
-    public final TouchPositionCorrection mTouchPositionCorrection = new TouchPositionCorrection();
-
-    public static class TouchPositionCorrection {
-        private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3;
-
-        public boolean mEnabled;
-        public float[] mXs;
-        public float[] mYs;
-        public float[] mRadii;
-
-        public void load(String[] data) {
-            final int dataLength = data.length;
-            if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) {
-                if (LatinImeLogger.sDBG)
-                    throw new RuntimeException(
-                            "the size of touch position correction data is invalid");
-                return;
-            }
-
-            final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
-            mXs = new float[length];
-            mYs = new float[length];
-            mRadii = new float[length];
-            try {
-                for (int i = 0; i < dataLength; ++i) {
-                    final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE;
-                    final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
-                    final float value = Float.parseFloat(data[i]);
-                    if (type == 0) {
-                        mXs[index] = value;
-                    } else if (type == 1) {
-                        mYs[index] = value;
-                    } else {
-                        mRadii[index] = value;
-                    }
-                }
-            } catch (NumberFormatException e) {
-                if (LatinImeLogger.sDBG) {
-                    throw new RuntimeException(
-                            "the number format for touch position correction data is invalid");
-                }
-                mXs = null;
-                mYs = null;
-                mRadii = null;
-            }
-        }
-
-        public void setEnabled(boolean enabled) {
-            mEnabled = enabled;
-        }
-
-        public boolean isValid() {
-            return mEnabled && mXs != null && mYs != null && mRadii != null
-                && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0;
-        }
-    }
-
-    protected void clearKeys() {
-        mKeys.clear();
-        mShiftKeys.clear();
-        mShiftLockKeys.clear();
-        clearHistogram();
-    }
-
-    public void onAddKey(Key key) {
-        mKeys.add(key);
-        updateHistogram(key);
-        if (key.mCode == Keyboard.CODE_SHIFT) {
-            mShiftKeys.add(key);
-            if (key.isSticky()) {
-                mShiftLockKeys.add(key);
-            }
-        }
-    }
-
-    private int mMaxHeightCount = 0;
-    private int mMaxWidthCount = 0;
-    private final Map<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>();
-    private final Map<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>();
-
-    private void clearHistogram() {
-        mMostCommonKeyHeight = 0;
-        mMaxHeightCount = 0;
-        mHeightHistogram.clear();
-
-        mMaxWidthCount = 0;
-        mMostCommonKeyWidth = 0;
-        mWidthHistogram.clear();
-    }
-
-    private static int updateHistogramCounter(Map<Integer, Integer> histogram, Integer key) {
-        final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1;
-        histogram.put(key, count);
-        return count;
-    }
-
-    private void updateHistogram(Key key) {
-        final Integer height = key.mHeight + key.mVerticalGap;
-        final int heightCount = updateHistogramCounter(mHeightHistogram, height);
-        if (heightCount > mMaxHeightCount) {
-            mMaxHeightCount = heightCount;
-            mMostCommonKeyHeight = height;
-        }
-
-        final Integer width = key.mWidth + key.mHorizontalGap;
-        final int widthCount = updateHistogramCounter(mWidthHistogram, width);
-        if (widthCount > mMaxWidthCount) {
-            mMaxWidthCount = widthCount;
-            mMostCommonKeyWidth = width;
-        }
-    }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/XmlParseUtils.java b/java/src/com/android/inputmethod/latin/XmlParseUtils.java
similarity index 94%
rename from java/src/com/android/inputmethod/keyboard/internal/XmlParseUtils.java
rename to java/src/com/android/inputmethod/latin/XmlParseUtils.java
index 170be347b1..d747a024ce 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/XmlParseUtils.java
+++ b/java/src/com/android/inputmethod/latin/XmlParseUtils.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard.internal;
+package com.android.inputmethod.latin;
 
 import android.content.res.TypedArray;
 
@@ -47,14 +47,14 @@ public class XmlParseUtils {
     }
 
     @SuppressWarnings("serial")
-    static class IllegalAttribute extends ParseException {
+    public static class IllegalAttribute extends ParseException {
         public IllegalAttribute(XmlPullParser parser, String attribute) {
             super("Tag " + parser.getName() + " has illegal attribute " + attribute, parser);
         }
     }
 
     @SuppressWarnings("serial")
-    static class NonEmptyTag extends ParseException{
+    public static class NonEmptyTag extends ParseException{
         public NonEmptyTag(String tag, XmlPullParser parser) {
             super(tag + " must be empty tag", parser);
         }
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index b479ff4ce4..3d26d972d4 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -25,8 +25,6 @@ import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.KeyboardView;
-import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
-import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SuggestedWords;
@@ -39,7 +37,7 @@ public class MoreSuggestions extends Keyboard {
         super(params);
     }
 
-    public static class Builder extends KeyboardBuilder<Builder.MoreSuggestionsParam> {
+    public static class Builder extends Keyboard.Builder<Builder.MoreSuggestionsParam> {
         private static final boolean DBG = LatinImeLogger.sDBG;
 
         private final MoreSuggestionsView mPaneView;
@@ -47,7 +45,7 @@ public class MoreSuggestions extends Keyboard {
         private int mFromPos;
         private int mToPos;
 
-        public static class MoreSuggestionsParam extends KeyboardParams {
+        public static class MoreSuggestionsParam extends Keyboard.Params {
             private final int[] mWidths = new int[SuggestionsView.MAX_SUGGESTIONS];
             private final int[] mRowNumbers = new int[SuggestionsView.MAX_SUGGESTIONS];
             private final int[] mColumnOrders = new int[SuggestionsView.MAX_SUGGESTIONS];
diff --git a/tests/src/com/android/inputmethod/latin/SuggestHelper.java b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
index 77496db487..cccd1a4a93 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestHelper.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
@@ -22,8 +22,6 @@ import android.text.TextUtils;
 import com.android.inputmethod.keyboard.KeyDetector;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
-import com.android.inputmethod.keyboard.internal.KeyboardParams;
 
 import java.io.File;
 import java.util.Locale;
@@ -40,7 +38,7 @@ public class SuggestHelper {
         // Use null as the locale for Suggest so as to force it to use the internal dictionary
         // (and not try to find a dictionary provider for a specified locale)
         mSuggest = new Suggest(context, dictionaryId, null);
-        mKeyboard = new KeyboardBuilder<KeyboardParams>(context, new KeyboardParams())
+        mKeyboard = new Keyboard.Builder<Keyboard.Params>(context, new Keyboard.Params())
                 .load(ALPHABET_KEYBOARD, keyboardId).build();
         mKeyDetector = new KeyDetector(0);
         init();
@@ -50,7 +48,7 @@ public class SuggestHelper {
             final long startOffset, final long length, final KeyboardId keyboardId,
             final Locale locale) {
         mSuggest = new Suggest(context, dictionaryPath, startOffset, length, null, locale);
-        mKeyboard = new KeyboardBuilder<KeyboardParams>(context, new KeyboardParams())
+        mKeyboard = new Keyboard.Builder<Keyboard.Params>(context, new Keyboard.Params())
                 .load(ALPHABET_KEYBOARD, keyboardId).build();
         mKeyDetector = new KeyDetector(0);
         init();
-- 
GitLab