Merge "Breakdown getWordRec"
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index c268abb..87d128f 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -35,47 +35,48 @@
      * All the key codes (unicode or custom code) that this key could generate, zero'th
      * being the most important.
      */
-    public int[] mCodes;
+    public final int[] mCodes;
     /** The unicode that this key generates in manual temporary upper case mode. */
-    public int mManualTemporaryUpperCaseCode;
+    public final int mManualTemporaryUpperCaseCode;
 
     /** Label to display */
-    public CharSequence mLabel;
+    public final CharSequence mLabel;
     /** Option of the label */
-    public int mLabelOption;
+    public final int mLabelOption;
 
     /** Icon to display instead of a label. Icon takes precedence over a label */
-    public Drawable mIcon;
-    /** Hint icon to display on the key in conjunction with the label */
-    public Drawable mHintIcon;
+    private Drawable mIcon;
     /** Preview version of the icon, for the preview popup */
+    private Drawable mPreviewIcon;
+    /** Hint icon to display on the key in conjunction with the label */
+    public final Drawable mHintIcon;
     /**
      * The hint icon to display on the key when keyboard is in manual temporary upper case
      * mode.
      */
-    public Drawable mManualTemporaryUpperCaseHintIcon;
+    public final Drawable mManualTemporaryUpperCaseHintIcon;
 
-    public Drawable mPreviewIcon;
     /** Width of the key, not including the gap */
-    public int mWidth;
+    public final int mWidth;
     /** Height of the key, not including the gap */
-    public int mHeight;
+    public final int mHeight;
     /** The horizontal gap before this key */
-    public int mGap;
+    public final int mGap;
     /** Whether this key is sticky, i.e., a toggle key */
-    public boolean mSticky;
+    public final boolean mSticky;
     /** X coordinate of the key in the keyboard layout */
-    public int mX;
+    public final int mX;
     /** Y coordinate of the key in the keyboard layout */
-    public int mY;
-    /** The current pressed state of this key */
-    public boolean mPressed;
-    /** If this is a sticky key, is it on? */
-    public boolean mOn;
+    public final int mY;
     /** Text to output when pressed. This can be multiple characters, like ".com" */
-    public CharSequence mOutputText;
+    public final CharSequence mOutputText;
     /** Popup characters */
-    public CharSequence mPopupCharacters;
+    public final CharSequence mPopupCharacters;
+    /**
+     * If this key pops up a mini keyboard, this is the resource id for the XML layout for that
+     * keyboard.
+     */
+    public final int mPopupResId;
 
     /**
      * Flags that specify the anchoring to edges of the keyboard for detecting touch events
@@ -83,19 +84,19 @@
      * {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT},
      * {@link Keyboard#EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM}.
      */
-    public int mEdgeFlags;
+    public final int mEdgeFlags;
     /** Whether this is a modifier key, such as Shift or Alt */
-    public boolean mModifier;
-    /** The Keyboard that this key belongs to */
-    protected final Keyboard mKeyboard;
-    /**
-     * If this key pops up a mini keyboard, this is the resource id for the XML layout for that
-     * keyboard.
-     */
-    public int mPopupResId;
+    public final boolean mModifier;
     /** Whether this key repeats itself when held down */
-    public boolean mRepeatable;
+    public final boolean mRepeatable;
 
+    /** The Keyboard that this key belongs to */
+    private final Keyboard mKeyboard;
+
+    /** The current pressed state of this key */
+    public boolean mPressed;
+    /** If this is a sticky key, is it on? */
+    public boolean mOn;
 
     private final static int[] KEY_STATE_NORMAL_ON = {
         android.R.attr.state_checkable,
@@ -136,38 +137,53 @@
     };
 
     /** Create an empty key with no attributes. */
-    public Key(Row parent) {
-        mKeyboard = parent.mParent;
-        mHeight = parent.mDefaultHeight;
-        mGap = parent.mDefaultHorizontalGap;
-        mWidth = parent.mDefaultWidth - mGap;
-        mEdgeFlags = parent.mRowEdgeFlags;
+    public Key(Row row, char letter, int x, int y) {
+        mKeyboard = row.getKeyboard();
+        mHeight = row.mDefaultHeight;
+        mGap = row.mDefaultHorizontalGap;
+        mWidth = row.mDefaultWidth - mGap;
+        mEdgeFlags = row.mRowEdgeFlags;
+        mHintIcon = null;
+        mManualTemporaryUpperCaseHintIcon = null;
+        mManualTemporaryUpperCaseCode = 0;
+        mLabelOption = 0;
+        mModifier = false;
+        mSticky = false;
+        mRepeatable = false;
+        mOutputText = null;
+        mPopupCharacters = null;
+        mPopupResId = 0;
+        mLabel = String.valueOf(letter);
+        mCodes = new int[] { letter };
+        // Horizontal gap is divided equally to both sides of the key.
+        mX = x + mGap / 2;
+        mY = y;
     }
 
     /** Create a key with the given top-left coordinate and extract its attributes from
      * the XML parser.
      * @param res resources associated with the caller's context
-     * @param parent the row that this key belongs to. The row must already be attached to
+     * @param row the row that this key belongs to. The row must already be attached to
      * a {@link Keyboard}.
      * @param x the x coordinate of the top-left
      * @param y the y coordinate of the top-left
      * @param parser the XML parser containing the attributes for this key
      */
-    public Key(Resources res, Row parent, int x, int y, XmlResourceParser parser,
+    public Key(Resources res, Row row, int x, int y, XmlResourceParser parser,
             KeyStyles keyStyles) {
-        this(parent);
+        mKeyboard = row.getKeyboard();
 
         TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard);
         mHeight = KeyboardParser.getDimensionOrFraction(a,
                 R.styleable.Keyboard_keyHeight,
-                mKeyboard.mDisplayHeight, parent.mDefaultHeight);
+                mKeyboard.getKeyboardHeight(), row.mDefaultHeight);
         mGap = KeyboardParser.getDimensionOrFraction(a,
                 R.styleable.Keyboard_horizontalGap,
-                mKeyboard.mDisplayWidth, parent.mDefaultHorizontalGap);
+                mKeyboard.getKeyboardWidth(), row.mDefaultHorizontalGap);
         mWidth = KeyboardParser.getDimensionOrFraction(a,
                 R.styleable.Keyboard_keyWidth,
-                mKeyboard.mDisplayWidth, parent.mDefaultWidth) - mGap;
+                mKeyboard.getKeyboardWidth(), row.mDefaultWidth) - mGap;
         a.recycle();
 
         a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Key);
@@ -186,7 +202,7 @@
         this.mX = x + mGap / 2;
         this.mY = y;
 
-        mCodes = style.getIntArray(a, R.styleable.Keyboard_Key_codes);
+        int[] codes = style.getIntArray(a, R.styleable.Keyboard_Key_codes);
         mPreviewIcon = style.getDrawable(a, R.styleable.Keyboard_Key_iconPreview);
         Keyboard.setDefaultBounds(mPreviewIcon);
         mPopupCharacters = style.getText(a, R.styleable.Keyboard_Key_popupCharacters);
@@ -194,8 +210,8 @@
         mRepeatable = style.getBoolean(a, R.styleable.Keyboard_Key_isRepeatable, false);
         mModifier = style.getBoolean(a, R.styleable.Keyboard_Key_isModifier, false);
         mSticky = style.getBoolean(a, R.styleable.Keyboard_Key_isSticky, false);
-        mEdgeFlags = style.getFlag(a, R.styleable.Keyboard_Key_keyEdgeFlags, 0);
-        mEdgeFlags |= parent.mRowEdgeFlags;
+        mEdgeFlags = style.getFlag(a, R.styleable.Keyboard_Key_keyEdgeFlags, 0)
+                | row.mRowEdgeFlags;
 
         mIcon = style.getDrawable(a, R.styleable.Keyboard_Key_keyIcon);
         Keyboard.setDefaultBounds(mIcon);
@@ -217,14 +233,25 @@
         if (shiftedIcon != null)
             mKeyboard.getShiftedIcons().put(this, shiftedIcon);
 
-        if (mCodes == null && !TextUtils.isEmpty(mLabel)) {
-            mCodes = new int[] { mLabel.charAt(0) };
-        }
+        if (codes == null && !TextUtils.isEmpty(mLabel))
+            codes = new int[] { mLabel.charAt(0) };
+        mCodes = codes;
+    }
 
-        if (mPopupCharacters == null || mPopupCharacters.length() == 0) {
-            // If there is a keyboard with no keys specified in popupCharacters
-            mPopupResId = 0;
-        }
+    public Drawable getIcon() {
+        return mIcon;
+    }
+
+    public Drawable getPreviewIcon() {
+        return mPreviewIcon;
+    }
+
+    public void setIcon(Drawable icon) {
+        mIcon = icon;
+    }
+
+    public void setPreviewIcon(Drawable icon) {
+        mPreviewIcon = icon;
     }
 
     /**
@@ -248,27 +275,29 @@
             mOn = !mOn;
     }
 
+    public boolean isInside(int x, int y) {
+        return mKeyboard.isInside(this, x, y);
+    }
+
     /**
-     * Detects if a point falls inside this key.
+     * Detects if a point falls on this key.
      * @param x the x-coordinate of the point
      * @param y the y-coordinate of the point
-     * @return whether or not the point falls inside the key. If the key is attached to an
-     * edge, it will assume that all points between the key and the edge are considered to be
-     * inside the key.
+     * @return whether or not the point falls on the key. If the key is attached to an edge, it will
+     * assume that all points between the key and the edge are considered to be on the key.
      */
-    public boolean isInside(int x, int y) {
-        boolean leftEdge = (mEdgeFlags & Keyboard.EDGE_LEFT) > 0;
-        boolean rightEdge = (mEdgeFlags & Keyboard.EDGE_RIGHT) > 0;
-        boolean topEdge = (mEdgeFlags & Keyboard.EDGE_TOP) > 0;
-        boolean bottomEdge = (mEdgeFlags & Keyboard.EDGE_BOTTOM) > 0;
-        if ((x >= this.mX || (leftEdge && x <= this.mX + this.mWidth))
-                && (x < this.mX + this.mWidth || (rightEdge && x >= this.mX))
-                && (y >= this.mY || (topEdge && y <= this.mY + this.mHeight))
-                && (y < this.mY + this.mHeight || (bottomEdge && y >= this.mY))) {
-            return true;
-        } else {
-            return false;
-        }
+    public boolean isOnKey(int x, int y) {
+        final int flags = mEdgeFlags;
+        final boolean leftEdge = (flags & Keyboard.EDGE_LEFT) != 0;
+        final boolean rightEdge = (flags & Keyboard.EDGE_RIGHT) != 0;
+        final boolean topEdge = (flags & Keyboard.EDGE_TOP) != 0;
+        final boolean bottomEdge = (flags & Keyboard.EDGE_BOTTOM) != 0;
+        final int left = this.mX;
+        final int right = left + this.mWidth;
+        final int top = this.mY;
+        final int bottom = top + this.mHeight;
+        return (x >= left || leftEdge) && (x < right || rightEdge)
+                && (y >= top || topEdge) && (y < bottom || bottomEdge);
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 869d130..3801986 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -80,16 +80,16 @@
     public static final int CODE_VOICE = -109;
 
     /** Horizontal gap default for all rows */
-    int mDefaultHorizontalGap;
+    private int mDefaultHorizontalGap;
 
     /** Default key width */
-    int mDefaultWidth;
+    private int mDefaultWidth;
 
     /** Default key height */
-    int mDefaultHeight;
+    private int mDefaultHeight;
 
     /** Default gap between rows */
-    int mDefaultVerticalGap;
+    private int mDefaultVerticalGap;
 
     /** List of shift keys in this keyboard and its icons and state */
     private final List<Key> mShiftKeys = new ArrayList<Key>();
@@ -116,12 +116,12 @@
     private final List<Key> mKeys = new ArrayList<Key>();
 
     /** Width of the screen available to fit the keyboard */
-    final int mDisplayWidth;
+    private final int mDisplayWidth;
 
     /** Height of the screen */
-    final int mDisplayHeight;
+    private final int mDisplayHeight;
 
-    protected final KeyboardId mId;
+    public final KeyboardId mId;
 
     // Variables for pre-computing nearest keys.
 
@@ -206,12 +206,7 @@
         int column = 0;
         mTotalWidth = 0;
 
-        Row row = new Row(this);
-        row.mDefaultHeight = mDefaultHeight;
-        row.mDefaultWidth = mDefaultWidth;
-        row.mDefaultHorizontalGap = mDefaultHorizontalGap;
-        row.mVerticalGap = mDefaultVerticalGap;
-        row.mRowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;
+        final Row row = new Row(this);
         final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
         for (int i = 0; i < characters.length(); i++) {
             char c = characters.charAt(i);
@@ -221,12 +216,7 @@
                 y += mDefaultVerticalGap + mDefaultHeight;
                 column = 0;
             }
-            final Key key = new Key(row);
-            // Horizontal gap is divided equally to both sides of the key.
-            key.mX = x + key.mGap / 2;
-            key.mY = y;
-            key.mLabel = String.valueOf(c);
-            key.mCodes = new int[] { c };
+            final Key key = new Key(row, c, x, y);
             column++;
             x += key.mWidth + key.mGap;
             mKeys.add(key);
@@ -237,43 +227,39 @@
         mTotalHeight = y + mDefaultHeight;
     }
 
-    public KeyboardId getKeyboardId() {
-        return mId;
-    }
-
     public List<Key> getKeys() {
         return mKeys;
     }
 
-    protected int getHorizontalGap() {
+    public int getHorizontalGap() {
         return mDefaultHorizontalGap;
     }
 
-    protected void setHorizontalGap(int gap) {
+    public void setHorizontalGap(int gap) {
         mDefaultHorizontalGap = gap;
     }
 
-    protected int getVerticalGap() {
+    public int getVerticalGap() {
         return mDefaultVerticalGap;
     }
 
-    protected void setVerticalGap(int gap) {
+    public void setVerticalGap(int gap) {
         mDefaultVerticalGap = gap;
     }
 
-    protected int getKeyHeight() {
+    public int getKeyHeight() {
         return mDefaultHeight;
     }
 
-    protected void setKeyHeight(int height) {
+    public void setKeyHeight(int height) {
         mDefaultHeight = height;
     }
 
-    protected int getKeyWidth() {
+    public int getKeyWidth() {
         return mDefaultWidth;
     }
 
-    protected void setKeyWidth(int width) {
+    public void setKeyWidth(int width) {
         mDefaultWidth = width;
         final int threshold = (int) (width * SEARCH_DISTANCE);
         mProximityThreshold = threshold * threshold;
@@ -310,7 +296,7 @@
     public void enableShiftLock() {
         for (final Key key : getShiftKeys()) {
             mShiftLockEnabled.add(key);
-            mNormalShiftIcons.put(key, key.mIcon);
+            mNormalShiftIcons.put(key, key.getIcon());
         }
     }
 
@@ -322,7 +308,7 @@
         final Map<Key, Drawable> shiftedIcons = getShiftedIcons();
         for (final Key key : getShiftKeys()) {
             key.mOn = newShiftLockState;
-            key.mIcon = newShiftLockState ? shiftedIcons.get(key) : mNormalShiftIcons.get(key);
+            key.setIcon(newShiftLockState ? shiftedIcons.get(key) : mNormalShiftIcons.get(key));
         }
         mShiftState.setShiftLocked(newShiftLockState);
         return true;
@@ -336,9 +322,9 @@
         final Map<Key, Drawable> shiftedIcons = getShiftedIcons();
         for (final Key key : getShiftKeys()) {
             if (!newShiftState && !mShiftState.isShiftLocked()) {
-                key.mIcon = mNormalShiftIcons.get(key);
+                key.setIcon(mNormalShiftIcons.get(key));
             } else if (newShiftState && !mShiftState.isShiftedOrShiftLocked()) {
-                key.mIcon = shiftedIcons.get(key);
+                key.setIcon(shiftedIcons.get(key));
             }
         }
         return mShiftState.setShifted(newShiftState);
@@ -366,21 +352,21 @@
     }
 
     public boolean isAlphaKeyboard() {
-        return mId.isAlphabetKeyboard();
+        return mId != null && mId.isAlphabetKeyboard();
     }
 
     public boolean isPhoneKeyboard() {
-        return mId.isPhoneKeyboard();
+        return mId != null && mId.isPhoneKeyboard();
     }
 
     public boolean isNumberKeyboard() {
-        return mId.isNumberKeyboard();
+        return mId != null && mId.isNumberKeyboard();
     }
 
     public void setSpaceKey(Key space) {
         mSpaceKey = space;
-        mSpaceIcon = space.mIcon;
-        mSpacePreviewIcon = space.mPreviewIcon;
+        mSpaceIcon = space.getIcon();
+        mSpacePreviewIcon = space.getPreviewIcon();
     }
 
     private void computeNearestNeighbors() {
@@ -409,6 +395,10 @@
         }
     }
 
+    public boolean isInside(Key key, int x, int y) {
+        return key.isOnKey(x, y);
+    }
+
     /**
      * Returns the indices of the keys that are closest to the given point.
      * @param x the x-coordinate of the point
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 509b5b6..c54b1b4 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -758,9 +758,10 @@
                 paint.setShadowLayer(0, 0, 0, 0);
             }
             // Draw key icon
-            if (key.mLabel == null && key.mIcon != null) {
-                final int drawableWidth = key.mIcon.getIntrinsicWidth();
-                final int drawableHeight = key.mIcon.getIntrinsicHeight();
+            final Drawable icon = key.getIcon();
+            if (key.mLabel == null && icon != null) {
+                final int drawableWidth = icon.getIntrinsicWidth();
+                final int drawableHeight = icon.getIntrinsicHeight();
                 final int drawableX;
                 final int drawableY = (
                         key.mHeight + padding.top - padding.bottom - drawableHeight) / 2;
@@ -780,7 +781,7 @@
                         drawVerticalLine(canvas, drawableX + drawableWidth / 2, rowHeight,
                                 0xc0008080, new Paint());
                 }
-                drawIcon(canvas, key.mIcon, drawableX, drawableY, drawableWidth, drawableHeight);
+                drawIcon(canvas, icon, drawableX, drawableY, drawableWidth, drawableHeight);
                 if (DEBUG_SHOW_ALIGN)
                     drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
                             0x80c00000, new Paint());
@@ -790,10 +791,10 @@
                 final int drawableHeight = key.mHeight;
                 final int drawableX = 0;
                 final int drawableY = HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL;
-                Drawable icon = (isManualTemporaryUpperCase
+                Drawable hintIcon = (isManualTemporaryUpperCase
                         && key.mManualTemporaryUpperCaseHintIcon != null)
                         ? key.mManualTemporaryUpperCaseHintIcon : key.mHintIcon;
-                drawIcon(canvas, icon, drawableX, drawableY, drawableWidth, drawableHeight);
+                drawIcon(canvas, hintIcon, drawableX, drawableY, drawableWidth, drawableHeight);
                 if (DEBUG_SHOW_ALIGN)
                     drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
                             0x80c0c000, new Paint());
@@ -965,8 +966,9 @@
                 mPreviewText.setTypeface(mKeyLetterStyle);
             }
         } else {
+            final Drawable previewIcon = key.getPreviewIcon();
             mPreviewText.setCompoundDrawables(null, null, null,
-                    key.mPreviewIcon != null ? key.mPreviewIcon : key.mIcon);
+                   previewIcon != null ? previewIcon : key.getIcon());
             mPreviewText.setText(null);
         }
         mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKey.java b/java/src/com/android/inputmethod/keyboard/LatinKey.java
deleted file mode 100644
index 1df4227..0000000
--- a/java/src/com/android/inputmethod/keyboard/LatinKey.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
-
-// TODO: We should remove this class
-public class LatinKey extends Key {
-    public LatinKey(Resources res, Row parent, int x, int y,
-            XmlResourceParser parser, KeyStyles keyStyles) {
-        super(res, parent, x, y, parser, keyStyles);
-    }
-
-    /**
-     * Overriding this method so that we can reduce the target area for certain keys.
-     */
-    @Override
-    public boolean isInside(int x, int y) {
-        boolean result = (mKeyboard instanceof LatinKeyboard)
-                && ((LatinKeyboard)mKeyboard).isInside(this, x, y);
-        return result;
-    }
-
-    boolean isInsideSuper(int x, int y) {
-        return super.isInside(x, y);
-    }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index 9348748..e612b52 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -22,7 +22,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -98,12 +97,6 @@
         mSpaceKeyIndex = indexOf(CODE_SPACE);
     }
 
-    @Override
-    protected Key createKeyFromXml(Resources res, Row parent, int x, int y, 
-            XmlResourceParser parser, KeyStyles keyStyles) {
-        return new LatinKey(res, parent, x, y, parser, keyStyles);
-    }
-
     /**
      * @return a key which should be invalidated.
      */
@@ -116,15 +109,15 @@
         final Resources res = mRes;
         // If application locales are explicitly selected.
         if (SubtypeSwitcher.getInstance().needsToDisplayLanguage()) {
-            mSpaceKey.mIcon = new BitmapDrawable(res,
-                    drawSpaceBar(OPACITY_FULLY_OPAQUE, isAutoCompletion));
+            mSpaceKey.setIcon(new BitmapDrawable(res,
+                    drawSpaceBar(OPACITY_FULLY_OPAQUE, isAutoCompletion)));
         } else {
             // sym_keyboard_space_led can be shared with Black and White symbol themes.
             if (isAutoCompletion) {
-                mSpaceKey.mIcon = new BitmapDrawable(res,
-                        drawSpaceBar(OPACITY_FULLY_OPAQUE, isAutoCompletion));
+                mSpaceKey.setIcon(new BitmapDrawable(res,
+                        drawSpaceBar(OPACITY_FULLY_OPAQUE, isAutoCompletion)));
             } else {
-                mSpaceKey.mIcon = mSpaceIcon;
+                mSpaceKey.setIcon(mSpaceIcon);
             }
         }
     }
@@ -245,15 +238,15 @@
             mSlidingLocaleIcon =
                     new SlidingLocaleDrawable(mContext, mSpacePreviewIcon, width, height);
             mSlidingLocaleIcon.setBounds(0, 0, width, height);
-            mSpaceKey.mPreviewIcon = mSlidingLocaleIcon;
+            mSpaceKey.setPreviewIcon(mSlidingLocaleIcon);
         }
         mSlidingLocaleIcon.setDiff(diff);
         if (Math.abs(diff) == Integer.MAX_VALUE) {
-            mSpaceKey.mPreviewIcon = mSpacePreviewIcon;
+            mSpaceKey.setPreviewIcon(mSpacePreviewIcon);
         } else {
-            mSpaceKey.mPreviewIcon = mSlidingLocaleIcon;
+            mSpaceKey.setPreviewIcon(mSlidingLocaleIcon);
         }
-        mSpaceKey.mPreviewIcon.invalidateSelf();
+        mSpaceKey.getPreviewIcon().invalidateSelf();
     }
 
     public int getLanguageChangeDirection() {
@@ -264,10 +257,6 @@
         return mSpaceDragLastDiff > 0 ? 1 : -1;
     }
 
-    boolean isCurrentlyInSpace() {
-        return mCurrentlyInSpace;
-    }
-
     public void setPreferredLetters(int[] frequencies) {
         mPrefLetterFrequencies = frequencies;
         mPrefLetter = 0;
@@ -289,8 +278,9 @@
      * Does the magic of locking the touch gesture into the spacebar when
      * switching input languages.
      */
-    @SuppressWarnings("unused")
-    public boolean isInside(LatinKey key, int x, int y) {
+    @Override
+    @SuppressWarnings("unused") // SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCHER is constant
+    public boolean isInside(Key key, int x, int y) {
         final int code = key.mCodes[0];
         if (code == CODE_SHIFT || code == CODE_DELETE) {
             y -= key.mHeight / 10;
@@ -308,13 +298,13 @@
                     mSpaceDragLastDiff = diff;
                     return true;
                 } else {
-                    boolean insideSpace = key.isInsideSuper(x, y);
-                    if (insideSpace) {
+                    boolean isOnSpace = key.isOnKey(x, y);
+                    if (isOnSpace) {
                         mCurrentlyInSpace = true;
                         mSpaceDragStartX = x;
                         updateLocaleDrag(0);
                     }
-                    return insideSpace;
+                    return isOnSpace;
                 }
             }
         } else if (mPrefLetterFrequencies != null) {
@@ -327,16 +317,16 @@
             final int[] pref = mPrefLetterFrequencies;
             if (mPrefLetter > 0) {
                 if (DEBUG_PREFERRED_LETTER) {
-                    if (mPrefLetter == code && !key.isInsideSuper(x, y)) {
+                    if (mPrefLetter == code && !key.isOnKey(x, y)) {
                         Log.d(TAG, "CORRECTED !!!!!!");
                     }
                 }
                 return mPrefLetter == code;
             } else {
-                final boolean inside = key.isInsideSuper(x, y);
+                final boolean isOnKey = key.isOnKey(x, y);
                 int[] nearby = getNearestKeys(x, y);
                 List<Key> nearbyKeys = getKeys();
-                if (inside) {
+                if (isOnKey) {
                     // If it's a preferred letter
                     if (inPrefList(code, pref)) {
                         // Check if its frequency is much lower than a nearby key
@@ -386,7 +376,7 @@
                 }
                 // Didn't find any
                 if (mPrefLetter == 0) {
-                    return inside;
+                    return isOnKey;
                 } else {
                     return mPrefLetter == code;
                 }
@@ -396,7 +386,7 @@
         // Lock into the spacebar
         if (mCurrentlyInSpace) return false;
 
-        return key.isInsideSuper(x, y);
+        return key.isOnKey(x, y);
     }
 
     private boolean inPrefList(int code, int[] pref) {
diff --git a/java/src/com/android/inputmethod/keyboard/Row.java b/java/src/com/android/inputmethod/keyboard/Row.java
index 0768b1f..37fa4e3 100644
--- a/java/src/com/android/inputmethod/keyboard/Row.java
+++ b/java/src/com/android/inputmethod/keyboard/Row.java
@@ -30,44 +30,52 @@
  */
 public class Row {
     /** Default width of a key in this row. */
-    public int mDefaultWidth;
+    public final int mDefaultWidth;
     /** Default height of a key in this row. */
-    public int mDefaultHeight;
+    public final int mDefaultHeight;
     /** Default horizontal gap between keys in this row. */
-    public int mDefaultHorizontalGap;
+    public final int mDefaultHorizontalGap;
     /** Vertical gap following this row. */
-    public int mVerticalGap;
+    public final int mVerticalGap;
     /**
      * Edge flags for this row of keys. Possible values that can be assigned are
      * {@link Keyboard#EDGE_TOP EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM EDGE_BOTTOM}
      */
-    public int mRowEdgeFlags;
+    public final int mRowEdgeFlags;
 
-    /* package */ final Keyboard mParent;
+    private final Keyboard mKeyboard;
 
-    /* package */ Row(Keyboard parent) {
-        this.mParent = parent;
+    public Row(Keyboard keyboard) {
+        this.mKeyboard = keyboard;
+        mDefaultHeight = keyboard.getKeyHeight();
+        mDefaultWidth = keyboard.getKeyWidth();
+        mDefaultHorizontalGap = keyboard.getHorizontalGap();
+        mVerticalGap = keyboard.getVerticalGap();
+        mRowEdgeFlags = Keyboard.EDGE_TOP | Keyboard.EDGE_BOTTOM;
     }
 
-    public Row(Resources res, Keyboard parent, XmlResourceParser parser) {
-        this.mParent = parent;
+    public Row(Resources res, Keyboard keyboard, XmlResourceParser parser) {
+        this.mKeyboard = keyboard;
+        final int keyboardWidth = keyboard.getKeyboardWidth();
+        final int keyboardHeight = keyboard.getKeyboardHeight();
         TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard);
         mDefaultWidth = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_keyWidth,
-                parent.mDisplayWidth, parent.mDefaultWidth);
+                R.styleable.Keyboard_keyWidth, keyboardWidth, keyboard.getKeyWidth());
         mDefaultHeight = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_keyHeight,
-                parent.mDisplayHeight, parent.mDefaultHeight);
+                R.styleable.Keyboard_keyHeight, keyboardHeight, keyboard.getKeyHeight());
         mDefaultHorizontalGap = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_horizontalGap,
-                parent.mDisplayWidth, parent.mDefaultHorizontalGap);
+                R.styleable.Keyboard_horizontalGap, keyboardWidth, keyboard.getHorizontalGap());
         mVerticalGap = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_verticalGap,
-                parent.mDisplayHeight, parent.mDefaultVerticalGap);
+                R.styleable.Keyboard_verticalGap, keyboardHeight, keyboard.getVerticalGap());
         a.recycle();
         a = res.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard_Row);
         mRowEdgeFlags = a.getInt(R.styleable.Keyboard_Row_rowEdgeFlags, 0);
+        a.recycle();
+    }
+
+    public Keyboard getKeyboard() {
+        return mKeyboard;
     }
 }
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 6ed743c..9948448 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -15,13 +15,11 @@
 ** limitations under the License.
 */
 
-#include <stdio.h>
-#include <assert.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include <jni.h>
 #include "dictionary.h"
+#include "jni.h"
+
+#include <assert.h>
+#include <stdio.h>
 
 // ----------------------------------------------------------------------------
 
@@ -30,8 +28,7 @@
 //
 // helper function to throw an exception
 //
-static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data)
-{
+static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) {
     if (jclass cls = env->FindClass(ex)) {
         char msg[1000];
         sprintf(msg, fmt, data);
@@ -40,11 +37,9 @@
     }
 }
 
-static jint latinime_BinaryDictionary_open
-        (JNIEnv *env, jobject object, jobject dictDirectBuffer,
-         jint typedLetterMultiplier, jint fullWordMultiplier, jint maxWordLength, jint maxWords,
-         jint maxAlternatives)
-{
+static jint latinime_BinaryDictionary_open(JNIEnv *env, jobject object, jobject dictDirectBuffer,
+        jint typedLetterMultiplier, jint fullWordMultiplier, jint maxWordLength, jint maxWords,
+        jint maxAlternatives) {
     void *dict = env->GetDirectBufferAddress(dictDirectBuffer);
     if (dict == NULL) {
         fprintf(stderr, "DICT: Dictionary buffer is null\n");
@@ -55,11 +50,9 @@
     return (jint) dictionary;
 }
 
-static int latinime_BinaryDictionary_getSuggestions(
-        JNIEnv *env, jobject object, jint dict, jintArray inputArray, jint arraySize,
-        jcharArray outputArray, jintArray frequencyArray,
-        jintArray nextLettersArray, jint nextLettersSize)
-{
+static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jint dict,
+        jintArray inputArray, jint arraySize, jcharArray outputArray, jintArray frequencyArray,
+        jintArray nextLettersArray, jint nextLettersSize) {
     Dictionary *dictionary = (Dictionary*) dict;
     if (dictionary == NULL) return 0;
 
@@ -82,11 +75,10 @@
     return count;
 }
 
-static int latinime_BinaryDictionary_getBigrams
-        (JNIEnv *env, jobject object, jint dict, jcharArray prevWordArray, jint prevWordLength,
-         jintArray inputArray, jint inputArraySize, jcharArray outputArray,
-         jintArray frequencyArray, jint maxWordLength, jint maxBigrams, jint maxAlternatives)
-{
+static int latinime_BinaryDictionary_getBigrams(JNIEnv *env, jobject object, jint dict,
+        jcharArray prevWordArray, jint prevWordLength, jintArray inputArray, jint inputArraySize,
+        jcharArray outputArray, jintArray frequencyArray, jint maxWordLength, jint maxBigrams,
+        jint maxAlternatives) {
     Dictionary *dictionary = (Dictionary*) dict;
     if (dictionary == NULL) return 0;
 
@@ -108,9 +100,8 @@
 }
 
 
-static jboolean latinime_BinaryDictionary_isValidWord
-        (JNIEnv *env, jobject object, jint dict, jcharArray wordArray, jint wordLength)
-{
+static jboolean latinime_BinaryDictionary_isValidWord(JNIEnv *env, jobject object, jint dict,
+        jcharArray wordArray, jint wordLength) {
     Dictionary *dictionary = (Dictionary*) dict;
     if (dictionary == NULL) return (jboolean) false;
 
@@ -121,32 +112,27 @@
     return result;
 }
 
-static void latinime_BinaryDictionary_close
-        (JNIEnv *env, jobject object, jint dict)
-{
+static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jint dict) {
     delete (Dictionary*) dict;
 }
 
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
-    {"openNative",           "(Ljava/nio/ByteBuffer;IIIII)I",
-                                          (void*)latinime_BinaryDictionary_open},
-    {"closeNative",          "(I)V",            (void*)latinime_BinaryDictionary_close},
-    {"getSuggestionsNative", "(I[II[C[I[II)I",  (void*)latinime_BinaryDictionary_getSuggestions},
-    {"isValidWordNative",    "(I[CI)Z",         (void*)latinime_BinaryDictionary_isValidWord},
-    {"getBigramsNative",    "(I[CI[II[C[IIII)I",         (void*)latinime_BinaryDictionary_getBigrams}
+    {"openNative", "(Ljava/nio/ByteBuffer;IIIII)I", (void*)latinime_BinaryDictionary_open},
+    {"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close},
+    {"getSuggestionsNative", "(I[II[C[I[II)I", (void*)latinime_BinaryDictionary_getSuggestions},
+    {"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord},
+    {"getBigramsNative", "(I[CI[II[C[IIII)I", (void*)latinime_BinaryDictionary_getBigrams}
 };
 
-static int registerNativeMethods(JNIEnv* env, const char* className,
-    JNINativeMethod* gMethods, int numMethods)
-{
+static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods,
+        int numMethods) {
     jclass clazz;
 
     clazz = env->FindClass(className);
     if (clazz == NULL) {
-        fprintf(stderr,
-            "Native registration unable to find class '%s'\n", className);
+        fprintf(stderr, "Native registration unable to find class '%s'\n", className);
         return JNI_FALSE;
     }
     if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
@@ -157,18 +143,16 @@
     return JNI_TRUE;
 }
 
-static int registerNatives(JNIEnv *env)
-{
+static int registerNatives(JNIEnv *env) {
     const char* const kClassPathName = "com/android/inputmethod/latin/BinaryDictionary";
-    return registerNativeMethods(env,
-            kClassPathName, gMethods, sizeof(gMethods) / sizeof(gMethods[0]));
+    return registerNativeMethods(env, kClassPathName, gMethods,
+            sizeof(gMethods) / sizeof(gMethods[0]));
 }
 
 /*
  * Returns the JNI version on success, -1 on failure.
  */
-jint JNI_OnLoad(JavaVM* vm, void* reserved)
-{
+jint JNI_OnLoad(JavaVM* vm, void* reserved) {
     JNIEnv* env = NULL;
     jint result = -1;