diff --git a/java/res/layout-xlarge/keyboard_popup_honeycomb.xml b/java/res/layout-xlarge/keyboard_popup_honeycomb.xml
index 5a91dbeb0b2d1902a99608102303d17508897351..0b8229ca5f79fa6142641d4e61e8477eb49b3bdd 100644
--- a/java/res/layout-xlarge/keyboard_popup_honeycomb.xml
+++ b/java/res/layout-xlarge/keyboard_popup_honeycomb.xml
@@ -26,9 +26,9 @@
         android:paddingLeft="40dip"
         android:paddingRight="40dip"
         >
-    <com.android.inputmethod.latin.BaseKeyboardView
+    <com.android.inputmethod.keyboard.KeyboardView
             xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-            android:id="@+id/BaseKeyboardView"
+            android:id="@+id/KeyboardView"
             android:layout_alignParentBottom="true"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/java/res/layout/input_basic.xml b/java/res/layout/input_basic.xml
index 168eba69194095bda17565fe34119dd1dd2c336a..7b85bae944152afaca3758c62db38ea06b9782fc 100644
--- a/java/res/layout/input_basic.xml
+++ b/java/res/layout/input_basic.xml
@@ -18,7 +18,7 @@
 */
 -->
 
-<com.android.inputmethod.latin.LatinKeyboardView
+<com.android.inputmethod.keyboard.LatinKeyboardView
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
         android:id="@+id/LatinkeyboardBaseView"
diff --git a/java/res/layout/input_basic_highcontrast.xml b/java/res/layout/input_basic_highcontrast.xml
index 19ff1db112d02983a7596a997e99d5e35c5e2c78..d9200fd5e8f6c8ff80dbcbd57fa60d142ce24044 100644
--- a/java/res/layout/input_basic_highcontrast.xml
+++ b/java/res/layout/input_basic_highcontrast.xml
@@ -18,7 +18,7 @@
 */
 -->
 
-<com.android.inputmethod.latin.LatinKeyboardView
+<com.android.inputmethod.keyboard.LatinKeyboardView
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 
diff --git a/java/res/layout/input_gingerbread.xml b/java/res/layout/input_gingerbread.xml
index fe31321f1a8fde4881a531817e8d8e60f20a8447..6233e6dc67f308291af71ba98aa5521a56dc495e 100644
--- a/java/res/layout/input_gingerbread.xml
+++ b/java/res/layout/input_gingerbread.xml
@@ -18,7 +18,7 @@
 */
 -->
 
-<com.android.inputmethod.latin.LatinKeyboardView
+<com.android.inputmethod.keyboard.LatinKeyboardView
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
         android:id="@+id/LatinkeyboardBaseView"
diff --git a/java/res/layout/input_honeycomb.xml b/java/res/layout/input_honeycomb.xml
index 079e70c651fa58d410764001677165d66e5b6eee..b39387481dd07dc90cded3380a7b886b1420ce81 100644
--- a/java/res/layout/input_honeycomb.xml
+++ b/java/res/layout/input_honeycomb.xml
@@ -18,7 +18,7 @@
 */
 -->
 
-<com.android.inputmethod.latin.LatinKeyboardView
+<com.android.inputmethod.keyboard.LatinKeyboardView
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
         android:id="@+id/LatinkeyboardBaseView"
diff --git a/java/res/layout/input_stone_bold.xml b/java/res/layout/input_stone_bold.xml
index 5fb439acbc4ff07b2ffdbb844d9d36df815c7ea3..bf25e159769e69a3f71c4f19e436a1b026900631 100644
--- a/java/res/layout/input_stone_bold.xml
+++ b/java/res/layout/input_stone_bold.xml
@@ -18,7 +18,7 @@
 */
 -->
 
-<com.android.inputmethod.latin.LatinKeyboardView
+<com.android.inputmethod.keyboard.LatinKeyboardView
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
         android:id="@+id/LatinkeyboardBaseView"
diff --git a/java/res/layout/input_stone_normal.xml b/java/res/layout/input_stone_normal.xml
index 7a7b36894977b996aea5ef2cb1fa4679f887e481..cf47086120ff533c54e67f90f8f07551a55f6246 100644
--- a/java/res/layout/input_stone_normal.xml
+++ b/java/res/layout/input_stone_normal.xml
@@ -18,7 +18,7 @@
 */
 -->
 
-<com.android.inputmethod.latin.LatinKeyboardView
+<com.android.inputmethod.keyboard.LatinKeyboardView
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
         android:id="@+id/LatinkeyboardBaseView"
diff --git a/java/res/layout/input_stone_popup.xml b/java/res/layout/input_stone_popup.xml
index 3a7b5b9ab4bd14c4432bdd99d34118f14370536a..b4da0453672107073bdd5ceaacdd5f2226b911b0 100644
--- a/java/res/layout/input_stone_popup.xml
+++ b/java/res/layout/input_stone_popup.xml
@@ -25,9 +25,9 @@
         android:orientation="horizontal"
         android:background="@drawable/keyboard_popup_panel_background"
         >
-    <com.android.inputmethod.latin.BaseKeyboardView
+    <com.android.inputmethod.keyboard.KeyboardView
             xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-            android:id="@+id/BaseKeyboardView"
+            android:id="@+id/KeyboardView"
             android:layout_alignParentBottom="true"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/java/res/layout/keyboard_popup.xml b/java/res/layout/keyboard_popup.xml
index 929e17111f10a9f89addb02b60461cd77fa3bdfb..ac8134bfb1d3e18321dbbf38bfbf94248dcec74a 100644
--- a/java/res/layout/keyboard_popup.xml
+++ b/java/res/layout/keyboard_popup.xml
@@ -26,9 +26,9 @@
         android:paddingLeft="16dip"
         android:paddingRight="16dip"
         >
-    <com.android.inputmethod.latin.BaseKeyboardView
+    <com.android.inputmethod.keyboard.KeyboardView
             xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-            android:id="@+id/BaseKeyboardView"
+            android:id="@+id/KeyboardView"
             android:layout_alignParentBottom="true"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/java/res/layout/keyboard_popup_honeycomb.xml b/java/res/layout/keyboard_popup_honeycomb.xml
index 63b5353813bc1cffaf9197e4b03dd9b301c50470..e5fcbd44c27728f84b7bc8ae639158288a3dd5fb 100644
--- a/java/res/layout/keyboard_popup_honeycomb.xml
+++ b/java/res/layout/keyboard_popup_honeycomb.xml
@@ -26,9 +26,9 @@
         android:paddingLeft="24dip"
         android:paddingRight="24dip"
         >
-    <com.android.inputmethod.latin.BaseKeyboardView
+    <com.android.inputmethod.keyboard.KeyboardView
             xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-            android:id="@+id/BaseKeyboardView"
+            android:id="@+id/KeyboardView"
             android:layout_alignParentBottom="true"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index ef7a968c980420521fbf9e6eb218e969f6630122..4fc5351bfc9100e8a425e76d092dfb253556e98a 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -16,7 +16,7 @@
 
 <resources>
 
-    <declare-styleable name="BaseKeyboardView">
+    <declare-styleable name="KeyboardView">
         <!-- Default KeyboardView style. -->
         <attr name="keyboardViewStyle" format="reference" />
 
@@ -68,14 +68,14 @@
         </attr>
 
         <attr name="colorScheme">
-            <!-- This should be aligned with BaseKeyboardView.COLOR_SCHEME_* -->
+            <!-- This should be aligned with KeyboardView.COLOR_SCHEME_* -->
             <enum name="white" value="0" />
             <enum name="black" value="1" />
         </attr>
 
     </declare-styleable>
 
-    <declare-styleable name="BaseKeyboard">
+    <declare-styleable name="Keyboard">
         <!-- Default width of a key, in pixels or percentage of display width. -->
         <attr name="keyWidth" format="dimension|fraction" />
         <!-- Default height of a key, in pixels or percentage of display width. -->
@@ -86,7 +86,7 @@
         <attr name="verticalGap" format="dimension|fraction" />
     </declare-styleable>
 
-    <declare-styleable name="BaseKeyboard_Key">
+    <declare-styleable name="Keyboard_Key">
         <!-- The unicode value or comma-separated values that this key outputs. -->
         <attr name="codes" format="integer|string" />
         <!-- The XML keyboard layout of any popup keyboard. -->
@@ -114,7 +114,7 @@
         <attr name="keyLabel" format="string" />
         <!-- The key label option -->
         <attr name="keyLabelOption">
-            <!-- This should be aligned with BaseKeyboardView.KEY_LABEL_OPTION_* -->
+            <!-- This should be aligned with KeyboardView.KEY_LABEL_OPTION_* -->
             <flag name="alignLeft" value="1" />
             <flag name="alignRight" value="2" />
             <flag name="alignBottom" value="8" />
@@ -135,7 +135,7 @@
         <attr name="shiftedIcon" format="reference" />
     </declare-styleable>
 
-    <declare-styleable name="BaseKeyboard_Row">
+    <declare-styleable name="Keyboard_Row">
         <!-- Row edge flags. -->
         <attr name="rowEdgeFlags">
             <!-- Row is anchored to the top of the keyboard. -->
@@ -145,12 +145,12 @@
         </attr>
     </declare-styleable>
 
-    <declare-styleable name="BaseKeyboard_Include">
+    <declare-styleable name="Keyboard_Include">
         <attr name="keyboardLayout" format="reference" />
     </declare-styleable>
 
-    <declare-styleable name="BaseKeyboard_Case">
-        <!-- This should be aligned with KeyboardSwitcher.MODE_* -->
+    <declare-styleable name="Keyboard_Case">
+        <!-- This should be aligned with KeyboardId.MODE_* -->
         <attr name="mode">
             <enum name="text" value="0" />
             <enum name="url" value="1" />
@@ -175,7 +175,7 @@
         </attr>
     </declare-styleable>
 
-    <declare-styleable name="BaseKeyboard_KeyStyle">
+    <declare-styleable name="Keyboard_KeyStyle">
         <attr name="styleName" format="string" />
         <attr name="parentStyle" format="string" />
     </declare-styleable>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 02d21b43ba3e7a5d83437b9edd484ce0a850178e..130714fd39990e1105a0317f7b2b7f1ff676a41d 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -15,7 +15,7 @@
 -->
 
 <resources>
-    <style name="BaseKeyboardView">
+    <style name="KeyboardView">
         <item name="android:background">@drawable/keyboard_background</item>
 
         <item name="keyBackground">@drawable/btn_keyboard_key</item>
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
new file mode 100644
index 0000000000000000000000000000000000000000..9b7937618178ab46a63636ed33b2411efbf9d937
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -0,0 +1,305 @@
+/*
+ * 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 com.android.inputmethod.keyboard.KeyboardParser.ParseException;
+import com.android.inputmethod.keyboard.KeyStyles.KeyStyle;
+import com.android.inputmethod.latin.R;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Xml;
+
+/**
+ * Class for describing the position and characteristics of a single key in the keyboard.
+ */
+public class Key {
+    /**
+     * All the key codes (unicode or custom code) that this key could generate, zero'th
+     * being the most important.
+     */
+    public int[] codes;
+    /** The unicode that this key generates in manual temporary upper case mode. */
+    public int manualTemporaryUpperCaseCode;
+
+    /** Label to display */
+    public CharSequence label;
+    /** Option of the label */
+    public int labelOption;
+
+    /** Icon to display instead of a label. Icon takes precedence over a label */
+    public Drawable icon;
+    /** Hint icon to display on the key in conjunction with the label */
+    public Drawable hintIcon;
+    /** Preview version of the icon, for the preview popup */
+    /**
+     * The hint icon to display on the key when keyboard is in manual temporary upper case
+     * mode.
+     */
+    public Drawable manualTemporaryUpperCaseHintIcon;
+
+    public Drawable iconPreview;
+    /** Width of the key, not including the gap */
+    public int width;
+    /** Height of the key, not including the gap */
+    public int height;
+    /** The horizontal gap before this key */
+    public int gap;
+    /** Whether this key is sticky, i.e., a toggle key */
+    public boolean sticky;
+    /** X coordinate of the key in the keyboard layout */
+    public int x;
+    /** Y coordinate of the key in the keyboard layout */
+    public int y;
+    /** The current pressed state of this key */
+    public boolean pressed;
+    /** If this is a sticky key, is it on? */
+    public boolean on;
+    /** Text to output when pressed. This can be multiple characters, like ".com" */
+    public CharSequence text;
+    /** Popup characters */
+    public CharSequence popupCharacters;
+
+    /**
+     * Flags that specify the anchoring to edges of the keyboard for detecting touch events
+     * that are just out of the boundary of the key. This is a bit mask of
+     * {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT},
+     * {@link Keyboard#EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM}.
+     */
+    public int edgeFlags;
+    /** Whether this is a modifier key, such as Shift or Alt */
+    public boolean modifier;
+    /** The Keyboard that this key belongs to */
+    protected final Keyboard keyboard;
+    /**
+     * If this key pops up a mini keyboard, this is the resource id for the XML layout for that
+     * keyboard.
+     */
+    public int popupResId;
+    /** Whether this key repeats itself when held down */
+    public boolean repeatable;
+
+
+    private final static int[] KEY_STATE_NORMAL_ON = {
+        android.R.attr.state_checkable,
+        android.R.attr.state_checked
+    };
+
+    private final static int[] KEY_STATE_PRESSED_ON = {
+        android.R.attr.state_pressed,
+        android.R.attr.state_checkable,
+        android.R.attr.state_checked
+    };
+
+    private final static int[] KEY_STATE_NORMAL_OFF = {
+        android.R.attr.state_checkable
+    };
+
+    private final static int[] KEY_STATE_PRESSED_OFF = {
+        android.R.attr.state_pressed,
+        android.R.attr.state_checkable
+    };
+
+    private final static int[] KEY_STATE_NORMAL = {
+    };
+
+    private final static int[] KEY_STATE_PRESSED = {
+        android.R.attr.state_pressed
+    };
+
+    /** Create an empty key with no attributes. */
+    public Key(Row parent) {
+        keyboard = parent.parent;
+        height = parent.defaultHeight;
+        gap = parent.defaultHorizontalGap;
+        width = parent.defaultWidth - gap;
+        edgeFlags = parent.rowEdgeFlags;
+    }
+
+    /** 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
+     * 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,
+            KeyStyles keyStyles) {
+        this(parent);
+
+        TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
+                R.styleable.Keyboard);
+        height = KeyboardParser.getDimensionOrFraction(a,
+                R.styleable.Keyboard_keyHeight,
+                keyboard.mDisplayHeight, parent.defaultHeight);
+        gap = KeyboardParser.getDimensionOrFraction(a,
+                R.styleable.Keyboard_horizontalGap,
+                keyboard.mDisplayWidth, parent.defaultHorizontalGap);
+        width = KeyboardParser.getDimensionOrFraction(a,
+                R.styleable.Keyboard_keyWidth,
+                keyboard.mDisplayWidth, parent.defaultWidth) - gap;
+        a.recycle();
+
+        a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Key);
+
+        final KeyStyle style;
+        if (a.hasValue(R.styleable.Keyboard_Key_keyStyle)) {
+            String styleName = a.getString(R.styleable.Keyboard_Key_keyStyle);
+            style = keyStyles.getKeyStyle(styleName);
+            if (style == null)
+                throw new ParseException("Unknown key style: " + styleName, parser);
+        } else {
+            style = keyStyles.getEmptyKeyStyle();
+        }
+
+        // Horizontal gap is divided equally to both sides of the key.
+        this.x = x + gap / 2;
+        this.y = y;
+
+        codes = style.getIntArray(a, R.styleable.Keyboard_Key_codes);
+        iconPreview = style.getDrawable(a, R.styleable.Keyboard_Key_iconPreview);
+        Keyboard.setDefaultBounds(iconPreview);
+        popupCharacters = style.getText(a, R.styleable.Keyboard_Key_popupCharacters);
+        popupResId = style.getResourceId(a, R.styleable.Keyboard_Key_popupKeyboard, 0);
+        repeatable = style.getBoolean(a, R.styleable.Keyboard_Key_isRepeatable, false);
+        modifier = style.getBoolean(a, R.styleable.Keyboard_Key_isModifier, false);
+        sticky = style.getBoolean(a, R.styleable.Keyboard_Key_isSticky, false);
+        edgeFlags = style.getFlag(a, R.styleable.Keyboard_Key_keyEdgeFlags, 0);
+        edgeFlags |= parent.rowEdgeFlags;
+
+        icon = style.getDrawable(a, R.styleable.Keyboard_Key_keyIcon);
+        Keyboard.setDefaultBounds(icon);
+        hintIcon = style.getDrawable(a, R.styleable.Keyboard_Key_keyHintIcon);
+        Keyboard.setDefaultBounds(hintIcon);
+        manualTemporaryUpperCaseHintIcon = style.getDrawable(a,
+                R.styleable.Keyboard_Key_manualTemporaryUpperCaseHintIcon);
+        Keyboard.setDefaultBounds(manualTemporaryUpperCaseHintIcon);
+
+        label = style.getText(a, R.styleable.Keyboard_Key_keyLabel);
+        labelOption = style.getFlag(a, R.styleable.Keyboard_Key_keyLabelOption, 0);
+        manualTemporaryUpperCaseCode = style.getInt(a,
+                R.styleable.Keyboard_Key_manualTemporaryUpperCaseCode, 0);
+        text = style.getText(a, R.styleable.Keyboard_Key_keyOutputText);
+        final Drawable shiftedIcon = style.getDrawable(a,
+                R.styleable.Keyboard_Key_shiftedIcon);
+        if (shiftedIcon != null)
+            keyboard.getShiftedIcons().put(this, shiftedIcon);
+
+        if (codes == null && !TextUtils.isEmpty(label)) {
+            codes = new int[] { label.charAt(0) };
+        }
+        a.recycle();
+    }
+
+    /**
+     * Informs the key that it has been pressed, in case it needs to change its appearance or
+     * state.
+     * @see #onReleased(boolean)
+     */
+    public void onPressed() {
+        pressed = !pressed;
+    }
+
+    /**
+     * Changes the pressed state of the key. If it is a sticky key, it will also change the
+     * toggled state of the key if the finger was release inside.
+     * @param inside whether the finger was released inside the key
+     * @see #onPressed()
+     */
+    public void onReleased(boolean inside) {
+        pressed = !pressed;
+        if (sticky) {
+            on = !on;
+        }
+    }
+
+    /**
+     * Detects if a point falls inside 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.
+     */
+    public boolean isInside(int x, int y) {
+        boolean leftEdge = (edgeFlags & Keyboard.EDGE_LEFT) > 0;
+        boolean rightEdge = (edgeFlags & Keyboard.EDGE_RIGHT) > 0;
+        boolean topEdge = (edgeFlags & Keyboard.EDGE_TOP) > 0;
+        boolean bottomEdge = (edgeFlags & Keyboard.EDGE_BOTTOM) > 0;
+        if ((x >= this.x || (leftEdge && x <= this.x + this.width))
+                && (x < this.x + this.width || (rightEdge && x >= this.x))
+                && (y >= this.y || (topEdge && y <= this.y + this.height))
+                && (y < this.y + this.height || (bottomEdge && y >= this.y))) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the square of the distance to the nearest edge of the key and the given point.
+     * @param x the x-coordinate of the point
+     * @param y the y-coordinate of the point
+     * @return the square of the distance of the point from the nearest edge of the key
+     */
+    public int squaredDistanceToEdge(int x, int y) {
+        final int left = this.x;
+        final int right = left + this.width;
+        final int top = this.y;
+        final int bottom = top + this.height;
+        final int edgeX = x < left ? left : (x > right ? right : x);
+        final int edgeY = y < top ? top : (y > bottom ? bottom : y);
+        final int dx = x - edgeX;
+        final int dy = y - edgeY;
+        return dx * dx + dy * dy;
+    }
+
+    /**
+     * Returns the drawable state for the key, based on the current state and type of the key.
+     * @return the drawable state of the key.
+     * @see android.graphics.drawable.StateListDrawable#setState(int[])
+     */
+    public int[] getCurrentDrawableState() {
+        int[] states = KEY_STATE_NORMAL;
+
+        if (on) {
+            if (pressed) {
+                states = KEY_STATE_PRESSED_ON;
+            } else {
+                states = KEY_STATE_NORMAL_ON;
+            }
+        } else {
+            if (sticky) {
+                if (pressed) {
+                    states = KEY_STATE_PRESSED_OFF;
+                } else {
+                    states = KEY_STATE_NORMAL_OFF;
+                }
+            } else {
+                if (pressed) {
+                    states = KEY_STATE_PRESSED;
+                }
+            }
+        }
+        return states;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
similarity index 91%
rename from java/src/com/android/inputmethod/latin/KeyDetector.java
rename to java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 600a12fe52e75480c538c84e14f26ae778cea5aa..777a7952096965ca5e688679052609d7a2f5726c 100644
--- a/java/src/com/android/inputmethod/latin/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -14,15 +14,15 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
-
-import com.android.inputmethod.latin.BaseKeyboard.Key;
+package com.android.inputmethod.keyboard;
 
 import java.util.Arrays;
 import java.util.List;
 
-abstract class KeyDetector {
-    protected BaseKeyboard mKeyboard;
+public abstract class KeyDetector {
+    public static final int NOT_A_KEY = -1;
+
+    protected Keyboard mKeyboard;
 
     private Key[] mKeys;
 
@@ -34,7 +34,7 @@ abstract class KeyDetector {
 
     protected int mProximityThresholdSquare;
 
-    public Key[] setKeyboard(BaseKeyboard keyboard, float correctionX, float correctionY) {
+    public Key[] setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
         if (keyboard == null)
             throw new NullPointerException();
         mCorrectionX = (int)correctionX;
@@ -84,7 +84,7 @@ abstract class KeyDetector {
      */
     public int[] newCodeArray() {
         int[] codes = new int[getMaxNearbyKeys()];
-        Arrays.fill(codes, BaseKeyboardView.NOT_A_KEY);
+        Arrays.fill(codes, NOT_A_KEY);
         return codes;
     }
 
diff --git a/java/src/com/android/inputmethod/latin/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/KeyStyles.java
similarity index 86%
rename from java/src/com/android/inputmethod/latin/KeyStyles.java
rename to java/src/com/android/inputmethod/keyboard/KeyStyles.java
index fceede7c3dc8d49a2d18900e1a146cae7edba509..daa9c86d11a7ab491f2390dc969d5d2a9534b20a 100644
--- a/java/src/com/android/inputmethod/latin/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyStyles.java
@@ -14,9 +14,10 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.keyboard;
 
-import com.android.inputmethod.latin.BaseKeyboardParser.ParseException;
+import com.android.inputmethod.keyboard.KeyboardParser.ParseException;
+import com.android.inputmethod.latin.R;
 
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -48,30 +49,37 @@ public class KeyStyles {
         private EmptyKeyStyle() {
         }
 
+        @Override
         public int[] getIntArray(TypedArray a, int index) {
             return parseIntArray(a, index);
         }
 
+        @Override
         public Drawable getDrawable(TypedArray a, int index) {
             return a.getDrawable(index);
         }
 
+        @Override
         public CharSequence getText(TypedArray a, int index) {
             return a.getText(index);
         }
 
+        @Override
         public int getResourceId(TypedArray a, int index, int defaultValue) {
             return a.getResourceId(index, defaultValue);
         }
 
+        @Override
         public int getInt(TypedArray a, int index, int defaultValue) {
             return a.getInt(index, defaultValue);
         }
 
+        @Override
         public int getFlag(TypedArray a, int index, int defaultValue) {
             return a.getInt(index, defaultValue);
         }
 
+        @Override
         public boolean getBoolean(TypedArray a, int index, boolean defaultValue) {
             return a.getBoolean(index, defaultValue);
         }
@@ -156,18 +164,18 @@ public class KeyStyles {
 
         private void parseKeyStyleAttributes(TypedArray a) {
             // TODO: Currently not all Key attributes can be declared as style.
-            readIntArray(a, R.styleable.BaseKeyboard_Key_codes);
-            readText(a, R.styleable.BaseKeyboard_Key_keyLabel);
-            readFlag(a, R.styleable.BaseKeyboard_Key_keyLabelOption);
-            readText(a, R.styleable.BaseKeyboard_Key_keyOutputText);
-            readDrawable(a, R.styleable.BaseKeyboard_Key_keyIcon);
-            readDrawable(a, R.styleable.BaseKeyboard_Key_iconPreview);
-            readDrawable(a, R.styleable.BaseKeyboard_Key_keyHintIcon);
-            readDrawable(a, R.styleable.BaseKeyboard_Key_shiftedIcon);
-            readResourceId(a, R.styleable.BaseKeyboard_Key_popupKeyboard);
-            readBoolean(a, R.styleable.BaseKeyboard_Key_isModifier);
-            readBoolean(a, R.styleable.BaseKeyboard_Key_isSticky);
-            readBoolean(a, R.styleable.BaseKeyboard_Key_isRepeatable);
+            readIntArray(a, R.styleable.Keyboard_Key_codes);
+            readText(a, R.styleable.Keyboard_Key_keyLabel);
+            readFlag(a, R.styleable.Keyboard_Key_keyLabelOption);
+            readText(a, R.styleable.Keyboard_Key_keyOutputText);
+            readDrawable(a, R.styleable.Keyboard_Key_keyIcon);
+            readDrawable(a, R.styleable.Keyboard_Key_iconPreview);
+            readDrawable(a, R.styleable.Keyboard_Key_keyHintIcon);
+            readDrawable(a, R.styleable.Keyboard_Key_shiftedIcon);
+            readResourceId(a, R.styleable.Keyboard_Key_popupKeyboard);
+            readBoolean(a, R.styleable.Keyboard_Key_isModifier);
+            readBoolean(a, R.styleable.Keyboard_Key_isSticky);
+            readBoolean(a, R.styleable.Keyboard_Key_isRepeatable);
         }
 
         private void readDrawable(TypedArray a, int index) {
@@ -211,14 +219,14 @@ public class KeyStyles {
 
     public void parseKeyStyleAttributes(TypedArray a, TypedArray keyAttrs,
             XmlResourceParser parser) {
-        String styleName = a.getString(R.styleable.BaseKeyboard_KeyStyle_styleName);
+        String styleName = a.getString(R.styleable.Keyboard_KeyStyle_styleName);
         if (mStyles.containsKey(styleName))
             throw new ParseException("duplicate key style declared: " + styleName, parser);
 
         final DeclaredKeyStyle style = new DeclaredKeyStyle();
-        if (a.hasValue(R.styleable.BaseKeyboard_KeyStyle_parentStyle)) {
+        if (a.hasValue(R.styleable.Keyboard_KeyStyle_parentStyle)) {
             String parentStyle = a.getString(
-                    R.styleable.BaseKeyboard_KeyStyle_parentStyle);
+                    R.styleable.Keyboard_KeyStyle_parentStyle);
             final DeclaredKeyStyle parent = mStyles.get(parentStyle);
             if (parent == null)
                 throw new ParseException("Unknown parentStyle " + parent, parser);
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
new file mode 100644
index 0000000000000000000000000000000000000000..b7541557990e9abc1f410907c928984f672a3c15
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -0,0 +1,386 @@
+/*
+ * 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 com.android.inputmethod.latin.R;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
+ * consists of rows of keys.
+ * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
+ * <pre>
+ * &lt;Keyboard
+ *         latin:keyWidth="%10p"
+ *         latin:keyHeight="50px"
+ *         latin:horizontalGap="2px"
+ *         latin:verticalGap="2px" &gt;
+ *     &lt;Row latin:keyWidth="32px" &gt;
+ *         &lt;Key latin:keyLabel="A" /&gt;
+ *         ...
+ *     &lt;/Row&gt;
+ *     ...
+ * &lt;/Keyboard&gt;
+ * </pre>
+ */
+public class Keyboard {
+
+    static final String TAG = "Keyboard";
+
+    public static final int EDGE_LEFT = 0x01;
+    public static final int EDGE_RIGHT = 0x02;
+    public static final int EDGE_TOP = 0x04;
+    public static final int EDGE_BOTTOM = 0x08;
+
+    public static final int KEYCODE_SHIFT = -1;
+    public static final int KEYCODE_MODE_CHANGE = -2;
+    public static final int KEYCODE_CANCEL = -3;
+    public static final int KEYCODE_DONE = -4;
+    public static final int KEYCODE_DELETE = -5;
+    public static final int KEYCODE_ALT = -6;
+
+    /** Horizontal gap default for all rows */
+    int mDefaultHorizontalGap;
+
+    /** Default key width */
+    int mDefaultWidth;
+
+    /** Default key height */
+    int mDefaultHeight;
+
+    /** Default gap between rows */
+    int mDefaultVerticalGap;
+
+    /** Is the keyboard in the shifted state */
+    private boolean mShifted;
+
+    /** List of shift keys in this keyboard */
+    private final List<Key> mShiftKeys = new ArrayList<Key>();
+
+    /** List of shift keys and its shifted state icon */
+    private final HashMap<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>();
+
+    /** Total height of the keyboard, including the padding and keys */
+    private int mTotalHeight;
+
+    /**
+     * Total width of the keyboard, including left side gaps and keys, but not any gaps on the
+     * right side.
+     */
+    private int mTotalWidth;
+
+    /** List of keys in this keyboard */
+    private final List<Key> mKeys = new ArrayList<Key>();
+
+    /** Width of the screen available to fit the keyboard */
+    final int mDisplayWidth;
+
+    /** Height of the screen */
+    final int mDisplayHeight;
+
+    protected final KeyboardId mId;
+
+    // Variables for pre-computing nearest keys.
+
+    public final int GRID_WIDTH;
+    public final int GRID_HEIGHT;
+    private final int GRID_SIZE;
+    private int mCellWidth;
+    private int mCellHeight;
+    private int[][] mGridNeighbors;
+    private int mProximityThreshold;
+    private static int[] EMPTY_INT_ARRAY = new int[0];
+    /** Number of key widths from current touch point to search for nearest keys. */
+    private static float SEARCH_DISTANCE = 1.2f;
+
+    /**
+     * Creates a keyboard from the given xml key layout file.
+     * @param context the application or service context
+     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
+     */
+    public Keyboard(Context context, int xmlLayoutResId) {
+        this(context, xmlLayoutResId, null);
+    }
+
+    /**
+     * Creates a keyboard from the given keyboard identifier.
+     * @param context the application or service context
+     * @param id keyboard identifier
+     */
+    public Keyboard(Context context, KeyboardId id) {
+        this(context, id.getXmlId(), id);
+    }
+
+    /**
+     * Creates a keyboard from the given xml key layout file.
+     * @param context the application or service context
+     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
+     * @param id keyboard identifier
+     */
+    private Keyboard(Context context, int xmlLayoutResId, KeyboardId id) {
+        this(context, xmlLayoutResId, id,
+                context.getResources().getDisplayMetrics().widthPixels,
+                context.getResources().getDisplayMetrics().heightPixels);
+    }
+
+    private Keyboard(Context context, int xmlLayoutResId, KeyboardId id, int width,
+            int height) {
+        Resources res = context.getResources();
+        GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
+        GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
+        GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;
+
+        mDisplayWidth = width;
+        mDisplayHeight = height;
+
+        mDefaultHorizontalGap = 0;
+        setKeyWidth(mDisplayWidth / 10);
+        mDefaultVerticalGap = 0;
+        mDefaultHeight = mDefaultWidth;
+        mId = id;
+        loadKeyboard(context, xmlLayoutResId);
+    }
+
+    /**
+     * <p>Creates a blank keyboard from the given resource file and populates it with the specified
+     * characters in left-to-right, top-to-bottom fashion, using the specified number of columns.
+     * </p>
+     * <p>If the specified number of columns is -1, then the keyboard will fit as many keys as
+     * possible in each row.</p>
+     * @param context the application or service context
+     * @param layoutTemplateResId the layout template file, containing no keys.
+     * @param characters the list of characters to display on the keyboard. One key will be created
+     * for each character.
+     * @param columns the number of columns of keys to display. If this number is greater than the
+     * number of keys that can fit in a row, it will be ignored. If this number is -1, the
+     * keyboard will fit as many keys as possible in each row.
+     */
+    public Keyboard(Context context, int layoutTemplateResId,
+            CharSequence characters, int columns, int horizontalPadding) {
+        this(context, layoutTemplateResId);
+        int x = 0;
+        int y = 0;
+        int column = 0;
+        mTotalWidth = 0;
+
+        Row row = new Row(this);
+        row.defaultHeight = mDefaultHeight;
+        row.defaultWidth = mDefaultWidth;
+        row.defaultHorizontalGap = mDefaultHorizontalGap;
+        row.verticalGap = mDefaultVerticalGap;
+        row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;
+        final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
+        for (int i = 0; i < characters.length(); i++) {
+            char c = characters.charAt(i);
+            if (column >= maxColumns
+                    || x + mDefaultWidth + horizontalPadding > mDisplayWidth) {
+                x = 0;
+                y += mDefaultVerticalGap + mDefaultHeight;
+                column = 0;
+            }
+            final Key key = new Key(row);
+            // Horizontal gap is divided equally to both sides of the key.
+            key.x = x + key.gap / 2;
+            key.y = y;
+            key.label = String.valueOf(c);
+            key.codes = new int[] { c };
+            column++;
+            x += key.width + key.gap;
+            mKeys.add(key);
+            if (x > mTotalWidth) {
+                mTotalWidth = x;
+            }
+        }
+        mTotalHeight = y + mDefaultHeight;
+    }
+
+    public KeyboardId getKeyboardId() {
+        return mId;
+    }
+
+    public List<Key> getKeys() {
+        return mKeys;
+    }
+
+    protected int getHorizontalGap() {
+        return mDefaultHorizontalGap;
+    }
+
+    protected void setHorizontalGap(int gap) {
+        mDefaultHorizontalGap = gap;
+    }
+
+    protected int getVerticalGap() {
+        return mDefaultVerticalGap;
+    }
+
+    protected void setVerticalGap(int gap) {
+        mDefaultVerticalGap = gap;
+    }
+
+    protected int getKeyHeight() {
+        return mDefaultHeight;
+    }
+
+    protected void setKeyHeight(int height) {
+        mDefaultHeight = height;
+    }
+
+    protected int getKeyWidth() {
+        return mDefaultWidth;
+    }
+
+    protected void setKeyWidth(int width) {
+        mDefaultWidth = width;
+        final int threshold = (int) (width * SEARCH_DISTANCE);
+        mProximityThreshold = threshold * threshold;
+    }
+
+    /**
+     * Returns the total height of the keyboard
+     * @return the total height of the keyboard
+     */
+    public int getHeight() {
+        return mTotalHeight;
+    }
+
+    public int getMinWidth() {
+        return mTotalWidth;
+    }
+
+    public int getKeyboardHeight() {
+        return mDisplayHeight;
+    }
+
+    public int getKeyboardWidth() {
+        return mDisplayWidth;
+    }
+
+    public boolean setShifted(boolean shiftState) {
+        for (final Key key : mShiftKeys) {
+            key.on = shiftState;
+        }
+        if (mShifted != shiftState) {
+            mShifted = shiftState;
+            return true;
+        }
+        return false;
+    }
+
+    public boolean isShiftedOrShiftLocked() {
+        return mShifted;
+    }
+
+    public List<Key> getShiftKeys() {
+        return mShiftKeys;
+    }
+
+    public Map<Key, Drawable> getShiftedIcons() {
+        return mShiftedIcons;
+    }
+
+    private void computeNearestNeighbors() {
+        // Round-up so we don't have any pixels outside the grid
+        mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH;
+        mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT;
+        mGridNeighbors = new int[GRID_SIZE][];
+        final int[] indices = new int[mKeys.size()];
+        final int gridWidth = GRID_WIDTH * mCellWidth;
+        final int gridHeight = GRID_HEIGHT * mCellHeight;
+        final int threshold = mProximityThreshold;
+        for (int x = 0; x < gridWidth; x += mCellWidth) {
+            for (int y = 0; y < gridHeight; y += mCellHeight) {
+                final int centerX = x + mCellWidth / 2;
+                final int centerY = y + mCellHeight / 2;
+                int count = 0;
+                for (int i = 0; i < mKeys.size(); i++) {
+                    final Key key = mKeys.get(i);
+                    if (key.squaredDistanceToEdge(centerX, centerY) < threshold)
+                        indices[count++] = i;
+                }
+                final int[] cell = new int[count];
+                System.arraycopy(indices, 0, cell, 0, count);
+                mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell;
+            }
+        }
+    }
+
+    /**
+     * Returns the indices of the keys that are closest to the given point.
+     * @param x the x-coordinate of the point
+     * @param y the y-coordinate of the point
+     * @return the array of integer indices for the nearest keys to the given point. If the given
+     * point is out of range, then an array of size zero is returned.
+     */
+    public int[] getNearestKeys(int x, int y) {
+        if (mGridNeighbors == null) computeNearestNeighbors();
+        if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {
+            int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);
+            if (index < GRID_SIZE) {
+                return mGridNeighbors[index];
+            }
+        }
+        return EMPTY_INT_ARRAY;
+    }
+
+    // TODO should be private
+    protected Row createRowFromXml(Resources res, XmlResourceParser parser) {
+        return new Row(res, this, parser);
+    }
+
+    // TODO should be private
+    protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
+            XmlResourceParser parser, KeyStyles keyStyles) {
+        return new Key(res, parent, x, y, parser, keyStyles);
+    }
+
+    private void loadKeyboard(Context context, int xmlLayoutResId) {
+        try {
+            final Resources res = context.getResources();
+            KeyboardParser parser = new KeyboardParser(this, res);
+            parser.parseKeyboard(res.getXml(xmlLayoutResId));
+            // mTotalWidth is the width of this keyboard which is maximum width of row.
+            mTotalWidth = parser.getMaxRowWidth();
+            mTotalHeight = parser.getTotalHeight();
+        } catch (XmlPullParserException e) {
+            Log.w(TAG, "keyboard XML parse error: " + e);
+            throw new IllegalArgumentException(e);
+        } catch (IOException e) {
+            Log.w(TAG, "keyboard XML parse error: " + e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected static void setDefaultBounds(Drawable drawable)  {
+        if (drawable != null)
+            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
+                    drawable.getIntrinsicHeight());
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..e52db295709a721a228910162e7af74a23dbdca0
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
@@ -0,0 +1,99 @@
+/*
+ * 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;
+
+public interface KeyboardActionListener {
+
+    /**
+     * Called when the user presses a key. This is sent before the
+     * {@link #onKey} is called. For keys that repeat, this is only
+     * called once.
+     *
+     * @param primaryCode
+     *            the unicode of the key being pressed. If the touch is
+     *            not on a valid key, the value will be zero.
+     */
+    void onPress(int primaryCode);
+
+    /**
+     * Called when the user releases a key. This is sent after the
+     * {@link #onKey} is called. For keys that repeat, this is only
+     * called once.
+     *
+     * @param primaryCode
+     *            the code of the key that was released
+     */
+    void onRelease(int primaryCode);
+
+    /**
+     * Send a key press to the listener.
+     *
+     * @param primaryCode
+     *            this is the key that was pressed
+     * @param keyCodes
+     *            the codes for all the possible alternative keys with
+     *            the primary code being the first. If the primary key
+     *            code is a single character such as an alphabet or
+     *            number or symbol, the alternatives will include other
+     *            characters that may be on the same key or adjacent
+     *            keys. These codes are useful to correct for
+     *            accidental presses of a key adjacent to the intended
+     *            key.
+     * @param x
+     *            x-coordinate pixel of touched event. If onKey is not called by onTouchEvent,
+     *            the value should be NOT_A_TOUCH_COORDINATE.
+     * @param y
+     *            y-coordinate pixel of touched event. If onKey is not called by onTouchEvent,
+     *            the value should be NOT_A_TOUCH_COORDINATE.
+     */
+    void onKey(int primaryCode, int[] keyCodes, int x, int y);
+
+    /**
+     * Sends a sequence of characters to the listener.
+     *
+     * @param text
+     *            the sequence of characters to be displayed.
+     */
+    void onText(CharSequence text);
+
+    /**
+     * Called when user released a finger outside any key.
+     */
+    void onCancel();
+
+    /**
+     * Called when the user quickly moves the finger from right to
+     * left.
+     */
+    void swipeLeft();
+
+    /**
+     * Called when the user quickly moves the finger from left to
+     * right.
+     */
+    void swipeRight();
+
+    /**
+     * Called when the user quickly moves the finger from up to down.
+     */
+    void swipeDown();
+
+    /**
+     * Called when the user quickly moves the finger from down to up.
+     */
+    void swipeUp();
+}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
new file mode 100644
index 0000000000000000000000000000000000000000..289e4c0e815d56c5a34213a2a691e182f7fb949c
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -0,0 +1,148 @@
+/*
+ * 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 com.android.inputmethod.latin.R;
+
+import android.view.inputmethod.EditorInfo;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * Represents the parameters necessary to construct a new LatinKeyboard,
+ * which also serve as a unique identifier for each keyboard type.
+ */
+public class KeyboardId {
+    public static final int MODE_TEXT = 0;
+    public static final int MODE_URL = 1;
+    public static final int MODE_EMAIL = 2;
+    public static final int MODE_IM = 3;
+    public static final int MODE_WEB = 4;
+    public static final int MODE_PHONE = 5;
+    public static final int MODE_NUMBER = 6;
+
+    public final Locale mLocale;
+    public final int mOrientation;
+    public final int mMode;
+    public final int mXmlId;
+    public final int mColorScheme;
+    public final boolean mHasSettingsKey;
+    public final boolean mVoiceKeyEnabled;
+    public final boolean mHasVoiceKey;
+    public final int mImeOptions;
+    public final boolean mEnableShiftLock;
+
+    private final int mHashCode;
+
+    public KeyboardId(Locale locale, int orientation, int mode,
+            int xmlId, int colorScheme, boolean hasSettingsKey, boolean voiceKeyEnabled,
+            boolean hasVoiceKey, int imeOptions, boolean enableShiftLock) {
+        this.mLocale = locale;
+        this.mOrientation = orientation;
+        this.mMode = mode;
+        this.mXmlId = xmlId;
+        this.mColorScheme = colorScheme;
+        this.mHasSettingsKey = hasSettingsKey;
+        this.mVoiceKeyEnabled = voiceKeyEnabled;
+        this.mHasVoiceKey = hasVoiceKey;
+        // We are interested only in IME_MASK_ACTION enum value and IME_FLAG_NO_ENTER_ACTION.
+        this.mImeOptions = imeOptions
+                & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION);
+        this.mEnableShiftLock = enableShiftLock;
+
+        this.mHashCode = Arrays.hashCode(new Object[] {
+                locale,
+                orientation,
+                mode,
+                xmlId,
+                colorScheme,
+                hasSettingsKey,
+                voiceKeyEnabled,
+                hasVoiceKey,
+                imeOptions,
+                enableShiftLock,
+        });
+    }
+
+    public int getXmlId() {
+        return mXmlId;
+    }
+
+    public boolean isAlphabetMode() {
+        return mXmlId == R.xml.kbd_qwerty;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        return other instanceof KeyboardId && equals((KeyboardId) other);
+    }
+
+    boolean equals(KeyboardId other) {
+        return other.mLocale.equals(this.mLocale)
+            && other.mOrientation == this.mOrientation
+            && other.mMode == this.mMode
+            && other.mXmlId == this.mXmlId
+            && other.mColorScheme == this.mColorScheme
+            && other.mHasSettingsKey == this.mHasSettingsKey
+            && other.mVoiceKeyEnabled == this.mVoiceKeyEnabled
+            && other.mHasVoiceKey == this.mHasVoiceKey
+            && other.mImeOptions == this.mImeOptions
+            && other.mEnableShiftLock == this.mEnableShiftLock;
+    }
+
+    @Override
+    public int hashCode() {
+        return mHashCode;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("[%s %s %5s imeOptions=0x%08x xml=0x%08x %s%s%s%s%s]",
+                mLocale,
+                (mOrientation == 1 ? "port" : "land"),
+                modeName(mMode),
+                mImeOptions,
+                mXmlId,
+                colorSchemeName(mColorScheme),
+                (mHasSettingsKey ? " hasSettingsKey" : ""),
+                (mVoiceKeyEnabled ? " voiceKeyEnabled" : ""),
+                (mHasVoiceKey ? " hasVoiceKey" : ""),
+                (mEnableShiftLock ? " enableShiftLock" : ""));
+    }
+
+    private static String modeName(int mode) {
+        switch (mode) {
+        case MODE_TEXT: return "text";
+        case MODE_URL: return "url";
+        case MODE_EMAIL: return "email";
+        case MODE_IM: return "im";
+        case MODE_WEB: return "web";
+        case MODE_PHONE: return "phone";
+        case MODE_NUMBER: return "number";
+        }
+        return null;
+    }
+
+    private static String colorSchemeName(int colorScheme) {
+        switch (colorScheme) {
+        case KeyboardView.COLOR_SCHEME_WHITE: return "white";
+        case KeyboardView.COLOR_SCHEME_BLACK: return "black";
+        }
+        return null;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/BaseKeyboardParser.java b/java/src/com/android/inputmethod/keyboard/KeyboardParser.java
similarity index 89%
rename from java/src/com/android/inputmethod/latin/BaseKeyboardParser.java
rename to java/src/com/android/inputmethod/keyboard/KeyboardParser.java
index 38b2a1b57cec8b7f132b321ab2cfb109d468c3d3..2147ee2891e3ea20fee282114860b887fce66082 100644
--- a/java/src/com/android/inputmethod/latin/BaseKeyboardParser.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardParser.java
@@ -14,11 +14,9 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.keyboard;
 
-import com.android.inputmethod.latin.BaseKeyboard.Key;
-import com.android.inputmethod.latin.BaseKeyboard.Row;
-import com.android.inputmethod.latin.KeyboardSwitcher.KeyboardId;
+import com.android.inputmethod.latin.R;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -36,7 +34,7 @@ import java.util.List;
 /**
  * Parser for BaseKeyboard.
  *
- * This class parses Keyboard XML file and fill out keys in BaseKeyboard.
+ * This class parses Keyboard XML file and fill out keys in Keyboard.
  * The Keyboard XML file looks like:
  * <pre>
  *   &gt;!-- xml/keyboard.xml --&lt;
@@ -102,8 +100,8 @@ import java.util.List;
  * </pre>
  */
 
-public class BaseKeyboardParser {
-    private static final String TAG = "BaseKeyboardParser";
+public class KeyboardParser {
+    private static final String TAG = "KeyboardParser";
     private static final boolean DEBUG_TAG = false;
 
     // Keyboard XML Tags
@@ -118,7 +116,7 @@ public class BaseKeyboardParser {
     private static final String TAG_DEFAULT = "default";
     private static final String TAG_KEY_STYLE = "key-style";
 
-    private final BaseKeyboard mKeyboard;
+    private final Keyboard mKeyboard;
     private final Resources mResources;
 
     private int mCurrentX = 0;
@@ -128,7 +126,7 @@ public class BaseKeyboardParser {
     private Row mCurrentRow = null;
     private final KeyStyles mKeyStyles = new KeyStyles();
 
-    public BaseKeyboardParser(BaseKeyboard keyboard, Resources res) {
+    public KeyboardParser(Keyboard keyboard, Resources res) {
         mKeyboard = keyboard;
         mResources = res;
     }
@@ -160,19 +158,19 @@ public class BaseKeyboardParser {
     }
 
     private void parseKeyboardAttributes(XmlResourceParser parser) {
-        final BaseKeyboard keyboard = mKeyboard;
+        final Keyboard keyboard = mKeyboard;
         final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.BaseKeyboard);
+                R.styleable.Keyboard);
         final int width = keyboard.getKeyboardWidth();
         final int height = keyboard.getKeyboardHeight();
         keyboard.setKeyWidth(getDimensionOrFraction(a,
-                R.styleable.BaseKeyboard_keyWidth, width, width / 10));
+                R.styleable.Keyboard_keyWidth, width, width / 10));
         keyboard.setKeyHeight(getDimensionOrFraction(a,
-                R.styleable.BaseKeyboard_keyHeight, height, 50));
+                R.styleable.Keyboard_keyHeight, height, 50));
         keyboard.setHorizontalGap(getDimensionOrFraction(a,
-                R.styleable.BaseKeyboard_horizontalGap, width, 0));
+                R.styleable.Keyboard_horizontalGap, width, 0));
         keyboard.setVerticalGap(getDimensionOrFraction(a,
-                R.styleable.BaseKeyboard_verticalGap, height, 0));
+                R.styleable.Keyboard_verticalGap, height, 0));
         a.recycle();
         if (DEBUG_TAG) Log.d(TAG, "id=" + keyboard.mId);
     }
@@ -266,7 +264,7 @@ public class BaseKeyboardParser {
                     mKeyStyles);
             checkEndTag(TAG_KEY, parser);
             keys.add(key);
-            if (key.codes[0] == BaseKeyboard.KEYCODE_SHIFT)
+            if (key.codes[0] == Keyboard.KEYCODE_SHIFT)
                 mKeyboard.getShiftKeys().add(key);
             endKey(key);
         }
@@ -278,8 +276,8 @@ public class BaseKeyboardParser {
             checkEndTag(TAG_SPACER, parser);
         } else {
             final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                    R.styleable.BaseKeyboard);
-            final int gap = getDimensionOrFraction(a, R.styleable.BaseKeyboard_horizontalGap,
+                    R.styleable.Keyboard);
+            final int gap = getDimensionOrFraction(a, R.styleable.Keyboard_horizontalGap,
                     mKeyboard.getKeyboardWidth(), 0);
             a.recycle();
             checkEndTag(TAG_SPACER, parser);
@@ -303,9 +301,9 @@ public class BaseKeyboardParser {
             checkEndTag(TAG_INCLUDE, parser);
         } else {
             final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                    R.styleable.BaseKeyboard_Include);
+                    R.styleable.Keyboard_Include);
             final int keyboardLayout = a.getResourceId(
-                    R.styleable.BaseKeyboard_Include_keyboardLayout, 0);
+                    R.styleable.Keyboard_Include_keyboardLayout, 0);
             a.recycle();
 
             checkEndTag(TAG_INCLUDE, parser);
@@ -395,25 +393,25 @@ public class BaseKeyboardParser {
             return true;
 
         final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.BaseKeyboard_Case);
+                R.styleable.Keyboard_Case);
         final TypedArray viewAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.BaseKeyboardView);
+                R.styleable.KeyboardView);
         try {
             final boolean modeMatched = matchInteger(a,
-                    R.styleable.BaseKeyboard_Case_mode, id.mMode);
+                    R.styleable.Keyboard_Case_mode, id.mMode);
             final boolean settingsKeyMatched = matchBoolean(a,
-                    R.styleable.BaseKeyboard_Case_hasSettingsKey, id.mHasSettingsKey);
+                    R.styleable.Keyboard_Case_hasSettingsKey, id.mHasSettingsKey);
             final boolean voiceEnabledMatched = matchBoolean(a,
-                    R.styleable.BaseKeyboard_Case_voiceKeyEnabled, id.mVoiceKeyEnabled);
+                    R.styleable.Keyboard_Case_voiceKeyEnabled, id.mVoiceKeyEnabled);
             final boolean voiceKeyMatched = matchBoolean(a,
-                    R.styleable.BaseKeyboard_Case_hasVoiceKey, id.mHasVoiceKey);
+                    R.styleable.Keyboard_Case_hasVoiceKey, id.mHasVoiceKey);
             final boolean colorSchemeMatched = matchInteger(viewAttr,
-                    R.styleable.BaseKeyboardView_colorScheme, id.mColorScheme);
+                    R.styleable.KeyboardView_colorScheme, id.mColorScheme);
             // As noted at KeyboardSwitcher.KeyboardId class, we are interested only in
             // enum value masked by IME_MASK_ACTION and IME_FLAG_NO_ENTER_ACTION. So matching
             // this attribute with id.mImeOptions as integer value is enough for our purpose.
             final boolean imeOptionsMatched = matchInteger(a,
-                    R.styleable.BaseKeyboard_Case_imeOptions, id.mImeOptions);
+                    R.styleable.Keyboard_Case_imeOptions, id.mImeOptions);
             final boolean selected = modeMatched && settingsKeyMatched && voiceEnabledMatched
                     && voiceKeyMatched && colorSchemeMatched && imeOptionsMatched;
 
@@ -421,15 +419,15 @@ public class BaseKeyboardParser {
                 Log.d(TAG, String.format(
                         "parseCaseCondition: %s%s%s%s%s%s%s",
                         Boolean.toString(selected).toUpperCase(),
-                        debugInteger(a, R.styleable.BaseKeyboard_Case_mode, "mode"),
-                        debugBoolean(a, R.styleable.BaseKeyboard_Case_hasSettingsKey,
+                        debugInteger(a, R.styleable.Keyboard_Case_mode, "mode"),
+                        debugBoolean(a, R.styleable.Keyboard_Case_hasSettingsKey,
                                 "hasSettingsKey"),
-                        debugBoolean(a, R.styleable.BaseKeyboard_Case_voiceKeyEnabled,
+                        debugBoolean(a, R.styleable.Keyboard_Case_voiceKeyEnabled,
                                 "voiceKeyEnabled"),
-                        debugBoolean(a, R.styleable.BaseKeyboard_Case_hasVoiceKey, "hasVoiceKey"),
-                        debugInteger(viewAttr, R.styleable.BaseKeyboardView_colorScheme,
+                        debugBoolean(a, R.styleable.Keyboard_Case_hasVoiceKey, "hasVoiceKey"),
+                        debugInteger(viewAttr, R.styleable.KeyboardView_colorScheme,
                                 "colorScheme"),
-                        debugInteger(a, R.styleable.BaseKeyboard_Case_imeOptions, "imeOptions")));
+                        debugInteger(a, R.styleable.Keyboard_Case_imeOptions, "imeOptions")));
             }
 
             return selected;
@@ -464,11 +462,11 @@ public class BaseKeyboardParser {
     private void parseKeyStyle(XmlResourceParser parser, List<Key> keys)
             throws XmlPullParserException, IOException {
         TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.BaseKeyboard_KeyStyle);
+                R.styleable.Keyboard_KeyStyle);
         TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.BaseKeyboard_Key);
+                R.styleable.Keyboard_Key);
         try {
-            if (!a.hasValue(R.styleable.BaseKeyboard_KeyStyle_styleName))
+            if (!a.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
                 throw new ParseException("<" + TAG_KEY_STYLE
                         + "/> needs styleName attribute", parser);
             if (keys != null)
diff --git a/java/src/com/android/inputmethod/latin/BaseKeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
similarity index 87%
rename from java/src/com/android/inputmethod/latin/BaseKeyboardView.java
rename to java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 3193cd46e2225f075442068b2d2a65762945bd35..ae6d097169e2304a3113aaa7ffdd47c569169e01 100644
--- a/java/src/com/android/inputmethod/latin/BaseKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.keyboard;
 
-import com.android.inputmethod.latin.BaseKeyboard.Key;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SubtypeSwitcher;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -49,27 +51,26 @@ import android.widget.TextView;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.WeakHashMap;
 
 /**
- * A view that renders a virtual {@link LatinKeyboard}. It handles rendering of keys and
- * detecting key presses and touch movements.
+ * A view that renders a virtual {@link Keyboard}. It handles rendering of keys and detecting key
+ * presses and touch movements.
  *
  * TODO: References to LatinKeyboard in this class should be replaced with ones to its base class.
  *
- * @attr ref R.styleable#BaseKeyboardView_keyBackground
- * @attr ref R.styleable#BaseKeyboardView_keyPreviewLayout
- * @attr ref R.styleable#BaseKeyboardView_keyPreviewOffset
- * @attr ref R.styleable#BaseKeyboardView_labelTextSize
- * @attr ref R.styleable#BaseKeyboardView_keyTextSize
- * @attr ref R.styleable#BaseKeyboardView_keyTextColor
- * @attr ref R.styleable#BaseKeyboardView_verticalCorrection
- * @attr ref R.styleable#BaseKeyboardView_popupLayout
+ * @attr ref R.styleable#KeyboardView_keyBackground
+ * @attr ref R.styleable#KeyboardView_keyPreviewLayout
+ * @attr ref R.styleable#KeyboardView_keyPreviewOffset
+ * @attr ref R.styleable#KeyboardView_labelTextSize
+ * @attr ref R.styleable#KeyboardView_keyTextSize
+ * @attr ref R.styleable#KeyboardView_keyTextColor
+ * @attr ref R.styleable#KeyboardView_verticalCorrection
+ * @attr ref R.styleable#KeyboardView_popupLayout
  */
-public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
-    private static final String TAG = "BaseKeyboardView";
+public class KeyboardView extends View implements PointerTracker.UIProxy {
+    private static final String TAG = "KeyboardView";
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_SHOW_ALIGN = false;
     private static final boolean DEBUG_KEYBOARD_GRID = false;
@@ -79,93 +80,10 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
 
     public static final int NOT_A_TOUCH_COORDINATE = -1;
 
-    public interface OnKeyboardActionListener {
-
-        /**
-         * Called when the user presses a key. This is sent before the
-         * {@link #onKey} is called. For keys that repeat, this is only
-         * called once.
-         *
-         * @param primaryCode
-         *            the unicode of the key being pressed. If the touch is
-         *            not on a valid key, the value will be zero.
-         */
-        void onPress(int primaryCode);
-
-        /**
-         * Called when the user releases a key. This is sent after the
-         * {@link #onKey} is called. For keys that repeat, this is only
-         * called once.
-         *
-         * @param primaryCode
-         *            the code of the key that was released
-         */
-        void onRelease(int primaryCode);
-
-        /**
-         * Send a key press to the listener.
-         *
-         * @param primaryCode
-         *            this is the key that was pressed
-         * @param keyCodes
-         *            the codes for all the possible alternative keys with
-         *            the primary code being the first. If the primary key
-         *            code is a single character such as an alphabet or
-         *            number or symbol, the alternatives will include other
-         *            characters that may be on the same key or adjacent
-         *            keys. These codes are useful to correct for
-         *            accidental presses of a key adjacent to the intended
-         *            key.
-         * @param x
-         *            x-coordinate pixel of touched event. If onKey is not called by onTouchEvent,
-         *            the value should be NOT_A_TOUCH_COORDINATE.
-         * @param y
-         *            y-coordinate pixel of touched event. If onKey is not called by onTouchEvent,
-         *            the value should be NOT_A_TOUCH_COORDINATE.
-         */
-        void onKey(int primaryCode, int[] keyCodes, int x, int y);
-
-        /**
-         * Sends a sequence of characters to the listener.
-         *
-         * @param text
-         *            the sequence of characters to be displayed.
-         */
-        void onText(CharSequence text);
-
-        /**
-         * Called when user released a finger outside any key.
-         */
-        void onCancel();
-
-        /**
-         * Called when the user quickly moves the finger from right to
-         * left.
-         */
-        void swipeLeft();
-
-        /**
-         * Called when the user quickly moves the finger from left to
-         * right.
-         */
-        void swipeRight();
-
-        /**
-         * Called when the user quickly moves the finger from up to down.
-         */
-        void swipeDown();
-
-        /**
-         * Called when the user quickly moves the finger from down to up.
-         */
-        void swipeUp();
-    }
-
     // Timing constants
     private final int mKeyRepeatInterval;
 
     // Miscellaneous constants
-    /* package */ static final int NOT_A_KEY = -1;
     private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
     private static final int HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL = -1;
 
@@ -187,7 +105,7 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
     private int mPopupLayout;
 
     // Main keyboard
-    private BaseKeyboard mKeyboard;
+    private Keyboard mKeyboard;
     private Key[] mKeys;
 
     // Key preview popup
@@ -196,7 +114,7 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
     private PopupWindow mPreviewPopup;
     private int mPreviewTextSizeLarge;
     private int[] mOffsetInWindow;
-    private int mOldPreviewKeyIndex = NOT_A_KEY;
+    private int mOldPreviewKeyIndex = KeyDetector.NOT_A_KEY;
     private boolean mShowPreview = true;
     private boolean mShowTouchPoints = true;
     private int mPopupPreviewOffsetX;
@@ -208,7 +126,7 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
 
     // Popup mini keyboard
     private PopupWindow mMiniKeyboardPopup;
-    private BaseKeyboardView mMiniKeyboard;
+    private KeyboardView mMiniKeyboard;
     private View mMiniKeyboardParent;
     private final WeakHashMap<Key, View> mMiniKeyboardCache = new WeakHashMap<Key, View>();
     private int mMiniKeyboardOriginX;
@@ -218,13 +136,13 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
     private final float mMiniKeyboardSlideAllowance;
     private int mMiniKeyboardTrackerId;
 
-    /** Listener for {@link OnKeyboardActionListener}. */
-    private OnKeyboardActionListener mKeyboardActionListener;
+    /** Listener for {@link KeyboardActionListener}. */
+    private KeyboardActionListener mKeyboardActionListener;
 
     private final ArrayList<PointerTracker> mPointerTrackers = new ArrayList<PointerTracker>();
 
     // TODO: Let the PointerTracker class manage this pointer queue
-    private final PointerQueue mPointerQueue = new PointerQueue();
+    private final PointerTrackerQueue mPointerQueue = new PointerTrackerQueue();
 
     private final boolean mHasDistinctMultitouch;
     private int mOldPointerCount = 1;
@@ -370,63 +288,15 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
         }
     };
 
-    static class PointerQueue {
-        private LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>();
-
-        public void add(PointerTracker tracker) {
-            mQueue.add(tracker);
-        }
-
-        public int lastIndexOf(PointerTracker tracker) {
-            LinkedList<PointerTracker> queue = mQueue;
-            for (int index = queue.size() - 1; index >= 0; index--) {
-                PointerTracker t = queue.get(index);
-                if (t == tracker)
-                    return index;
-            }
-            return -1;
-        }
-
-        public void releaseAllPointersOlderThan(PointerTracker tracker, long eventTime) {
-            LinkedList<PointerTracker> queue = mQueue;
-            int oldestPos = 0;
-            for (PointerTracker t = queue.get(oldestPos); t != tracker; t = queue.get(oldestPos)) {
-                if (t.isModifier()) {
-                    oldestPos++;
-                } else {
-                    t.onUpEvent(t.getLastX(), t.getLastY(), eventTime);
-                    t.setAlreadyProcessed();
-                    queue.remove(oldestPos);
-                }
-            }
-        }
-
-        public void releaseAllPointersExcept(PointerTracker tracker, long eventTime) {
-            for (PointerTracker t : mQueue) {
-                if (t == tracker)
-                    continue;
-                t.onUpEvent(t.getLastX(), t.getLastY(), eventTime);
-                t.setAlreadyProcessed();
-            }
-            mQueue.clear();
-            if (tracker != null)
-                mQueue.add(tracker);
-        }
-
-        public void remove(PointerTracker tracker) {
-            mQueue.remove(tracker);
-        }
-    }
-
-    public BaseKeyboardView(Context context, AttributeSet attrs) {
+    public KeyboardView(Context context, AttributeSet attrs) {
         this(context, attrs, R.attr.keyboardViewStyle);
     }
 
-    public BaseKeyboardView(Context context, AttributeSet attrs, int defStyle) {
+    public KeyboardView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
         TypedArray a = context.obtainStyledAttributes(
-                attrs, R.styleable.BaseKeyboardView, defStyle, R.style.BaseKeyboardView);
+                attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
         LayoutInflater inflate =
                 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         int previewLayout = 0;
@@ -438,53 +308,53 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
             int attr = a.getIndex(i);
 
             switch (attr) {
-            case R.styleable.BaseKeyboardView_keyBackground:
+            case R.styleable.KeyboardView_keyBackground:
                 mKeyBackground = a.getDrawable(attr);
                 break;
-            case R.styleable.BaseKeyboardView_keyHysteresisDistance:
+            case R.styleable.KeyboardView_keyHysteresisDistance:
                 mKeyHysteresisDistance = a.getDimensionPixelOffset(attr, 0);
                 break;
-            case R.styleable.BaseKeyboardView_verticalCorrection:
+            case R.styleable.KeyboardView_verticalCorrection:
                 mVerticalCorrection = a.getDimensionPixelOffset(attr, 0);
                 break;
-            case R.styleable.BaseKeyboardView_keyPreviewLayout:
+            case R.styleable.KeyboardView_keyPreviewLayout:
                 previewLayout = a.getResourceId(attr, 0);
                 break;
-            case R.styleable.BaseKeyboardView_keyPreviewOffset:
+            case R.styleable.KeyboardView_keyPreviewOffset:
                 mPreviewOffset = a.getDimensionPixelOffset(attr, 0);
                 break;
-            case R.styleable.BaseKeyboardView_keyPreviewHeight:
+            case R.styleable.KeyboardView_keyPreviewHeight:
                 mPreviewHeight = a.getDimensionPixelSize(attr, 80);
                 break;
-            case R.styleable.BaseKeyboardView_keyLetterSize:
+            case R.styleable.KeyboardView_keyLetterSize:
                 mKeyLetterSize = a.getDimensionPixelSize(attr, 18);
                 break;
-            case R.styleable.BaseKeyboardView_keyTextColor:
+            case R.styleable.KeyboardView_keyTextColor:
                 mKeyTextColor = a.getColor(attr, 0xFF000000);
                 break;
-            case R.styleable.BaseKeyboardView_keyTextColorDisabled:
+            case R.styleable.KeyboardView_keyTextColorDisabled:
                 mKeyTextColorDisabled = a.getColor(attr, 0xFF000000);
                 break;
-            case R.styleable.BaseKeyboardView_labelTextSize:
+            case R.styleable.KeyboardView_labelTextSize:
                 mLabelTextSize = a.getDimensionPixelSize(attr, 14);
                 break;
-            case R.styleable.BaseKeyboardView_popupLayout:
+            case R.styleable.KeyboardView_popupLayout:
                 mPopupLayout = a.getResourceId(attr, 0);
                 break;
-            case R.styleable.BaseKeyboardView_shadowColor:
+            case R.styleable.KeyboardView_shadowColor:
                 mShadowColor = a.getColor(attr, 0);
                 break;
-            case R.styleable.BaseKeyboardView_shadowRadius:
+            case R.styleable.KeyboardView_shadowRadius:
                 mShadowRadius = a.getFloat(attr, 0f);
                 break;
             // TODO: Use Theme (android.R.styleable.Theme_backgroundDimAmount)
-            case R.styleable.BaseKeyboardView_backgroundDimAmount:
+            case R.styleable.KeyboardView_backgroundDimAmount:
                 mBackgroundDimAmount = a.getFloat(attr, 0.5f);
                 break;
-            case R.styleable.BaseKeyboardView_keyLetterStyle:
+            case R.styleable.KeyboardView_keyLetterStyle:
                 mKeyLetterStyle = Typeface.defaultFromStyle(a.getInt(attr, Typeface.NORMAL));
                 break;
-            case R.styleable.BaseKeyboardView_colorScheme:
+            case R.styleable.KeyboardView_colorScheme:
                 mColorScheme = a.getInt(attr, COLOR_SCHEME_WHITE);
                 break;
             }
@@ -575,7 +445,7 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
         mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
     }
 
-    public void setOnKeyboardActionListener(OnKeyboardActionListener listener) {
+    public void setOnKeyboardActionListener(KeyboardActionListener listener) {
         mKeyboardActionListener = listener;
         for (PointerTracker tracker : mPointerTrackers) {
             tracker.setOnKeyboardActionListener(listener);
@@ -583,21 +453,21 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
     }
 
     /**
-     * Returns the {@link OnKeyboardActionListener} object.
+     * Returns the {@link KeyboardActionListener} object.
      * @return the listener attached to this keyboard
      */
-    protected OnKeyboardActionListener getOnKeyboardActionListener() {
+    protected KeyboardActionListener getOnKeyboardActionListener() {
         return mKeyboardActionListener;
     }
 
     /**
      * Attaches a keyboard to this view. The keyboard can be switched at any time and the
      * view will re-layout itself to accommodate the keyboard.
-     * @see BaseKeyboard
+     * @see Keyboard
      * @see #getKeyboard()
      * @param keyboard the keyboard to display in this view
      */
-    protected void setKeyboard(BaseKeyboard keyboard) {
+    public void setKeyboard(Keyboard keyboard) {
         if (mKeyboard != null) {
             dismissKeyPreview();
         }
@@ -622,9 +492,9 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
     /**
      * Returns the current keyboard being displayed by this view.
      * @return the currently attached keyboard
-     * @see #setKeyboard(BaseKeyboard)
+     * @see #setKeyboard(Keyboard)
      */
-    protected BaseKeyboard getKeyboard() {
+    public Keyboard getKeyboard() {
         return mKeyboard;
     }
 
@@ -632,6 +502,7 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
      * Return whether the device has distinct multi-touch panel.
      * @return true if the device has distinct multi-touch panel.
      */
+    @Override
     public boolean hasDistinctMultitouch() {
         return mHasDistinctMultitouch;
     }
@@ -670,7 +541,7 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
     }
 
     /**
-     * When enabled, calls to {@link OnKeyboardActionListener#onKey} will include key
+     * When enabled, calls to {@link KeyboardActionListener#onKey} will include key
      * codes for adjacent keys.  When disabled, only the primary key code will be
      * reported.
      * @param enabled whether or not the proximity correction is enabled
@@ -715,7 +586,7 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
      * @param keyboard
      * @param keys
      */
-    private void computeProximityThreshold(BaseKeyboard keyboard, Key[] keys) {
+    private void computeProximityThreshold(Keyboard keyboard, Key[] keys) {
         if (keyboard == null || keys == null || keys.length == 0) return;
         final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
         int maxCount = 0;
@@ -750,6 +621,7 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
         canvas.drawBitmap(mBuffer, 0, 0, null);
     }
 
+    @SuppressWarnings("unused")
     private void onBufferDraw() {
         if (mBuffer == null || mKeyboardChanged) {
             if (mBuffer == null || mKeyboardChanged &&
@@ -1020,22 +892,24 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
     private void dismissKeyPreview() {
         for (PointerTracker tracker : mPointerTrackers)
             tracker.releaseKey();
-        showPreview(NOT_A_KEY, null);
+        showPreview(KeyDetector.NOT_A_KEY, null);
     }
 
+    @Override
     public void showPreview(int keyIndex, PointerTracker tracker) {
         int oldKeyIndex = mOldPreviewKeyIndex;
         mOldPreviewKeyIndex = keyIndex;
         // We should re-draw popup preview when 1) we need to hide the preview, 2) we will show
         // the space key preview and 3) pointer moves off the space key to other letter key, we
         // should hide the preview of the previous key.
+        @SuppressWarnings("unused")
         final boolean hidePreviewOrShowSpaceKeyPreview = (tracker == null)
                 || (SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCHER
                         && SubtypeSwitcher.getInstance().needsToDisplayLanguage()
                         && (tracker.isSpaceKey(keyIndex) || tracker.isSpaceKey(oldKeyIndex)));
         // If key changed and preview is on or the key is space (language switch is enabled)
         if (oldKeyIndex != keyIndex && (mShowPreview || (hidePreviewOrShowSpaceKeyPreview))) {
-            if (keyIndex == NOT_A_KEY) {
+            if (keyIndex == KeyDetector.NOT_A_KEY) {
                 mHandler.cancelPopupPreview();
                 mHandler.dismissPreview(mDelayAfterPreview);
             } else if (tracker != null) {
@@ -1145,9 +1019,10 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
      * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only
      * one key is changing it's content. Any changes that affect the position or size of the key
      * may not be honored.
-     * @param key key in the attached {@link BaseKeyboard}.
+     * @param key key in the attached {@link Keyboard}.
      * @see #invalidateAllKeys
      */
+    @Override
     public void invalidateKey(Key key) {
         if (key == null)
             return;
@@ -1183,7 +1058,7 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
     private void onLongPressShiftKey(PointerTracker tracker) {
         tracker.setAlreadyProcessed();
         mPointerQueue.remove(tracker);
-        mKeyboardActionListener.onKey(LatinKeyboardView.KEYCODE_CAPSLOCK, null, 0, 0);
+        mKeyboardActionListener.onKey(LatinKeyboard.KEYCODE_CAPSLOCK, null, 0, 0);
     }
 
     private View inflateMiniKeyboardContainer(Key popupKey) {
@@ -1194,34 +1069,43 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
         if (container == null)
             throw new NullPointerException();
 
-        BaseKeyboardView miniKeyboard =
-                (BaseKeyboardView)container.findViewById(R.id.BaseKeyboardView);
-        miniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() {
+        KeyboardView miniKeyboard =
+                (KeyboardView)container.findViewById(R.id.KeyboardView);
+        miniKeyboard.setOnKeyboardActionListener(new KeyboardActionListener() {
+            @Override
             public void onKey(int primaryCode, int[] keyCodes, int x, int y) {
                 mKeyboardActionListener.onKey(primaryCode, keyCodes, x, y);
                 dismissPopupKeyboard();
             }
 
+            @Override
             public void onText(CharSequence text) {
                 mKeyboardActionListener.onText(text);
                 dismissPopupKeyboard();
             }
 
+            @Override
             public void onCancel() {
                 dismissPopupKeyboard();
             }
 
+            @Override
             public void swipeLeft() {
             }
+            @Override
             public void swipeRight() {
             }
+            @Override
             public void swipeUp() {
             }
+            @Override
             public void swipeDown() {
             }
+            @Override
             public void onPress(int primaryCode) {
                 mKeyboardActionListener.onPress(primaryCode);
             }
+            @Override
             public void onRelease(int primaryCode) {
                 mKeyboardActionListener.onRelease(primaryCode);
             }
@@ -1231,12 +1115,12 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
         // Remove gesture detector on mini-keyboard
         miniKeyboard.mGestureDetector = null;
 
-        BaseKeyboard keyboard;
+        Keyboard keyboard;
         if (popupKey.popupCharacters != null) {
-            keyboard = new BaseKeyboard(getContext(), popupKeyboardId, popupKey.popupCharacters,
+            keyboard = new Keyboard(getContext(), popupKeyboardId, popupKey.popupCharacters,
                     -1, getPaddingLeft() + getPaddingRight());
         } else {
-            keyboard = new BaseKeyboard(getContext(), popupKeyboardId);
+            keyboard = new Keyboard(getContext(), popupKeyboardId);
         }
         miniKeyboard.setKeyboard(keyboard);
         miniKeyboard.setPopupParent(this);
@@ -1256,8 +1140,8 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
         // and bottom edge flags on.
         // When you want to use one row mini-keyboard from xml file, make sure that the row has
         // both top and bottom edge flags set.
-        return (edgeFlags & BaseKeyboard.EDGE_TOP) != 0
-                && (edgeFlags & BaseKeyboard.EDGE_BOTTOM) != 0;
+        return (edgeFlags & Keyboard.EDGE_TOP) != 0
+                && (edgeFlags & Keyboard.EDGE_BOTTOM) != 0;
     }
 
     /**
@@ -1279,7 +1163,7 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
             container = inflateMiniKeyboardContainer(popupKey);
             mMiniKeyboardCache.put(popupKey, container);
         }
-        mMiniKeyboard = (BaseKeyboardView)container.findViewById(R.id.BaseKeyboardView);
+        mMiniKeyboard = (KeyboardView)container.findViewById(R.id.KeyboardView);
         if (mWindowOffset == null) {
             mWindowOffset = new int[2];
             getLocationInWindow(mWindowOffset);
@@ -1325,7 +1209,7 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
         mMiniKeyboardOriginY = y + container.getPaddingTop() - mWindowOffset[1];
         mMiniKeyboard.setPopupOffset(adjustedX, y);
         // TODO: change the below line to use getLatinKeyboard() instead of getKeyboard()
-        BaseKeyboard baseMiniKeyboard = mMiniKeyboard.getKeyboard();
+        Keyboard baseMiniKeyboard = mMiniKeyboard.getKeyboard();
         if (baseMiniKeyboard != null && baseMiniKeyboard.setShifted(mKeyboard == null
                 ? false : mKeyboard.isShiftedOrShiftLocked())) {
             mMiniKeyboard.invalidateAllKeys();
@@ -1376,7 +1260,7 @@ public class BaseKeyboardView extends View implements PointerTracker.UIProxy {
     private PointerTracker getPointerTracker(final int id) {
         final ArrayList<PointerTracker> pointers = mPointerTrackers;
         final Key[] keys = mKeys;
-        final OnKeyboardActionListener listener = mKeyboardActionListener;
+        final KeyboardActionListener listener = mKeyboardActionListener;
 
         // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
         for (int i = pointers.size(); i <= id; i++) {
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKey.java b/java/src/com/android/inputmethod/keyboard/LatinKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..4eaf4c8f6f9c8f8a4d3e10049cafea859331f050
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/LatinKey.java
@@ -0,0 +1,90 @@
+/*
+ * 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;
+
+public class LatinKey extends Key {
+
+    // functional normal state (with properties)
+    private final int[] KEY_STATE_FUNCTIONAL_NORMAL = {
+            android.R.attr.state_single
+    };
+
+    // functional pressed state (with properties)
+    private final int[] KEY_STATE_FUNCTIONAL_PRESSED = {
+            android.R.attr.state_single,
+            android.R.attr.state_pressed
+    };
+
+    private boolean mShiftLockEnabled;
+
+    public LatinKey(Resources res, Row parent, int x, int y,
+            XmlResourceParser parser, KeyStyles keyStyles) {
+        super(res, parent, x, y, parser, keyStyles);
+        if (popupCharacters != null && popupCharacters.length() == 0) {
+            // If there is a keyboard with no keys specified in popupCharacters
+            popupResId = 0;
+        }
+    }
+
+    void enableShiftLock() {
+        mShiftLockEnabled = true;
+    }
+
+    // sticky is used for shift key.  If a key is not sticky and is modifier,
+    // the key will be treated as functional.
+    private boolean isFunctionalKey() {
+        return !sticky && modifier;
+    }
+
+    @Override
+    public void onReleased(boolean inside) {
+        if (!mShiftLockEnabled) {
+            super.onReleased(inside);
+        } else {
+            pressed = !pressed;
+        }
+    }
+
+    /**
+     * Overriding this method so that we can reduce the target area for certain keys.
+     */
+    @Override
+    public boolean isInside(int x, int y) {
+        boolean result = (keyboard instanceof LatinKeyboard)
+                && ((LatinKeyboard)keyboard).isInside(this, x, y);
+        return result;
+    }
+
+    boolean isInsideSuper(int x, int y) {
+        return super.isInside(x, y);
+    }
+
+    @Override
+    public int[] getCurrentDrawableState() {
+        if (isFunctionalKey()) {
+            if (pressed) {
+                return KEY_STATE_FUNCTIONAL_PRESSED;
+            } else {
+                return KEY_STATE_FUNCTIONAL_NORMAL;
+            }
+        }
+        return super.getCurrentDrawableState();
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
similarity index 72%
rename from java/src/com/android/inputmethod/latin/LatinKeyboard.java
rename to java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index cae0b10b3e508491bf98be25057ff31d81558a81..29f749a46d2da17ec291af6b43be583465714b30 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -1,12 +1,12 @@
 /*
  * Copyright (C) 2008 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
@@ -14,7 +14,11 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.keyboard;
+
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SubtypeSwitcher;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -22,28 +26,33 @@ import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.Paint.Align;
-import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.text.TextPaint;
 import android.util.Log;
-import android.view.ViewConfiguration;
 
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
-public class LatinKeyboard extends BaseKeyboard {
+public class LatinKeyboard extends Keyboard {
 
     private static final boolean DEBUG_PREFERRED_LETTER = false;
     private static final String TAG = "LatinKeyboard";
-    private static final int OPACITY_FULLY_OPAQUE = 255;
+
+    public static final int KEYCODE_OPTIONS = -100;
+    public static final int KEYCODE_OPTIONS_LONGPRESS = -101;
+    // TODO: remove this once LatinIME stops referring to this.
+    public static final int KEYCODE_VOICE = -102;
+    public static final int KEYCODE_NEXT_LANGUAGE = -104;
+    public static final int KEYCODE_PREV_LANGUAGE = -105;
+    public static final int KEYCODE_CAPSLOCK = -106;
+
+    static final int OPACITY_FULLY_OPAQUE = 255;
     private static final int SPACE_LED_LENGTH_PERCENT = 80;
 
     private Drawable mShiftLockPreviewIcon;
@@ -76,22 +85,22 @@ public class LatinKeyboard extends BaseKeyboard {
     // Minimum width of space key preview (proportional to keyboard width)
     private static final float SPACEBAR_POPUP_MIN_RATIO = 0.4f;
     // Height in space key the language name will be drawn. (proportional to space key height)
-    private static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f;
+    public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f;
     // If the full language name needs to be smaller than this value to be drawn on space key,
     // its short language name will be used instead.
     private static final float MINIMUM_SCALE_OF_LANGUAGE_NAME = 0.8f;
 
     private static int sSpacebarVerticalCorrection;
 
-    public LatinKeyboard(Context context, KeyboardSwitcher.KeyboardId id) {
+    public LatinKeyboard(Context context, KeyboardId id) {
         super(context, id);
         final Resources res = context.getResources();
         mContext = context;
         mRes = res;
-        if (id.mColorScheme == BaseKeyboardView.COLOR_SCHEME_BLACK) {
+        if (id.mColorScheme == KeyboardView.COLOR_SCHEME_BLACK) {
             mSpaceBarTextShadowColor = res.getColor(
                     R.color.latinkeyboard_bar_language_shadow_black);
-        } else { // default color scheme is BaseKeyboardView.COLOR_SCHEME_WHITE
+        } else { // default color scheme is KeyboardView.COLOR_SCHEME_WHITE
             mSpaceBarTextShadowColor = res.getColor(
                     R.color.latinkeyboard_bar_language_shadow_white);
         }
@@ -181,7 +190,7 @@ public class LatinKeyboard extends BaseKeyboard {
         return isAlphaKeyboard() && mShiftState.isManualTemporaryUpperCase();
     }
 
-    /* package */ LatinKeyboardShiftState getKeyboardShiftState() {
+    public LatinKeyboardShiftState getKeyboardShiftState() {
         return mShiftState;
     }
 
@@ -190,11 +199,11 @@ public class LatinKeyboard extends BaseKeyboard {
     }
 
     public boolean isPhoneKeyboard() {
-        return mId.mMode == KeyboardSwitcher.MODE_PHONE;
+        return mId.mMode == KeyboardId.MODE_PHONE;
     }
 
     public boolean isNumberKeyboard() {
-        return mId.mMode == KeyboardSwitcher.MODE_NUMBER;
+        return mId.mMode == KeyboardId.MODE_NUMBER;
     }
 
     /**
@@ -272,6 +281,7 @@ public class LatinKeyboard extends BaseKeyboard {
         return language;
     }
 
+    @SuppressWarnings("unused")
     private Bitmap drawSpaceBar(int opacity, boolean isAutoCompletion) {
         final int width = mSpaceKey.width;
         final int height = mSpaceIcon.getIntrinsicHeight();
@@ -334,7 +344,8 @@ public class LatinKeyboard extends BaseKeyboard {
             final int width = Math.max(mSpaceKey.width,
                     (int)(getMinWidth() * SPACEBAR_POPUP_MIN_RATIO));
             final int height = mSpacePreviewIcon.getIntrinsicHeight();
-            mSlidingLocaleIcon = new SlidingLocaleDrawable(mSpacePreviewIcon, width, height);
+            mSlidingLocaleIcon =
+                    new SlidingLocaleDrawable(mContext, mSpacePreviewIcon, width, height);
             mSlidingLocaleIcon.setBounds(0, 0, width, height);
             mSpaceKey.iconPreview = mSlidingLocaleIcon;
         }
@@ -380,6 +391,7 @@ public class LatinKeyboard extends BaseKeyboard {
      * 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) {
         final int code = key.codes[0];
         if (code == KEYCODE_SHIFT || code == KEYCODE_DELETE) {
@@ -528,193 +540,4 @@ public class LatinKeyboard extends BaseKeyboard {
         int textSize = array.getDimensionPixelSize(array.getResourceId(0, 0), defValue);
         return textSize;
     }
-
-    public static class LatinKey extends BaseKeyboard.Key {
-
-        // functional normal state (with properties)
-        private final int[] KEY_STATE_FUNCTIONAL_NORMAL = {
-                android.R.attr.state_single
-        };
-
-        // functional pressed state (with properties)
-        private final int[] KEY_STATE_FUNCTIONAL_PRESSED = {
-                android.R.attr.state_single,
-                android.R.attr.state_pressed
-        };
-
-        private boolean mShiftLockEnabled;
-
-        public LatinKey(Resources res, BaseKeyboard.Row parent, int x, int y,
-                XmlResourceParser parser, KeyStyles keyStyles) {
-            super(res, parent, x, y, parser, keyStyles);
-            if (popupCharacters != null && popupCharacters.length() == 0) {
-                // If there is a keyboard with no keys specified in popupCharacters
-                popupResId = 0;
-            }
-        }
-
-        private void enableShiftLock() {
-            mShiftLockEnabled = true;
-        }
-
-        // sticky is used for shift key.  If a key is not sticky and is modifier,
-        // the key will be treated as functional.
-        private boolean isFunctionalKey() {
-            return !sticky && modifier;
-        }
-
-        @Override
-        public void onReleased(boolean inside) {
-            if (!mShiftLockEnabled) {
-                super.onReleased(inside);
-            } else {
-                pressed = !pressed;
-            }
-        }
-
-        /**
-         * Overriding this method so that we can reduce the target area for certain keys.
-         */
-        @Override
-        public boolean isInside(int x, int y) {
-            boolean result = (keyboard instanceof LatinKeyboard)
-                    && ((LatinKeyboard)keyboard).isInside(this, x, y);
-            return result;
-        }
-
-        private boolean isInsideSuper(int x, int y) {
-            return super.isInside(x, y);
-        }
-
-        @Override
-        public int[] getCurrentDrawableState() {
-            if (isFunctionalKey()) {
-                if (pressed) {
-                    return KEY_STATE_FUNCTIONAL_PRESSED;
-                } else {
-                    return KEY_STATE_FUNCTIONAL_NORMAL;
-                }
-            }
-            return super.getCurrentDrawableState();
-        }
-    }
-
-    /**
-     * Animation to be displayed on the spacebar preview popup when switching 
-     * languages by swiping the spacebar. It draws the current, previous and
-     * next languages and moves them by the delta of touch movement on the spacebar.
-     */
-    private class SlidingLocaleDrawable extends Drawable {
-
-        private final int mWidth;
-        private final int mHeight;
-        private final Drawable mBackground;
-        private final TextPaint mTextPaint;
-        private final int mMiddleX;
-        private final Drawable mLeftDrawable;
-        private final Drawable mRightDrawable;
-        private final int mThreshold;
-        private int mDiff;
-        private boolean mHitThreshold;
-        private String mCurrentLanguage;
-        private String mNextLanguage;
-        private String mPrevLanguage;
-
-        public SlidingLocaleDrawable(Drawable background, int width, int height) {
-            mBackground = background;
-            setDefaultBounds(mBackground);
-            mWidth = width;
-            mHeight = height;
-            final TextPaint textPaint = new TextPaint();
-            textPaint.setTextSize(getTextSizeFromTheme(android.R.style.TextAppearance_Medium, 18));
-            textPaint.setColor(R.color.latinkeyboard_transparent);
-            textPaint.setTextAlign(Align.CENTER);
-            textPaint.setAlpha(OPACITY_FULLY_OPAQUE);
-            textPaint.setAntiAlias(true);
-            mTextPaint = textPaint;
-            mMiddleX = (mWidth - mBackground.getIntrinsicWidth()) / 2;
-            final Resources res = mRes;
-            mLeftDrawable = res.getDrawable(
-                    R.drawable.sym_keyboard_feedback_language_arrows_left);
-            mRightDrawable = res.getDrawable(
-                    R.drawable.sym_keyboard_feedback_language_arrows_right);
-            mThreshold = ViewConfiguration.get(mContext).getScaledTouchSlop();
-        }
-
-        private void setDiff(int diff) {
-            if (diff == Integer.MAX_VALUE) {
-                mHitThreshold = false;
-                mCurrentLanguage = null;
-                return;
-            }
-            mDiff = diff;
-            if (mDiff > mWidth) mDiff = mWidth;
-            if (mDiff < -mWidth) mDiff = -mWidth;
-            if (Math.abs(mDiff) > mThreshold) mHitThreshold = true;
-            invalidateSelf();
-        }
-
-
-        @Override
-        public void draw(Canvas canvas) {
-            canvas.save();
-            if (mHitThreshold) {
-                Paint paint = mTextPaint;
-                final int width = mWidth;
-                final int height = mHeight;
-                final int diff = mDiff;
-                final Drawable lArrow = mLeftDrawable;
-                final Drawable rArrow = mRightDrawable;
-                canvas.clipRect(0, 0, width, height);
-                if (mCurrentLanguage == null) {
-                    SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
-                    mCurrentLanguage = subtypeSwitcher.getInputLanguageName();
-                    mNextLanguage = subtypeSwitcher.getNextInputLanguageName();
-                    mPrevLanguage = subtypeSwitcher.getPreviousInputLanguageName();
-                }
-                // Draw language text with shadow
-                final float baseline = mHeight * SPACEBAR_LANGUAGE_BASELINE - paint.descent();
-                paint.setColor(mRes.getColor(R.color.latinkeyboard_feedback_language_text));
-                canvas.drawText(mCurrentLanguage, width / 2 + diff, baseline, paint);
-                canvas.drawText(mNextLanguage, diff - width / 2, baseline, paint);
-                canvas.drawText(mPrevLanguage, diff + width + width / 2, baseline, paint);
-
-                setDefaultBounds(lArrow);
-                rArrow.setBounds(width - rArrow.getIntrinsicWidth(), 0, width,
-                        rArrow.getIntrinsicHeight());
-                lArrow.draw(canvas);
-                rArrow.draw(canvas);
-            }
-            if (mBackground != null) {
-                canvas.translate(mMiddleX, 0);
-                mBackground.draw(canvas);
-            }
-            canvas.restore();
-        }
-
-        @Override
-        public int getOpacity() {
-            return PixelFormat.TRANSLUCENT;
-        }
-
-        @Override
-        public void setAlpha(int alpha) {
-            // Ignore
-        }
-
-        @Override
-        public void setColorFilter(ColorFilter cf) {
-            // Ignore
-        }
-
-        @Override
-        public int getIntrinsicWidth() {
-            return mWidth;
-        }
-
-        @Override
-        public int getIntrinsicHeight() {
-            return mHeight;
-        }
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardShiftState.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardShiftState.java
similarity index 80%
rename from java/src/com/android/inputmethod/latin/LatinKeyboardShiftState.java
rename to java/src/com/android/inputmethod/keyboard/LatinKeyboardShiftState.java
index e916306c84316d30c1363619d8b9d9cb897cefec..6d78428570bb04fa3a8b62c1adcc9918877ac8e4 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboardShiftState.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardShiftState.java
@@ -1,4 +1,22 @@
-package com.android.inputmethod.latin;
+/*
+ * 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 com.android.inputmethod.latin.KeyboardSwitcher;
 
 import android.util.Log;
 
@@ -86,4 +104,4 @@ public class LatinKeyboardShiftState {
         default: return "UKNOWN";
         }
     }
-}
\ No newline at end of file
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
similarity index 93%
rename from java/src/com/android/inputmethod/latin/LatinKeyboardView.java
rename to java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index ac68e3c398fa6beb4367815296b23a0922012182..55427d23f0f9e87e0f3cd849a8a51fca953c9561 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -1,12 +1,12 @@
 /*
  * Copyright (C) 2008 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
@@ -14,9 +14,9 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.keyboard;
 
-import com.android.inputmethod.latin.BaseKeyboard.Key;
+import com.android.inputmethod.latin.LatinIMEUtil;
 import com.android.inputmethod.voice.VoiceIMEConnector;
 
 import android.content.Context;
@@ -31,15 +31,7 @@ import android.view.MotionEvent;
 
 import java.util.List;
 
-public class LatinKeyboardView extends BaseKeyboardView {
-
-    public static final int KEYCODE_OPTIONS = -100;
-    public static final int KEYCODE_OPTIONS_LONGPRESS = -101;
-    // TODO: remove this once LatinIME stops referring to this.
-    public static final int KEYCODE_VOICE = -102;
-    public static final int KEYCODE_NEXT_LANGUAGE = -104;
-    public static final int KEYCODE_PREV_LANGUAGE = -105;
-    public static final int KEYCODE_CAPSLOCK = -106;
+public class LatinKeyboardView extends KeyboardView {
 
     /** Whether we've started dropping move events because we found a big jump */
     private boolean mDroppingEvents;
@@ -84,7 +76,7 @@ public class LatinKeyboardView extends BaseKeyboardView {
     }
 
     public LatinKeyboard getLatinKeyboard() {
-        BaseKeyboard keyboard = getKeyboard();
+        Keyboard keyboard = getKeyboard();
         if (keyboard instanceof LatinKeyboard) {
             return (LatinKeyboard)keyboard;
         } else {
@@ -95,8 +87,8 @@ public class LatinKeyboardView extends BaseKeyboardView {
     @Override
     protected boolean onLongPress(Key key) {
         int primaryCode = key.codes[0];
-        if (primaryCode == KEYCODE_OPTIONS) {
-            return invokeOnKey(KEYCODE_OPTIONS_LONGPRESS);
+        if (primaryCode == LatinKeyboard.KEYCODE_OPTIONS) {
+            return invokeOnKey(LatinKeyboard.KEYCODE_OPTIONS_LONGPRESS);
         } else if (primaryCode == '0' && getLatinKeyboard().isPhoneKeyboard()) {
             // Long pressing on 0 in phone number keypad gives you a '+'.
             return invokeOnKey('+');
@@ -107,8 +99,8 @@ public class LatinKeyboardView extends BaseKeyboardView {
 
     private boolean invokeOnKey(int primaryCode) {
         getOnKeyboardActionListener().onKey(primaryCode, null,
-                BaseKeyboardView.NOT_A_TOUCH_COORDINATE,
-                BaseKeyboardView.NOT_A_TOUCH_COORDINATE);
+                KeyboardView.NOT_A_TOUCH_COORDINATE,
+                KeyboardView.NOT_A_TOUCH_COORDINATE);
         return true;
     }
 
@@ -223,7 +215,8 @@ public class LatinKeyboardView extends BaseKeyboardView {
             int languageDirection = keyboard.getLanguageChangeDirection();
             if (languageDirection != 0) {
                 getOnKeyboardActionListener().onKey(
-                        languageDirection == 1 ? KEYCODE_NEXT_LANGUAGE : KEYCODE_PREV_LANGUAGE,
+                        languageDirection == 1
+                        ? LatinKeyboard.KEYCODE_NEXT_LANGUAGE : LatinKeyboard.KEYCODE_PREV_LANGUAGE,
                         null, mLastX, mLastY);
                 me.setAction(MotionEvent.ACTION_CANCEL);
                 keyboard.keyReleased();
@@ -236,8 +229,8 @@ public class LatinKeyboardView extends BaseKeyboardView {
 
     /****************************  INSTRUMENTATION  *******************************/
 
-    static final boolean DEBUG_AUTO_PLAY = false;
-    static final boolean DEBUG_LINE = false;
+    public static final boolean DEBUG_AUTO_PLAY = false;
+    public static final boolean DEBUG_LINE = false;
     private static final int MSG_TOUCH_DOWN = 1;
     private static final int MSG_TOUCH_UP = 2;
 
diff --git a/java/src/com/android/inputmethod/latin/MiniKeyboardKeyDetector.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
similarity index 86%
rename from java/src/com/android/inputmethod/latin/MiniKeyboardKeyDetector.java
rename to java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
index 3cc43b99cda6602f918d58678804209f04dcf2d6..53879cde11ebc0a14e5e3cbfd1c3862f271dfd90 100644
--- a/java/src/com/android/inputmethod/latin/MiniKeyboardKeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
@@ -14,11 +14,9 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.keyboard;
 
-import com.android.inputmethod.latin.BaseKeyboard.Key;
-
-class MiniKeyboardKeyDetector extends KeyDetector {
+public class MiniKeyboardKeyDetector extends KeyDetector {
     private static final int MAX_NEARBY_KEYS = 1;
 
     private final int mSlideAllowanceSquare;
@@ -42,7 +40,7 @@ class MiniKeyboardKeyDetector extends KeyDetector {
         final int touchX = getTouchX(x);
         final int touchY = getTouchY(y);
 
-        int closestKeyIndex = BaseKeyboardView.NOT_A_KEY;
+        int closestKeyIndex = NOT_A_KEY;
         int closestKeyDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
         final int keyCount = keys.length;
         for (int index = 0; index < keyCount; index++) {
@@ -53,7 +51,7 @@ class MiniKeyboardKeyDetector extends KeyDetector {
             }
         }
 
-        if (allKeys != null && closestKeyIndex != BaseKeyboardView.NOT_A_KEY)
+        if (allKeys != null && closestKeyIndex != NOT_A_KEY)
             allKeys[0] = keys[closestKeyIndex].codes[0];
         return closestKeyIndex;
     }
diff --git a/java/src/com/android/inputmethod/latin/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
similarity index 94%
rename from java/src/com/android/inputmethod/latin/PointerTracker.java
rename to java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 327fef107879b2f8815ce8b5094de990261f36de..aa0f9bd3758da44aafdd35fe625cafa2c3e975f9 100644
--- a/java/src/com/android/inputmethod/latin/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -14,11 +14,11 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.keyboard;
 
-import com.android.inputmethod.latin.BaseKeyboard.Key;
-import com.android.inputmethod.latin.BaseKeyboardView.OnKeyboardActionListener;
-import com.android.inputmethod.latin.BaseKeyboardView.UIHandler;
+import com.android.inputmethod.keyboard.KeyboardView.UIHandler;
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.R;
 
 import android.content.res.Resources;
 import android.util.Log;
@@ -44,16 +44,16 @@ public class PointerTracker {
     private final int mMultiTapKeyTimeout;
 
     // Miscellaneous constants
-    private static final int NOT_A_KEY = BaseKeyboardView.NOT_A_KEY;
-    private static final int[] KEY_DELETE = { BaseKeyboard.KEYCODE_DELETE };
+    private static final int NOT_A_KEY = KeyDetector.NOT_A_KEY;
+    private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE };
 
     private final UIProxy mProxy;
     private final UIHandler mHandler;
     private final KeyDetector mKeyDetector;
-    private OnKeyboardActionListener mListener;
+    private KeyboardActionListener mListener;
     private final boolean mHasDistinctMultitouch;
 
-    private BaseKeyboard mKeyboard;
+    private Keyboard mKeyboard;
     private Key[] mKeys;
     private int mKeyHysteresisDistanceSquared = -1;
 
@@ -181,11 +181,11 @@ public class PointerTracker {
         resetMultiTap();
     }
 
-    public void setOnKeyboardActionListener(OnKeyboardActionListener listener) {
+    public void setOnKeyboardActionListener(KeyboardActionListener listener) {
         mListener = listener;
     }
 
-    public void setKeyboard(BaseKeyboard keyboard, Key[] keys, float keyHysteresisDistance) {
+    public void setKeyboard(Keyboard keyboard, Key[] keys, float keyHysteresisDistance) {
         if (keyboard == null || keys == null || keyHysteresisDistance < 0)
             throw new IllegalArgumentException();
         mKeyboard = keyboard;
@@ -208,8 +208,8 @@ public class PointerTracker {
         if (key == null)
             return false;
         int primaryCode = key.codes[0];
-        return primaryCode == BaseKeyboard.KEYCODE_SHIFT
-                || primaryCode == BaseKeyboard.KEYCODE_MODE_CHANGE;
+        return primaryCode == Keyboard.KEYCODE_SHIFT
+                || primaryCode == Keyboard.KEYCODE_MODE_CHANGE;
     }
 
     public boolean isModifier() {
@@ -420,7 +420,7 @@ public class PointerTracker {
 
     private void startLongPressTimer(int keyIndex) {
         Key key = getKey(keyIndex);
-        if (key.codes[0] == BaseKeyboard.KEYCODE_SHIFT) {
+        if (key.codes[0] == Keyboard.KEYCODE_SHIFT) {
             mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this);
         } else {
             mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this);
@@ -433,7 +433,7 @@ public class PointerTracker {
     }
 
     private void detectAndSendKey(int index, int x, int y, long eventTime) {
-        final OnKeyboardActionListener listener = mListener;
+        final KeyboardActionListener listener = mListener;
         final Key key = getKey(index);
 
         if (key == null) {
@@ -453,7 +453,7 @@ public class PointerTracker {
                 // Multi-tap
                 if (mInMultiTap) {
                     if (mTapCount != -1) {
-                        mListener.onKey(BaseKeyboard.KEYCODE_DELETE, KEY_DELETE, x, y);
+                        mListener.onKey(Keyboard.KEYCODE_DELETE, KEY_DELETE, x, y);
                     } else {
                         mTapCount = 0;
                     }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java
new file mode 100644
index 0000000000000000000000000000000000000000..a3c0808282082495b6af37fe012a5e8e41a89ba4
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java
@@ -0,0 +1,67 @@
+/*
+ * 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 java.util.LinkedList;
+
+public class PointerTrackerQueue {
+    private LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>();
+
+    public void add(PointerTracker tracker) {
+        mQueue.add(tracker);
+    }
+
+    public int lastIndexOf(PointerTracker tracker) {
+        LinkedList<PointerTracker> queue = mQueue;
+        for (int index = queue.size() - 1; index >= 0; index--) {
+            PointerTracker t = queue.get(index);
+            if (t == tracker)
+                return index;
+        }
+        return -1;
+    }
+
+    public void releaseAllPointersOlderThan(PointerTracker tracker, long eventTime) {
+        LinkedList<PointerTracker> queue = mQueue;
+        int oldestPos = 0;
+        for (PointerTracker t = queue.get(oldestPos); t != tracker; t = queue.get(oldestPos)) {
+            if (t.isModifier()) {
+                oldestPos++;
+            } else {
+                t.onUpEvent(t.getLastX(), t.getLastY(), eventTime);
+                t.setAlreadyProcessed();
+                queue.remove(oldestPos);
+            }
+        }
+    }
+
+    public void releaseAllPointersExcept(PointerTracker tracker, long eventTime) {
+        for (PointerTracker t : mQueue) {
+            if (t == tracker)
+                continue;
+            t.onUpEvent(t.getLastX(), t.getLastY(), eventTime);
+            t.setAlreadyProcessed();
+        }
+        mQueue.clear();
+        if (tracker != null)
+            mQueue.add(tracker);
+    }
+
+    public void remove(PointerTracker tracker) {
+        mQueue.remove(tracker);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/ProximityKeyDetector.java b/java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java
similarity index 88%
rename from java/src/com/android/inputmethod/latin/ProximityKeyDetector.java
rename to java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java
index 35bdc672843326fcdb705404ca3ed504366803de..047173445bc8d56cf36c48a0c58fd75a1ab5a1bb 100644
--- a/java/src/com/android/inputmethod/latin/ProximityKeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java
@@ -14,13 +14,11 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
-
-import com.android.inputmethod.latin.BaseKeyboard.Key;
+package com.android.inputmethod.keyboard;
 
 import java.util.Arrays;
 
-class ProximityKeyDetector extends KeyDetector {
+public class ProximityKeyDetector extends KeyDetector {
     private static final int MAX_NEARBY_KEYS = 12;
 
     // working area
@@ -37,8 +35,8 @@ class ProximityKeyDetector extends KeyDetector {
         final int touchX = getTouchX(x);
         final int touchY = getTouchY(y);
 
-        int primaryIndex = BaseKeyboardView.NOT_A_KEY;
-        int closestKeyIndex = BaseKeyboardView.NOT_A_KEY;
+        int primaryIndex = NOT_A_KEY;
+        int closestKeyIndex = NOT_A_KEY;
         int closestKeyDist = mProximityThresholdSquare + 1;
         final int[] distances = mDistances;
         Arrays.fill(distances, Integer.MAX_VALUE);
@@ -72,6 +70,6 @@ class ProximityKeyDetector extends KeyDetector {
             }
         }
 
-        return primaryIndex == BaseKeyboardView.NOT_A_KEY ? closestKeyIndex : primaryIndex;
+        return primaryIndex == NOT_A_KEY ? closestKeyIndex : primaryIndex;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/Row.java b/java/src/com/android/inputmethod/keyboard/Row.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb778f317d39242aa0cf46cb5a01039ecc143a1a
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/Row.java
@@ -0,0 +1,73 @@
+/*
+ * 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 com.android.inputmethod.latin.R;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.Xml;
+
+/**
+ * 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 class Row {
+    /** Default width of a key in this row. */
+    public int defaultWidth;
+    /** Default height of a key in this row. */
+    public int defaultHeight;
+    /** Default horizontal gap between keys in this row. */
+    public int defaultHorizontalGap;
+    /** Vertical gap following this row. */
+    public int verticalGap;
+    /**
+     * 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 rowEdgeFlags;
+
+    final Keyboard parent;
+
+    Row(Keyboard parent) {
+        this.parent = parent;
+    }
+
+    public Row(Resources res, Keyboard parent, XmlResourceParser parser) {
+        this.parent = parent;
+        TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
+                R.styleable.Keyboard);
+        defaultWidth = KeyboardParser.getDimensionOrFraction(a,
+                R.styleable.Keyboard_keyWidth,
+                parent.mDisplayWidth, parent.mDefaultWidth);
+        defaultHeight = KeyboardParser.getDimensionOrFraction(a,
+                R.styleable.Keyboard_keyHeight,
+                parent.mDisplayHeight, parent.mDefaultHeight);
+        defaultHorizontalGap = KeyboardParser.getDimensionOrFraction(a,
+                R.styleable.Keyboard_horizontalGap,
+                parent.mDisplayWidth, parent.mDefaultHorizontalGap);
+        verticalGap = KeyboardParser.getDimensionOrFraction(a,
+                R.styleable.Keyboard_verticalGap,
+                parent.mDisplayHeight, parent.mDefaultVerticalGap);
+        a.recycle();
+        a = res.obtainAttributes(Xml.asAttributeSet(parser),
+                R.styleable.Keyboard_Row);
+        rowEdgeFlags = a.getInt(R.styleable.Keyboard_Row_rowEdgeFlags, 0);
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java b/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java
new file mode 100644
index 0000000000000000000000000000000000000000..689791cff79601475d5f4272c9bba68924dbf34f
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java
@@ -0,0 +1,163 @@
+/*
+ * 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 com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Paint.Align;
+import android.graphics.drawable.Drawable;
+import android.text.TextPaint;
+import android.view.ViewConfiguration;
+
+/**
+ * Animation to be displayed on the spacebar preview popup when switching languages by swiping the
+ * spacebar. It draws the current, previous and next languages and moves them by the delta of touch
+ * movement on the spacebar.
+ */
+public class SlidingLocaleDrawable extends Drawable {
+
+    private final Context mContext;
+    private final Resources mRes;
+    private final int mWidth;
+    private final int mHeight;
+    private final Drawable mBackground;
+    private final TextPaint mTextPaint;
+    private final int mMiddleX;
+    private final Drawable mLeftDrawable;
+    private final Drawable mRightDrawable;
+    private final int mThreshold;
+    private int mDiff;
+    private boolean mHitThreshold;
+    private String mCurrentLanguage;
+    private String mNextLanguage;
+    private String mPrevLanguage;
+
+    public SlidingLocaleDrawable(Context context, Drawable background, int width, int height) {
+        mContext = context;
+        mRes = context.getResources();
+        mBackground = background;
+        LatinKeyboard.setDefaultBounds(mBackground);
+        mWidth = width;
+        mHeight = height;
+        final TextPaint textPaint = new TextPaint();
+        textPaint.setTextSize(getTextSizeFromTheme(android.R.style.TextAppearance_Medium, 18));
+        textPaint.setColor(R.color.latinkeyboard_transparent);
+        textPaint.setTextAlign(Align.CENTER);
+        textPaint.setAlpha(LatinKeyboard.OPACITY_FULLY_OPAQUE);
+        textPaint.setAntiAlias(true);
+        mTextPaint = textPaint;
+        mMiddleX = (mWidth - mBackground.getIntrinsicWidth()) / 2;
+        final Resources res = mRes;
+        mLeftDrawable = res.getDrawable(
+                R.drawable.sym_keyboard_feedback_language_arrows_left);
+        mRightDrawable = res.getDrawable(
+                R.drawable.sym_keyboard_feedback_language_arrows_right);
+        mThreshold = ViewConfiguration.get(mContext).getScaledTouchSlop();
+    }
+
+    private int getTextSizeFromTheme(int style, int defValue) {
+        TypedArray array = mContext.getTheme().obtainStyledAttributes(
+                style, new int[] { android.R.attr.textSize });
+        int textSize = array.getDimensionPixelSize(array.getResourceId(0, 0), defValue);
+        return textSize;
+    }
+
+    void setDiff(int diff) {
+        if (diff == Integer.MAX_VALUE) {
+            mHitThreshold = false;
+            mCurrentLanguage = null;
+            return;
+        }
+        mDiff = diff;
+        if (mDiff > mWidth) mDiff = mWidth;
+        if (mDiff < -mWidth) mDiff = -mWidth;
+        if (Math.abs(mDiff) > mThreshold) mHitThreshold = true;
+        invalidateSelf();
+    }
+
+
+    @Override
+    public void draw(Canvas canvas) {
+        canvas.save();
+        if (mHitThreshold) {
+            Paint paint = mTextPaint;
+            final int width = mWidth;
+            final int height = mHeight;
+            final int diff = mDiff;
+            final Drawable lArrow = mLeftDrawable;
+            final Drawable rArrow = mRightDrawable;
+            canvas.clipRect(0, 0, width, height);
+            if (mCurrentLanguage == null) {
+                SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
+                mCurrentLanguage = subtypeSwitcher.getInputLanguageName();
+                mNextLanguage = subtypeSwitcher.getNextInputLanguageName();
+                mPrevLanguage = subtypeSwitcher.getPreviousInputLanguageName();
+            }
+            // Draw language text with shadow
+            final float baseline = mHeight * LatinKeyboard.SPACEBAR_LANGUAGE_BASELINE
+                    - paint.descent();
+            paint.setColor(mRes.getColor(R.color.latinkeyboard_feedback_language_text));
+            canvas.drawText(mCurrentLanguage, width / 2 + diff, baseline, paint);
+            canvas.drawText(mNextLanguage, diff - width / 2, baseline, paint);
+            canvas.drawText(mPrevLanguage, diff + width + width / 2, baseline, paint);
+
+            LatinKeyboard.setDefaultBounds(lArrow);
+            rArrow.setBounds(width - rArrow.getIntrinsicWidth(), 0, width,
+                    rArrow.getIntrinsicHeight());
+            lArrow.draw(canvas);
+            rArrow.draw(canvas);
+        }
+        if (mBackground != null) {
+            canvas.translate(mMiddleX, 0);
+            mBackground.draw(canvas);
+        }
+        canvas.restore();
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        // Ignore
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        // Ignore
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mWidth;
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mHeight;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/SwipeTracker.java b/java/src/com/android/inputmethod/keyboard/SwipeTracker.java
similarity index 97%
rename from java/src/com/android/inputmethod/latin/SwipeTracker.java
rename to java/src/com/android/inputmethod/keyboard/SwipeTracker.java
index 970e9196560160d22f9ad2ef904392010b8ea60b..730cdc3902e1ff2beef39028cdda3dde7e55881d 100644
--- a/java/src/com/android/inputmethod/latin/SwipeTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/SwipeTracker.java
@@ -14,11 +14,11 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.keyboard;
 
 import android.view.MotionEvent;
 
-class SwipeTracker {
+public class SwipeTracker {
     private static final int NUM_PAST = 4;
     private static final int LONGEST_PAST_TIME = 200;
 
@@ -91,7 +91,7 @@ class SwipeTracker {
         return mYVelocity;
     }
 
-    static class EventRingBuffer {
+    public static class EventRingBuffer {
         private final int bufSize;
         private final float xBuf[];
         private final float yBuf[];
@@ -154,4 +154,4 @@ class SwipeTracker {
             end = advance(end);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/java/src/com/android/inputmethod/latin/BaseKeyboard.java b/java/src/com/android/inputmethod/latin/BaseKeyboard.java
deleted file mode 100644
index e2331f33405f2008c1cce49e2cf81fcd3e86025d..0000000000000000000000000000000000000000
--- a/java/src/com/android/inputmethod/latin/BaseKeyboard.java
+++ /dev/null
@@ -1,717 +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.latin;
-
-import com.android.inputmethod.latin.BaseKeyboardParser.ParseException;
-import com.android.inputmethod.latin.KeyStyles.KeyStyle;
-import com.android.inputmethod.latin.KeyboardSwitcher.KeyboardId;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Xml;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
- * consists of rows of keys.
- * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
- * <pre>
- * &lt;Keyboard
- *         latin:keyWidth="%10p"
- *         latin:keyHeight="50px"
- *         latin:horizontalGap="2px"
- *         latin:verticalGap="2px" &gt;
- *     &lt;Row latin:keyWidth="32px" &gt;
- *         &lt;Key latin:keyLabel="A" /&gt;
- *         ...
- *     &lt;/Row&gt;
- *     ...
- * &lt;/Keyboard&gt;
- * </pre>
- */
-public class BaseKeyboard {
-
-    static final String TAG = "BaseKeyboard";
-
-    public static final int EDGE_LEFT = 0x01;
-    public static final int EDGE_RIGHT = 0x02;
-    public static final int EDGE_TOP = 0x04;
-    public static final int EDGE_BOTTOM = 0x08;
-
-    public static final int KEYCODE_SHIFT = -1;
-    public static final int KEYCODE_MODE_CHANGE = -2;
-    public static final int KEYCODE_CANCEL = -3;
-    public static final int KEYCODE_DONE = -4;
-    public static final int KEYCODE_DELETE = -5;
-    public static final int KEYCODE_ALT = -6;
-
-    /** Horizontal gap default for all rows */
-    private int mDefaultHorizontalGap;
-
-    /** Default key width */
-    private int mDefaultWidth;
-
-    /** Default key height */
-    private int mDefaultHeight;
-
-    /** Default gap between rows */
-    private int mDefaultVerticalGap;
-
-    /** Is the keyboard in the shifted state */
-    private boolean mShifted;
-
-    /** List of shift keys in this keyboard */
-    private final List<Key> mShiftKeys = new ArrayList<Key>();
-
-    /** List of shift keys and its shifted state icon */
-    private final HashMap<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>();
-
-    /** Total height of the keyboard, including the padding and keys */
-    private int mTotalHeight;
-
-    /**
-     * Total width of the keyboard, including left side gaps and keys, but not any gaps on the
-     * right side.
-     */
-    private int mTotalWidth;
-
-    /** List of keys in this keyboard */
-    private final List<Key> mKeys = new ArrayList<Key>();
-
-    /** Width of the screen available to fit the keyboard */
-    private final int mDisplayWidth;
-
-    /** Height of the screen */
-    private final int mDisplayHeight;
-
-    protected final KeyboardId mId;
-
-    // Variables for pre-computing nearest keys.
-
-    public final int GRID_WIDTH;
-    public final int GRID_HEIGHT;
-    private final int GRID_SIZE;
-    private int mCellWidth;
-    private int mCellHeight;
-    private int[][] mGridNeighbors;
-    private int mProximityThreshold;
-    private static int[] EMPTY_INT_ARRAY = new int[0];
-    /** Number of key widths from current touch point to search for nearest keys. */
-    private static float SEARCH_DISTANCE = 1.2f;
-
-    /**
-     * 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 BaseKeyboard}
-     * defines.
-     */
-    public static class Row {
-        /** Default width of a key in this row. */
-        public int defaultWidth;
-        /** Default height of a key in this row. */
-        public int defaultHeight;
-        /** Default horizontal gap between keys in this row. */
-        public int defaultHorizontalGap;
-        /** Vertical gap following this row. */
-        public int verticalGap;
-        /**
-         * Edge flags for this row of keys. Possible values that can be assigned are
-         * {@link BaseKeyboard#EDGE_TOP EDGE_TOP} and {@link BaseKeyboard#EDGE_BOTTOM EDGE_BOTTOM}
-         */
-        public int rowEdgeFlags;
-
-        private final BaseKeyboard parent;
-
-        private Row(BaseKeyboard parent) {
-            this.parent = parent;
-        }
-
-        public Row(Resources res, BaseKeyboard parent, XmlResourceParser parser) {
-            this.parent = parent;
-            TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
-                    R.styleable.BaseKeyboard);
-            defaultWidth = BaseKeyboardParser.getDimensionOrFraction(a,
-                    R.styleable.BaseKeyboard_keyWidth,
-                    parent.mDisplayWidth, parent.mDefaultWidth);
-            defaultHeight = BaseKeyboardParser.getDimensionOrFraction(a,
-                    R.styleable.BaseKeyboard_keyHeight,
-                    parent.mDisplayHeight, parent.mDefaultHeight);
-            defaultHorizontalGap = BaseKeyboardParser.getDimensionOrFraction(a,
-                    R.styleable.BaseKeyboard_horizontalGap,
-                    parent.mDisplayWidth, parent.mDefaultHorizontalGap);
-            verticalGap = BaseKeyboardParser.getDimensionOrFraction(a,
-                    R.styleable.BaseKeyboard_verticalGap,
-                    parent.mDisplayHeight, parent.mDefaultVerticalGap);
-            a.recycle();
-            a = res.obtainAttributes(Xml.asAttributeSet(parser),
-                    R.styleable.BaseKeyboard_Row);
-            rowEdgeFlags = a.getInt(R.styleable.BaseKeyboard_Row_rowEdgeFlags, 0);
-        }
-    }
-
-    /**
-     * Class for describing the position and characteristics of a single key in the keyboard.
-     */
-    public static class Key {
-        /**
-         * All the key codes (unicode or custom code) that this key could generate, zero'th
-         * being the most important.
-         */
-        public int[] codes;
-        /** The unicode that this key generates in manual temporary upper case mode. */
-        public int manualTemporaryUpperCaseCode;
-
-        /** Label to display */
-        public CharSequence label;
-        /** Option of the label */
-        public int labelOption;
-
-        /** Icon to display instead of a label. Icon takes precedence over a label */
-        public Drawable icon;
-        /** Hint icon to display on the key in conjunction with the label */
-        public Drawable hintIcon;
-        /** Preview version of the icon, for the preview popup */
-        /**
-         * The hint icon to display on the key when keyboard is in manual temporary upper case
-         * mode.
-         */
-        public Drawable manualTemporaryUpperCaseHintIcon;
-
-        public Drawable iconPreview;
-        /** Width of the key, not including the gap */
-        public int width;
-        /** Height of the key, not including the gap */
-        public int height;
-        /** The horizontal gap before this key */
-        public int gap;
-        /** Whether this key is sticky, i.e., a toggle key */
-        public boolean sticky;
-        /** X coordinate of the key in the keyboard layout */
-        public int x;
-        /** Y coordinate of the key in the keyboard layout */
-        public int y;
-        /** The current pressed state of this key */
-        public boolean pressed;
-        /** If this is a sticky key, is it on? */
-        public boolean on;
-        /** Text to output when pressed. This can be multiple characters, like ".com" */
-        public CharSequence text;
-        /** Popup characters */
-        public CharSequence popupCharacters;
-
-        /**
-         * Flags that specify the anchoring to edges of the keyboard for detecting touch events
-         * that are just out of the boundary of the key. This is a bit mask of
-         * {@link BaseKeyboard#EDGE_LEFT}, {@link BaseKeyboard#EDGE_RIGHT},
-         * {@link BaseKeyboard#EDGE_TOP} and {@link BaseKeyboard#EDGE_BOTTOM}.
-         */
-        public int edgeFlags;
-        /** Whether this is a modifier key, such as Shift or Alt */
-        public boolean modifier;
-        /** The BaseKeyboard that this key belongs to */
-        protected final BaseKeyboard keyboard;
-        /**
-         * If this key pops up a mini keyboard, this is the resource id for the XML layout for that
-         * keyboard.
-         */
-        public int popupResId;
-        /** Whether this key repeats itself when held down */
-        public boolean repeatable;
-
-
-        private final static int[] KEY_STATE_NORMAL_ON = {
-            android.R.attr.state_checkable,
-            android.R.attr.state_checked
-        };
-
-        private final static int[] KEY_STATE_PRESSED_ON = {
-            android.R.attr.state_pressed,
-            android.R.attr.state_checkable,
-            android.R.attr.state_checked
-        };
-
-        private final static int[] KEY_STATE_NORMAL_OFF = {
-            android.R.attr.state_checkable
-        };
-
-        private final static int[] KEY_STATE_PRESSED_OFF = {
-            android.R.attr.state_pressed,
-            android.R.attr.state_checkable
-        };
-
-        private final static int[] KEY_STATE_NORMAL = {
-        };
-
-        private final static int[] KEY_STATE_PRESSED = {
-            android.R.attr.state_pressed
-        };
-
-        /** Create an empty key with no attributes. */
-        public Key(Row parent) {
-            keyboard = parent.parent;
-            height = parent.defaultHeight;
-            gap = parent.defaultHorizontalGap;
-            width = parent.defaultWidth - gap;
-            edgeFlags = parent.rowEdgeFlags;
-        }
-
-        /** 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
-         * a {@link BaseKeyboard}.
-         * @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,
-                KeyStyles keyStyles) {
-            this(parent);
-
-            TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
-                    R.styleable.BaseKeyboard);
-            height = BaseKeyboardParser.getDimensionOrFraction(a,
-                    R.styleable.BaseKeyboard_keyHeight,
-                    keyboard.mDisplayHeight, parent.defaultHeight);
-            gap = BaseKeyboardParser.getDimensionOrFraction(a,
-                    R.styleable.BaseKeyboard_horizontalGap,
-                    keyboard.mDisplayWidth, parent.defaultHorizontalGap);
-            width = BaseKeyboardParser.getDimensionOrFraction(a,
-                    R.styleable.BaseKeyboard_keyWidth,
-                    keyboard.mDisplayWidth, parent.defaultWidth) - gap;
-            a.recycle();
-
-            a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.BaseKeyboard_Key);
-
-            final KeyStyle style;
-            if (a.hasValue(R.styleable.BaseKeyboard_Key_keyStyle)) {
-                String styleName = a.getString(R.styleable.BaseKeyboard_Key_keyStyle);
-                style = keyStyles.getKeyStyle(styleName);
-                if (style == null)
-                    throw new ParseException("Unknown key style: " + styleName, parser);
-            } else {
-                style = keyStyles.getEmptyKeyStyle();
-            }
-
-            // Horizontal gap is divided equally to both sides of the key.
-            this.x = x + gap / 2;
-            this.y = y;
-
-            codes = style.getIntArray(a, R.styleable.BaseKeyboard_Key_codes);
-            iconPreview = style.getDrawable(a, R.styleable.BaseKeyboard_Key_iconPreview);
-            setDefaultBounds(iconPreview);
-            popupCharacters = style.getText(a, R.styleable.BaseKeyboard_Key_popupCharacters);
-            popupResId = style.getResourceId(a, R.styleable.BaseKeyboard_Key_popupKeyboard, 0);
-            repeatable = style.getBoolean(a, R.styleable.BaseKeyboard_Key_isRepeatable, false);
-            modifier = style.getBoolean(a, R.styleable.BaseKeyboard_Key_isModifier, false);
-            sticky = style.getBoolean(a, R.styleable.BaseKeyboard_Key_isSticky, false);
-            edgeFlags = style.getFlag(a, R.styleable.BaseKeyboard_Key_keyEdgeFlags, 0);
-            edgeFlags |= parent.rowEdgeFlags;
-
-            icon = style.getDrawable(a, R.styleable.BaseKeyboard_Key_keyIcon);
-            setDefaultBounds(icon);
-            hintIcon = style.getDrawable(a, R.styleable.BaseKeyboard_Key_keyHintIcon);
-            setDefaultBounds(hintIcon);
-            manualTemporaryUpperCaseHintIcon = style.getDrawable(a,
-                    R.styleable.BaseKeyboard_Key_manualTemporaryUpperCaseHintIcon);
-            setDefaultBounds(manualTemporaryUpperCaseHintIcon);
-
-            label = style.getText(a, R.styleable.BaseKeyboard_Key_keyLabel);
-            labelOption = style.getFlag(a, R.styleable.BaseKeyboard_Key_keyLabelOption, 0);
-            manualTemporaryUpperCaseCode = style.getInt(a,
-                    R.styleable.BaseKeyboard_Key_manualTemporaryUpperCaseCode, 0);
-            text = style.getText(a, R.styleable.BaseKeyboard_Key_keyOutputText);
-            final Drawable shiftedIcon = style.getDrawable(a,
-                    R.styleable.BaseKeyboard_Key_shiftedIcon);
-            if (shiftedIcon != null)
-                keyboard.getShiftedIcons().put(this, shiftedIcon);
-
-            if (codes == null && !TextUtils.isEmpty(label)) {
-                codes = new int[] { label.charAt(0) };
-            }
-            a.recycle();
-        }
-
-        /**
-         * Informs the key that it has been pressed, in case it needs to change its appearance or
-         * state.
-         * @see #onReleased(boolean)
-         */
-        public void onPressed() {
-            pressed = !pressed;
-        }
-
-        /**
-         * Changes the pressed state of the key. If it is a sticky key, it will also change the
-         * toggled state of the key if the finger was release inside.
-         * @param inside whether the finger was released inside the key
-         * @see #onPressed()
-         */
-        public void onReleased(boolean inside) {
-            pressed = !pressed;
-            if (sticky) {
-                on = !on;
-            }
-        }
-
-        /**
-         * Detects if a point falls inside 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.
-         */
-        public boolean isInside(int x, int y) {
-            boolean leftEdge = (edgeFlags & EDGE_LEFT) > 0;
-            boolean rightEdge = (edgeFlags & EDGE_RIGHT) > 0;
-            boolean topEdge = (edgeFlags & EDGE_TOP) > 0;
-            boolean bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0;
-            if ((x >= this.x || (leftEdge && x <= this.x + this.width))
-                    && (x < this.x + this.width || (rightEdge && x >= this.x))
-                    && (y >= this.y || (topEdge && y <= this.y + this.height))
-                    && (y < this.y + this.height || (bottomEdge && y >= this.y))) {
-                return true;
-            } else {
-                return false;
-            }
-        }
-
-        /**
-         * Returns the square of the distance to the nearest edge of the key and the given point.
-         * @param x the x-coordinate of the point
-         * @param y the y-coordinate of the point
-         * @return the square of the distance of the point from the nearest edge of the key
-         */
-        public int squaredDistanceToEdge(int x, int y) {
-            final int left = this.x;
-            final int right = left + this.width;
-            final int top = this.y;
-            final int bottom = top + this.height;
-            final int edgeX = x < left ? left : (x > right ? right : x);
-            final int edgeY = y < top ? top : (y > bottom ? bottom : y);
-            final int dx = x - edgeX;
-            final int dy = y - edgeY;
-            return dx * dx + dy * dy;
-        }
-
-        /**
-         * Returns the drawable state for the key, based on the current state and type of the key.
-         * @return the drawable state of the key.
-         * @see android.graphics.drawable.StateListDrawable#setState(int[])
-         */
-        public int[] getCurrentDrawableState() {
-            int[] states = KEY_STATE_NORMAL;
-
-            if (on) {
-                if (pressed) {
-                    states = KEY_STATE_PRESSED_ON;
-                } else {
-                    states = KEY_STATE_NORMAL_ON;
-                }
-            } else {
-                if (sticky) {
-                    if (pressed) {
-                        states = KEY_STATE_PRESSED_OFF;
-                    } else {
-                        states = KEY_STATE_NORMAL_OFF;
-                    }
-                } else {
-                    if (pressed) {
-                        states = KEY_STATE_PRESSED;
-                    }
-                }
-            }
-            return states;
-        }
-    }
-
-    /**
-     * Creates a keyboard from the given xml key layout file.
-     * @param context the application or service context
-     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
-     */
-    public BaseKeyboard(Context context, int xmlLayoutResId) {
-        this(context, xmlLayoutResId, null);
-    }
-
-    /**
-     * Creates a keyboard from the given keyboard identifier.
-     * @param context the application or service context
-     * @param id keyboard identifier
-     */
-    public BaseKeyboard(Context context, KeyboardId id) {
-        this(context, id.getXmlId(), id);
-    }
-
-    /**
-     * Creates a keyboard from the given xml key layout file.
-     * @param context the application or service context
-     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
-     * @param id keyboard identifier
-     */
-    private BaseKeyboard(Context context, int xmlLayoutResId, KeyboardId id) {
-        this(context, xmlLayoutResId, id,
-                context.getResources().getDisplayMetrics().widthPixels,
-                context.getResources().getDisplayMetrics().heightPixels);
-    }
-
-    private BaseKeyboard(Context context, int xmlLayoutResId, KeyboardId id, int width,
-            int height) {
-        Resources res = context.getResources();
-        GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
-        GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
-        GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;
-
-        mDisplayWidth = width;
-        mDisplayHeight = height;
-
-        mDefaultHorizontalGap = 0;
-        setKeyWidth(mDisplayWidth / 10);
-        mDefaultVerticalGap = 0;
-        mDefaultHeight = mDefaultWidth;
-        mId = id;
-        loadKeyboard(context, xmlLayoutResId);
-    }
-
-    /**
-     * <p>Creates a blank keyboard from the given resource file and populates it with the specified
-     * characters in left-to-right, top-to-bottom fashion, using the specified number of columns.
-     * </p>
-     * <p>If the specified number of columns is -1, then the keyboard will fit as many keys as
-     * possible in each row.</p>
-     * @param context the application or service context
-     * @param layoutTemplateResId the layout template file, containing no keys.
-     * @param characters the list of characters to display on the keyboard. One key will be created
-     * for each character.
-     * @param columns the number of columns of keys to display. If this number is greater than the
-     * number of keys that can fit in a row, it will be ignored. If this number is -1, the
-     * keyboard will fit as many keys as possible in each row.
-     */
-    public BaseKeyboard(Context context, int layoutTemplateResId,
-            CharSequence characters, int columns, int horizontalPadding) {
-        this(context, layoutTemplateResId);
-        int x = 0;
-        int y = 0;
-        int column = 0;
-        mTotalWidth = 0;
-
-        Row row = new Row(this);
-        row.defaultHeight = mDefaultHeight;
-        row.defaultWidth = mDefaultWidth;
-        row.defaultHorizontalGap = mDefaultHorizontalGap;
-        row.verticalGap = mDefaultVerticalGap;
-        row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;
-        final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
-        for (int i = 0; i < characters.length(); i++) {
-            char c = characters.charAt(i);
-            if (column >= maxColumns
-                    || x + mDefaultWidth + horizontalPadding > mDisplayWidth) {
-                x = 0;
-                y += mDefaultVerticalGap + mDefaultHeight;
-                column = 0;
-            }
-            final Key key = new Key(row);
-            // Horizontal gap is divided equally to both sides of the key.
-            key.x = x + key.gap / 2;
-            key.y = y;
-            key.label = String.valueOf(c);
-            key.codes = new int[] { c };
-            column++;
-            x += key.width + key.gap;
-            mKeys.add(key);
-            if (x > mTotalWidth) {
-                mTotalWidth = x;
-            }
-        }
-        mTotalHeight = y + mDefaultHeight;
-    }
-
-    public KeyboardId getKeyboardId() {
-        return mId;
-    }
-
-    public List<Key> getKeys() {
-        return mKeys;
-    }
-
-    protected int getHorizontalGap() {
-        return mDefaultHorizontalGap;
-    }
-
-    protected void setHorizontalGap(int gap) {
-        mDefaultHorizontalGap = gap;
-    }
-
-    protected int getVerticalGap() {
-        return mDefaultVerticalGap;
-    }
-
-    protected void setVerticalGap(int gap) {
-        mDefaultVerticalGap = gap;
-    }
-
-    protected int getKeyHeight() {
-        return mDefaultHeight;
-    }
-
-    protected void setKeyHeight(int height) {
-        mDefaultHeight = height;
-    }
-
-    protected int getKeyWidth() {
-        return mDefaultWidth;
-    }
-
-    protected void setKeyWidth(int width) {
-        mDefaultWidth = width;
-        final int threshold = (int) (width * SEARCH_DISTANCE);
-        mProximityThreshold = threshold * threshold;
-    }
-
-    /**
-     * Returns the total height of the keyboard
-     * @return the total height of the keyboard
-     */
-    public int getHeight() {
-        return mTotalHeight;
-    }
-
-    public int getMinWidth() {
-        return mTotalWidth;
-    }
-
-    public int getKeyboardHeight() {
-        return mDisplayHeight;
-    }
-
-    public int getKeyboardWidth() {
-        return mDisplayWidth;
-    }
-
-    public boolean setShifted(boolean shiftState) {
-        for (final Key key : mShiftKeys) {
-            key.on = shiftState;
-        }
-        if (mShifted != shiftState) {
-            mShifted = shiftState;
-            return true;
-        }
-        return false;
-    }
-
-    public boolean isShiftedOrShiftLocked() {
-        return mShifted;
-    }
-
-    public List<Key> getShiftKeys() {
-        return mShiftKeys;
-    }
-
-    public Map<Key, Drawable> getShiftedIcons() {
-        return mShiftedIcons;
-    }
-
-    private void computeNearestNeighbors() {
-        // Round-up so we don't have any pixels outside the grid
-        mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH;
-        mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT;
-        mGridNeighbors = new int[GRID_SIZE][];
-        final int[] indices = new int[mKeys.size()];
-        final int gridWidth = GRID_WIDTH * mCellWidth;
-        final int gridHeight = GRID_HEIGHT * mCellHeight;
-        final int threshold = mProximityThreshold;
-        for (int x = 0; x < gridWidth; x += mCellWidth) {
-            for (int y = 0; y < gridHeight; y += mCellHeight) {
-                final int centerX = x + mCellWidth / 2;
-                final int centerY = y + mCellHeight / 2;
-                int count = 0;
-                for (int i = 0; i < mKeys.size(); i++) {
-                    final Key key = mKeys.get(i);
-                    if (key.squaredDistanceToEdge(centerX, centerY) < threshold)
-                        indices[count++] = i;
-                }
-                final int[] cell = new int[count];
-                System.arraycopy(indices, 0, cell, 0, count);
-                mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell;
-            }
-        }
-    }
-
-    /**
-     * Returns the indices of the keys that are closest to the given point.
-     * @param x the x-coordinate of the point
-     * @param y the y-coordinate of the point
-     * @return the array of integer indices for the nearest keys to the given point. If the given
-     * point is out of range, then an array of size zero is returned.
-     */
-    public int[] getNearestKeys(int x, int y) {
-        if (mGridNeighbors == null) computeNearestNeighbors();
-        if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {
-            int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);
-            if (index < GRID_SIZE) {
-                return mGridNeighbors[index];
-            }
-        }
-        return EMPTY_INT_ARRAY;
-    }
-
-    // TODO should be private
-    protected BaseKeyboard.Row createRowFromXml(Resources res, XmlResourceParser parser) {
-        return new BaseKeyboard.Row(res, this, parser);
-    }
-
-    // TODO should be private
-    protected BaseKeyboard.Key createKeyFromXml(Resources res, Row parent, int x, int y,
-            XmlResourceParser parser, KeyStyles keyStyles) {
-        return new BaseKeyboard.Key(res, parent, x, y, parser, keyStyles);
-    }
-
-    private void loadKeyboard(Context context, int xmlLayoutResId) {
-        try {
-            final Resources res = context.getResources();
-            BaseKeyboardParser parser = new BaseKeyboardParser(this, res);
-            parser.parseKeyboard(res.getXml(xmlLayoutResId));
-            // mTotalWidth is the width of this keyboard which is maximum width of row.
-            mTotalWidth = parser.getMaxRowWidth();
-            mTotalHeight = parser.getTotalHeight();
-        } catch (XmlPullParserException e) {
-            Log.w(TAG, "keyboard XML parse error: " + e);
-            throw new IllegalArgumentException(e);
-        } catch (IOException e) {
-            Log.w(TAG, "keyboard XML parse error: " + e);
-            throw new RuntimeException(e);
-        }
-    }
-
-    protected static void setDefaultBounds(Drawable drawable)  {
-        if (drawable != null)
-            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
-                    drawable.getIntrinsicHeight());
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
index fc517aeb78218d835288249e499f3b8364343ea1..ad3f1db9b98c346632d895450985c3604f5f25b9 100644
--- a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
+++ b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
@@ -57,6 +57,7 @@ public class InputLanguageSelection extends PreferenceActivity {
             return this.label;
         }
 
+        @Override
         public int compareTo(Object o) {
             return sCollator.compare(this.label, ((Loc) o).label);
         }
diff --git a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
index 00b6f0a43d1f7177d5eca4074603dc1fdb258597..e8487e798aeaa4b2a2f9fe339038a658963d78a8 100644
--- a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
@@ -16,16 +16,19 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.LatinKeyboard;
+import com.android.inputmethod.keyboard.LatinKeyboardView;
+
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.util.Log;
 import android.view.InflateException;
-import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 
 import java.lang.ref.SoftReference;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Locale;
 
@@ -34,14 +37,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
     private static final boolean DEBUG = false;
     public static final boolean DEBUG_STATE = false;
 
-    public static final int MODE_TEXT = 0;
-    public static final int MODE_URL = 1;
-    public static final int MODE_EMAIL = 2;
-    public static final int MODE_IM = 3;
-    public static final int MODE_WEB = 4;
-    public static final int MODE_PHONE = 5;
-    public static final int MODE_NUMBER = 6;
-
     // Changing DEFAULT_LAYOUT_ID also requires prefs_for_debug.xml to be matched with.
     public static final String DEFAULT_LAYOUT_ID = "5";
     public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902";
@@ -74,7 +69,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
     private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache =
             new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
 
-    private int mMode = MODE_TEXT; /* default value */
+    private int mMode = KeyboardId.MODE_TEXT; /* default value */
     private int mImeOptions;
     private boolean mIsSymbols;
     /** mIsAutoCompletionActive indicates that auto completed word will be input instead of
@@ -132,129 +127,13 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
         // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard"
         // respectively here for xlarge device's layout switching.
         mSymbolsId = new KeyboardId(locale, orientation, mode,
-                mode == MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols,
+                mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols,
                 colorScheme, hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true);
         mSymbolsShiftedId = new KeyboardId(locale, orientation, mode,
-                mode == MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift,
+                mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift,
                 colorScheme, hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true);
     }
 
-    /**
-     * Represents the parameters necessary to construct a new LatinKeyboard,
-     * which also serve as a unique identifier for each keyboard type.
-     */
-    public static class KeyboardId {
-        public final Locale mLocale;
-        public final int mOrientation;
-        public final int mMode;
-        public final int mXmlId;
-        public final int mColorScheme;
-        public final boolean mHasSettingsKey;
-        public final boolean mVoiceKeyEnabled;
-        public final boolean mHasVoiceKey;
-        public final int mImeOptions;
-        public final boolean mEnableShiftLock;
-
-        private final int mHashCode;
-
-        public KeyboardId(Locale locale, int orientation, int mode,
-                int xmlId, int colorScheme, boolean hasSettingsKey, boolean voiceKeyEnabled,
-                boolean hasVoiceKey, int imeOptions, boolean enableShiftLock) {
-            this.mLocale = locale;
-            this.mOrientation = orientation;
-            this.mMode = mode;
-            this.mXmlId = xmlId;
-            this.mColorScheme = colorScheme;
-            this.mHasSettingsKey = hasSettingsKey;
-            this.mVoiceKeyEnabled = voiceKeyEnabled;
-            this.mHasVoiceKey = hasVoiceKey;
-            // We are interested only in IME_MASK_ACTION enum value and IME_FLAG_NO_ENTER_ACTION.
-            this.mImeOptions = imeOptions
-                    & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION);
-            this.mEnableShiftLock = enableShiftLock;
-
-            this.mHashCode = Arrays.hashCode(new Object[] {
-                    locale,
-                    orientation,
-                    mode,
-                    xmlId,
-                    colorScheme,
-                    hasSettingsKey,
-                    voiceKeyEnabled,
-                    hasVoiceKey,
-                    imeOptions,
-                    enableShiftLock,
-            });
-        }
-
-        public int getXmlId() {
-            return mXmlId;
-        }
-
-        public boolean isAlphabetMode() {
-            return mXmlId == R.xml.kbd_qwerty;
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            return other instanceof KeyboardId && equals((KeyboardId) other);
-        }
-
-        private boolean equals(KeyboardId other) {
-            return other.mLocale.equals(this.mLocale)
-                && other.mOrientation == this.mOrientation
-                && other.mMode == this.mMode
-                && other.mXmlId == this.mXmlId
-                && other.mColorScheme == this.mColorScheme
-                && other.mHasSettingsKey == this.mHasSettingsKey
-                && other.mVoiceKeyEnabled == this.mVoiceKeyEnabled
-                && other.mHasVoiceKey == this.mHasVoiceKey
-                && other.mImeOptions == this.mImeOptions
-                && other.mEnableShiftLock == this.mEnableShiftLock;
-        }
-
-        @Override
-        public int hashCode() {
-            return mHashCode;
-        }
-
-        @Override
-        public String toString() {
-            return String.format("[%s %s %5s imeOptions=0x%08x xml=0x%08x %s%s%s%s%s]",
-                    mLocale,
-                    (mOrientation == 1 ? "port" : "land"),
-                    modeName(mMode),
-                    mImeOptions,
-                    mXmlId,
-                    colorSchemeName(mColorScheme),
-                    (mHasSettingsKey ? " hasSettingsKey" : ""),
-                    (mVoiceKeyEnabled ? " voiceKeyEnabled" : ""),
-                    (mHasVoiceKey ? " hasVoiceKey" : ""),
-                    (mEnableShiftLock ? " enableShiftLock" : ""));
-        }
-
-        private static String modeName(int mode) {
-            switch (mode) {
-            case MODE_TEXT: return "text";
-            case MODE_URL: return "url";
-            case MODE_EMAIL: return "email";
-            case MODE_IM: return "im";
-            case MODE_WEB: return "web";
-            case MODE_PHONE: return "phone";
-            case MODE_NUMBER: return "number";
-            }
-            return null;
-        }
-
-        private static String colorSchemeName(int colorScheme) {
-            switch (colorScheme) {
-            case BaseKeyboardView.COLOR_SCHEME_WHITE: return "white";
-            case BaseKeyboardView.COLOR_SCHEME_BLACK: return "black";
-            }
-            return null;
-        }
-    }
-
     private boolean hasVoiceKey(boolean isSymbols) {
         return mVoiceKeyEnabled && (isSymbols != mVoiceButtonOnPrimary);
     }
@@ -327,9 +206,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
         final boolean enableShiftLock;
 
         if (isSymbols) {
-            if (mode == MODE_PHONE) {
+            if (mode == KeyboardId.MODE_PHONE) {
                 xmlId = R.xml.kbd_phone_symbols;
-            } else if (mode == MODE_NUMBER) {
+            } else if (mode == KeyboardId.MODE_NUMBER) {
                 // Note: MODE_NUMBER keyboard layout has no "switch alpha symbol" key.
                 xmlId = R.xml.kbd_number;
             } else {
@@ -337,10 +216,10 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
             }
             enableShiftLock = false;
         } else {
-            if (mode == MODE_PHONE) {
+            if (mode == KeyboardId.MODE_PHONE) {
                 xmlId = R.xml.kbd_phone;
                 enableShiftLock = false;
-            } else if (mode == MODE_NUMBER) {
+            } else if (mode == KeyboardId.MODE_NUMBER) {
                 xmlId = R.xml.kbd_number;
                 enableShiftLock = false;
             } else {
@@ -731,7 +610,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
 
     private int getColorScheme() {
         return (mInputView != null)
-                ? mInputView.getColorScheme() : BaseKeyboardView.COLOR_SCHEME_WHITE;
+                ? mInputView.getColorScheme() : KeyboardView.COLOR_SCHEME_WHITE;
     }
 
     public void onAutoCompletionStateChanged(boolean isAutoCompletion) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 36c77efaf4abc3aff620b80d150d315ad992a21b..4f4112fa8fdde5dc6a79b8445db1fa5ab020262b 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -16,6 +16,12 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardActionListener;
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.LatinKeyboard;
+import com.android.inputmethod.keyboard.LatinKeyboardView;
 import com.android.inputmethod.latin.LatinIMEUtil.RingCharBuffer;
 import com.android.inputmethod.voice.VoiceIMEConnector;
 
@@ -73,7 +79,7 @@ import java.util.Locale;
  * Input method implementation for Qwerty'ish keyboard.
  */
 public class LatinIME extends InputMethodService
-        implements BaseKeyboardView.OnKeyboardActionListener,
+        implements KeyboardActionListener,
         SharedPreferences.OnSharedPreferenceChangeListener,
         Tutorial.TutorialListener {
     private static final String TAG = "LatinIME";
@@ -548,10 +554,10 @@ public class LatinIME extends InputMethodService
         switch (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) {
             case EditorInfo.TYPE_CLASS_NUMBER:
             case EditorInfo.TYPE_CLASS_DATETIME:
-                mode = KeyboardSwitcher.MODE_NUMBER;
+                mode = KeyboardId.MODE_NUMBER;
                 break;
             case EditorInfo.TYPE_CLASS_PHONE:
-                mode = KeyboardSwitcher.MODE_PHONE;
+                mode = KeyboardId.MODE_PHONE;
                 break;
             case EditorInfo.TYPE_CLASS_TEXT:
                 //startPrediction();
@@ -568,24 +574,24 @@ public class LatinIME extends InputMethodService
                 }
                 if (isEmailVariation(variation)) {
                     mPredictionOn = false;
-                    mode = KeyboardSwitcher.MODE_EMAIL;
+                    mode = KeyboardId.MODE_EMAIL;
                 } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_URI) {
                     mPredictionOn = false;
-                    mode = KeyboardSwitcher.MODE_URL;
+                    mode = KeyboardId.MODE_URL;
                 } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE) {
-                    mode = KeyboardSwitcher.MODE_IM;
+                    mode = KeyboardId.MODE_IM;
                 } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) {
                     mPredictionOn = false;
-                    mode = KeyboardSwitcher.MODE_TEXT;
+                    mode = KeyboardId.MODE_TEXT;
                 } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) {
-                    mode = KeyboardSwitcher.MODE_WEB;
+                    mode = KeyboardId.MODE_WEB;
                     // If it's a browser edit field and auto correct is not ON explicitly, then
                     // disable auto correction, but keep suggestions on.
                     if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) {
                         mInputTypeNoAutoCorrect = true;
                     }
                 } else {
-                    mode = KeyboardSwitcher.MODE_TEXT;
+                    mode = KeyboardId.MODE_TEXT;
                 }
 
                 // If NO_SUGGESTIONS is set, don't do prediction.
@@ -604,7 +610,7 @@ public class LatinIME extends InputMethodService
                 }
                 break;
             default:
-                mode = KeyboardSwitcher.MODE_TEXT;
+                mode = KeyboardId.MODE_TEXT;
                 break;
         }
         inputView.closing();
@@ -675,7 +681,7 @@ public class LatinIME extends InputMethodService
 
         mVoiceConnector.flushVoiceInputLogs(mConfigurationChanging);
 
-        BaseKeyboardView inputView = mKeyboardSwitcher.getInputView();
+        KeyboardView inputView = mKeyboardSwitcher.getInputView();
         if (inputView != null)
             inputView.closing();
         if (mAutoDictionary != null) mAutoDictionary.flushPendingWrites();
@@ -685,7 +691,7 @@ public class LatinIME extends InputMethodService
     @Override
     public void onFinishInputView(boolean finishingInput) {
         super.onFinishInputView(finishingInput);
-        BaseKeyboardView inputView = mKeyboardSwitcher.getInputView();
+        KeyboardView inputView = mKeyboardSwitcher.getInputView();
         if (inputView != null)
             inputView.setForeground(false);
         // Remove pending messages related to update suggestions
@@ -1073,51 +1079,52 @@ public class LatinIME extends InputMethodService
 
     // Implementation of KeyboardViewListener
 
+    @Override
     public void onKey(int primaryCode, int[] keyCodes, int x, int y) {
         long when = SystemClock.uptimeMillis();
-        if (primaryCode != BaseKeyboard.KEYCODE_DELETE || when > mLastKeyTime + QUICK_PRESS) {
+        if (primaryCode != Keyboard.KEYCODE_DELETE || when > mLastKeyTime + QUICK_PRESS) {
             mDeleteCount = 0;
         }
         mLastKeyTime = when;
         KeyboardSwitcher switcher = mKeyboardSwitcher;
         final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
         switch (primaryCode) {
-        case BaseKeyboard.KEYCODE_DELETE:
+        case Keyboard.KEYCODE_DELETE:
             handleBackspace();
             mDeleteCount++;
             LatinImeLogger.logOnDelete();
             break;
-        case BaseKeyboard.KEYCODE_SHIFT:
+        case Keyboard.KEYCODE_SHIFT:
             // Shift key is handled in onPress() when device has distinct multi-touch panel.
             if (!distinctMultiTouch)
                 switcher.toggleShift();
             break;
-        case BaseKeyboard.KEYCODE_MODE_CHANGE:
+        case Keyboard.KEYCODE_MODE_CHANGE:
             // Symbol key is handled in onPress() when device has distinct multi-touch panel.
             if (!distinctMultiTouch)
                 switcher.changeKeyboardMode();
             break;
-        case BaseKeyboard.KEYCODE_CANCEL:
+        case Keyboard.KEYCODE_CANCEL:
             if (!isShowingOptionDialog()) {
                 handleClose();
             }
             break;
-        case LatinKeyboardView.KEYCODE_OPTIONS:
+        case LatinKeyboard.KEYCODE_OPTIONS:
             onOptionKeyPressed();
             break;
-        case LatinKeyboardView.KEYCODE_OPTIONS_LONGPRESS:
+        case LatinKeyboard.KEYCODE_OPTIONS_LONGPRESS:
             onOptionKeyLongPressed();
             break;
-        case LatinKeyboardView.KEYCODE_NEXT_LANGUAGE:
+        case LatinKeyboard.KEYCODE_NEXT_LANGUAGE:
             toggleLanguage(false, true);
             break;
-        case LatinKeyboardView.KEYCODE_PREV_LANGUAGE:
+        case LatinKeyboard.KEYCODE_PREV_LANGUAGE:
             toggleLanguage(false, false);
             break;
-        case LatinKeyboardView.KEYCODE_CAPSLOCK:
+        case LatinKeyboard.KEYCODE_CAPSLOCK:
             switcher.toggleCapsLock();
             break;
-        case LatinKeyboardView.KEYCODE_VOICE: /* was a button press, was not a swipe */
+        case LatinKeyboard.KEYCODE_VOICE: /* was a button press, was not a swipe */
             mVoiceConnector.startListening(false,
                     mKeyboardSwitcher.getInputView().getWindowToken(), mConfigurationChanging);
             break;
@@ -1143,6 +1150,7 @@ public class LatinIME extends InputMethodService
         mEnteredText = null;
     }
 
+    @Override
     public void onText(CharSequence text) {
         mVoiceConnector.commitVoiceInput();
         InputConnection ic = getCurrentInputConnection();
@@ -1161,6 +1169,7 @@ public class LatinIME extends InputMethodService
         mEnteredText = text;
     }
 
+    @Override
     public void onCancel() {
         // User released a finger outside any key
     }
@@ -1420,24 +1429,26 @@ public class LatinIME extends InputMethodService
     }
 
     public void switchToKeyboardView() {
-      mHandler.post(new Runnable() {
-          public void run() {
-              if (DEBUG) {
-                  Log.d(TAG, "Switch to keyboard view.");
-              }
-              View v = mKeyboardSwitcher.getInputView();
-              if (v != null) {
-                  // Confirms that the keyboard view doesn't have parent view.
-                  ViewParent p = v.getParent();
-                  if (p != null && p instanceof ViewGroup) {
-                      ((ViewGroup)p).removeView(v);
-                  }
-                  setInputView(v);
-              }
-              setCandidatesViewShown(isCandidateStripVisible());
-              updateInputViewShown();
-              mHandler.postUpdateSuggestions();
-          }});
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (DEBUG) {
+                    Log.d(TAG, "Switch to keyboard view.");
+                }
+                View v = mKeyboardSwitcher.getInputView();
+                if (v != null) {
+                    // Confirms that the keyboard view doesn't have parent view.
+                    ViewParent p = v.getParent();
+                    if (p != null && p instanceof ViewGroup) {
+                        ((ViewGroup) p).removeView(v);
+                    }
+                    setInputView(v);
+                }
+                setCandidatesViewShown(isCandidateStripVisible());
+                updateInputViewShown();
+                mHandler.postUpdateSuggestions();
+            }
+        });
     }
 
     public void clearSuggestions() {
@@ -1586,8 +1597,8 @@ public class LatinIME extends InputMethodService
             LatinImeLogger.logOnManualSuggestion(
                     "", suggestion.toString(), index, suggestions);
             final char primaryCode = suggestion.charAt(0);
-            onKey(primaryCode, new int[]{primaryCode}, BaseKeyboardView.NOT_A_TOUCH_COORDINATE,
-                    BaseKeyboardView.NOT_A_TOUCH_COORDINATE);
+            onKey(primaryCode, new int[]{primaryCode}, KeyboardView.NOT_A_TOUCH_COORDINATE,
+                    KeyboardView.NOT_A_TOUCH_COORDINATE);
             if (ic != null) {
                 ic.endBatchEdit();
             }
@@ -1877,6 +1888,7 @@ public class LatinIME extends InputMethodService
         switcher.updateShiftState();
     }
 
+    @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
             String key) {
         mSubtypeSwitcher.onSharedPreferenceChanged(sharedPreferences, key);
@@ -1888,6 +1900,7 @@ public class LatinIME extends InputMethodService
         }
     }
 
+    @Override
     public void swipeRight() {
         if (LatinKeyboardView.DEBUG_AUTO_PLAY) {
             CharSequence text = ((android.text.ClipboardManager)getSystemService(
@@ -1898,38 +1911,43 @@ public class LatinIME extends InputMethodService
         }
     }
 
+    @Override
     public void swipeLeft() {
     }
 
+    @Override
     public void swipeDown() {
         handleClose();
     }
 
+    @Override
     public void swipeUp() {
     }
 
+    @Override
     public void onPress(int primaryCode) {
         vibrate();
         playKeyClick(primaryCode);
         KeyboardSwitcher switcher = mKeyboardSwitcher;
         final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
-        if (distinctMultiTouch && primaryCode == BaseKeyboard.KEYCODE_SHIFT) {
+        if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_SHIFT) {
             switcher.onPressShift();
-        } else if (distinctMultiTouch && primaryCode == BaseKeyboard.KEYCODE_MODE_CHANGE) {
+        } else if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_MODE_CHANGE) {
             switcher.onPressSymbol();
         } else {
             switcher.onOtherKeyPressed();
         }
     }
 
+    @Override
     public void onRelease(int primaryCode) {
         KeyboardSwitcher switcher = mKeyboardSwitcher;
         // Reset any drag flags in the keyboard
         switcher.keyReleased();
         final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
-        if (distinctMultiTouch && primaryCode == BaseKeyboard.KEYCODE_SHIFT) {
+        if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_SHIFT) {
             switcher.onReleaseShift();
-        } else if (distinctMultiTouch && primaryCode == BaseKeyboard.KEYCODE_MODE_CHANGE) {
+        } else if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_MODE_CHANGE) {
             switcher.onReleaseSymbol();
         }
     }
@@ -1966,7 +1984,7 @@ public class LatinIME extends InputMethodService
             // FIXME: These should be triggered after auto-repeat logic
             int sound = AudioManager.FX_KEYPRESS_STANDARD;
             switch (primaryCode) {
-                case BaseKeyboard.KEYCODE_DELETE:
+                case Keyboard.KEYCODE_DELETE:
                     sound = AudioManager.FX_KEYPRESS_DELETE;
                     break;
                 case KEYCODE_ENTER:
@@ -2006,6 +2024,7 @@ public class LatinIME extends InputMethodService
     }
 
     // Tutorial.TutorialListener
+    @Override
     public void onTutorialDone() {
         sendDownUpKeyEvents(-1); // Inform the setupwizard that tutorial is in last bubble
         mTutorial = null;
@@ -2171,6 +2190,7 @@ public class LatinIME extends InputMethodService
                 itemInputMethod, itemSettings},
                 new DialogInterface.OnClickListener() {
 
+            @Override
             public void onClick(DialogInterface di, int position) {
                 di.dismiss();
                 switch (position) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIMEDebugSettings.java b/java/src/com/android/inputmethod/latin/LatinIMEDebugSettings.java
index cba1a0af9265d66ebc896f13ee0f2ffc8c75e4e1..68738eca13257a2c7478a39542e9a59b6ef2bf7b 100644
--- a/java/src/com/android/inputmethod/latin/LatinIMEDebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/LatinIMEDebugSettings.java
@@ -43,6 +43,7 @@ public class LatinIMEDebugSettings extends PreferenceActivity
         updateDebugMode();
     }
 
+    @Override
     public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
         if (key.equals(DEBUG_MODE_KEY)) {
             if (mDebugMode != null) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIMESettings.java b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
index ae6646113bb73a74d20bf3b8cb702517b1f8f5ed..187b6d39409518b1eb8885cab25f91d996244120 100644
--- a/java/src/com/android/inputmethod/latin/LatinIMESettings.java
+++ b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
@@ -137,6 +137,7 @@ public class LatinIMESettings extends PreferenceActivity
         super.onDestroy();
     }
 
+    @Override
     public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
         (new BackupManager(this)).dataChanged();
         // If turning on voice input, show dialog
@@ -181,6 +182,7 @@ public class LatinIMESettings extends PreferenceActivity
         switch (id) {
             case VOICE_INPUT_CONFIRM_DIALOG:
                 DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+                    @Override
                     public void onClick(DialogInterface dialog, int whichButton) {
                         if (whichButton == DialogInterface.BUTTON_NEGATIVE) {
                             mVoicePreference.setValue(mVoiceModeOff);
@@ -226,6 +228,7 @@ public class LatinIMESettings extends PreferenceActivity
         }
     }
 
+    @Override
     public void onDismiss(DialogInterface dialog) {
         mLogger.settingsWarningDialogDismissed();
         if (!mOkClicked) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIMEUtil.java b/java/src/com/android/inputmethod/latin/LatinIMEUtil.java
index a58f630cd3d462bef7e2c041b94941e0dcc98b12..f508b9ab830a72eaea5b1ca10daab1ec7c412ec6 100644
--- a/java/src/com/android/inputmethod/latin/LatinIMEUtil.java
+++ b/java/src/com/android/inputmethod/latin/LatinIMEUtil.java
@@ -310,6 +310,7 @@ public class LatinIMEUtil {
 
         public void write(final String log) {
             mLoggingHandler.post(new Runnable() {
+                @Override
                 public void run() {
                     createLogFileIfNotExist();
                     final long currentTime = System.currentTimeMillis();
@@ -327,6 +328,7 @@ public class LatinIMEUtil {
 
         public void printAll() {
             mLoggingHandler.post(new Runnable() {
+                @Override
                 public void run() {
                     mWriter.flush();
                     StringBuilder sb = new StringBuilder();
@@ -355,6 +357,7 @@ public class LatinIMEUtil {
 
         public void clearAll() {
             mLoggingHandler.post(new Runnable() {
+                @Override
                 public void run() {
                     if (mFile != null && mFile.exists()) {
                         if (LatinImeLogger.sDBG) {
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
index b64e3ddb2490186240b737a87e530c771053355e..de194d21bcaa4f32424ad67c59dd52886542184a 100644
--- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java
+++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.latin.Dictionary.DataType;
 
 import android.content.Context;
@@ -27,6 +28,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang
 
     public static boolean sDBG = false;
 
+    @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
     }
 
@@ -67,7 +69,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang
     public static void onAddSuggestedWord(String word, int typeId, DataType dataType) {
     }
 
-    public static void onSetKeyboard(BaseKeyboard kb) {
+    public static void onSetKeyboard(Keyboard kb) {
     }
 
     public static void onPrintAllUsabilityStudtyLogs() {
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 103443e6dbcda34f5a92b75ccbf37678c5c7d6be..a9891768993ff54ab7272a83093272d041516fbe 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.keyboard.LatinKeyboard;
 import com.android.inputmethod.voice.SettingsUtil;
 import com.android.inputmethod.voice.VoiceIMEConnector;
 import com.android.inputmethod.voice.VoiceInput;
@@ -197,7 +198,7 @@ public class SubtypeSwitcher {
                     || VoiceIMEConnector.getInstance().needsToShowWarningDialog()) {
                 if (mVoiceInput != null) {
                     // TODO: Call proper function to trigger VoiceIME
-                    mService.onKey(LatinKeyboardView.KEYCODE_VOICE, null, 0, 0);
+                    mService.onKey(LatinKeyboard.KEYCODE_VOICE, null, 0, 0);
                 }
             }
         } else {
@@ -350,7 +351,7 @@ public class SubtypeSwitcher {
                 if (DBG) {
                     Log.d(TAG, "Set and call voice input.");
                 }
-                mService.onKey(LatinKeyboardView.KEYCODE_VOICE, null, 0, 0);
+                mService.onKey(LatinKeyboard.KEYCODE_VOICE, null, 0, 0);
                 return true;
             }
         }
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index ca2ffe13a54c207fea3b2f9605ba615540acc637..4c9b7509a48b7c9a8cf004311215092a6fd20602 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -423,6 +423,7 @@ public class Suggest implements Dictionary.WordCallback {
         return false;
     }
 
+    @Override
     public boolean addWord(final char[] word, final int offset, final int length, int freq,
             final int dicTypeId, final Dictionary.DataType dataType) {
         Dictionary.DataType dataTypeForLog = dataType;
diff --git a/java/src/com/android/inputmethod/latin/TextEntryState.java b/java/src/com/android/inputmethod/latin/TextEntryState.java
index 56347af5db24ef83a37a90eb3719ac2f8687e42a..31a192bfe3e315bb07237f0a99720e64d4341939 100644
--- a/java/src/com/android/inputmethod/latin/TextEntryState.java
+++ b/java/src/com/android/inputmethod/latin/TextEntryState.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.latin;
 
-import com.android.inputmethod.latin.BaseKeyboard.Key;
+import com.android.inputmethod.keyboard.Key;
 
 import android.content.Context;
 import android.text.format.DateFormat;
diff --git a/java/src/com/android/inputmethod/latin/Tutorial.java b/java/src/com/android/inputmethod/latin/Tutorial.java
index cd7636f3c3da9c2cc61f9cd50201c4a9447ff3e3..bd069bd0d8c545ccb596ddd8bd9066caee3937e8 100644
--- a/java/src/com/android/inputmethod/latin/Tutorial.java
+++ b/java/src/com/android/inputmethod/latin/Tutorial.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.keyboard.LatinKeyboardView;
+
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
@@ -133,6 +135,7 @@ public class Tutorial implements OnTouchListener {
                     if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) offy -= window.getHeight();
                     if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) offx -= window.getWidth();
                     textView.setOnTouchListener(new View.OnTouchListener() {
+                        @Override
                         public boolean onTouch(View view, MotionEvent me) {
                             Tutorial.this.next();
                             return true;
@@ -237,6 +240,7 @@ public class Tutorial implements OnTouchListener {
         return true;
     }
 
+    @Override
     public boolean onTouch(View v, MotionEvent event) {
         if (event.getAction() == MotionEvent.ACTION_DOWN) {
             next();
diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java
index 49b95e9aa14279c8e869871f84d4b3cab1867c6d..a522303e6842c9c4d0e4ab7de81afb5f0137f7f0 100644
--- a/java/src/com/android/inputmethod/latin/UserDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserDictionary.java
@@ -97,6 +97,7 @@ public class UserDictionary extends ExpandableDictionary {
 
         final ContentResolver contentResolver = getContext().getContentResolver();
         new Thread("addWord") {
+            @Override
             public void run() {
                 contentResolver.insert(Words.CONTENT_URI, values);
             }
diff --git a/java/src/com/android/inputmethod/voice/FieldContext.java b/java/src/com/android/inputmethod/voice/FieldContext.java
index 5fbacfb6c3d16e0fd84d502e6982882b75cce97c..dfdfbaa9f7896ec8e2853b7fcd379085b24f229c 100644
--- a/java/src/com/android/inputmethod/voice/FieldContext.java
+++ b/java/src/com/android/inputmethod/voice/FieldContext.java
@@ -73,6 +73,7 @@ public class FieldContext {
         bundle.putInt(IME_OPTIONS, info.imeOptions);
     }
 
+    @SuppressWarnings("static-access")
     private static void addInputConnectionToBundle(
         InputConnection conn, Bundle bundle) {
         if (conn == null) {
@@ -96,6 +97,7 @@ public class FieldContext {
         return mFieldInfo;
     }
 
+    @Override
     public String toString() {
         return mFieldInfo.toString();
     }
diff --git a/java/src/com/android/inputmethod/voice/RecognitionView.java b/java/src/com/android/inputmethod/voice/RecognitionView.java
index 1d12977134140b034dcdac5c0004a95b8bc0b1ba..12d0de8529ce2e7bbc5360c9c7daf3e2a082e158 100644
--- a/java/src/com/android/inputmethod/voice/RecognitionView.java
+++ b/java/src/com/android/inputmethod/voice/RecognitionView.java
@@ -51,6 +51,7 @@ import java.util.List;
  * plays beeps, shows errors, etc.
  */
 public class RecognitionView {
+    @SuppressWarnings("unused")
     private static final String TAG = "RecognitionView";
 
     private Handler mUiHandler;  // Reference to UI thread
@@ -78,6 +79,7 @@ public class RecognitionView {
 
     /** Updates the microphone icon to show user their volume.*/
     private Runnable mUpdateVolumeRunnable = new Runnable() {
+        @Override
         public void run() {
             if (mState != State.LISTENING) {
                 return;
@@ -141,6 +143,7 @@ public class RecognitionView {
 
     public void restoreState() {
         mUiHandler.post(new Runnable() {
+            @Override
             public void run() {
                 // Restart the spinner
                 if (mState == State.WORKING) {
@@ -153,6 +156,7 @@ public class RecognitionView {
 
     public void showInitializing() {
         mUiHandler.post(new Runnable() {
+            @Override
             public void run() {
                 prepareDialog(false, mContext.getText(R.string.voice_initializing), mInitializing,
                         mContext.getText(R.string.cancel)); 
@@ -162,6 +166,7 @@ public class RecognitionView {
 
     public void showListening() {
         mUiHandler.post(new Runnable() {
+            @Override
             public void run() {
                 mState = State.LISTENING;
                 prepareDialog(false, mContext.getText(R.string.voice_listening), mSpeakNow.get(0),
@@ -177,6 +182,7 @@ public class RecognitionView {
 
     public void showError(final String message) {
         mUiHandler.post(new Runnable() {
+            @Override
             public void run() {
                 mState = State.READY;
                 prepareDialog(false, message, mError, mContext.getText(R.string.ok));
@@ -190,6 +196,7 @@ public class RecognitionView {
         final int speechEndPosition) {
 
         mUiHandler.post(new Runnable() {
+            @Override
             public void run() {
                 mState = State.WORKING;
                 prepareDialog(true, mContext.getText(R.string.voice_working), null, mContext
@@ -309,6 +316,7 @@ public class RecognitionView {
 
     public void finish() {
         mUiHandler.post(new Runnable() {
+            @Override
             public void run() {
                 mState = State.READY;
                 exitWorking();
diff --git a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
index 5574a21de511c6e98080c13ac0f5a288d45c945d..73b3b19e3e4f2d6f43a6ae0d5213522ea8f4e2cc 100644
--- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
+++ b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
@@ -39,7 +39,6 @@ import android.text.Selection;
 import android.text.Spannable;
 import android.text.TextUtils;
 import android.text.method.LinkMovementMethod;
-import android.text.method.MovementMethod;
 import android.text.style.ClickableSpan;
 import android.text.style.URLSpan;
 import android.view.LayoutInflater;
@@ -120,6 +119,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
         if (VOICE_INSTALLED) {
             mVoiceInput = new VoiceInput(context, this);
             mHints = new Hints(context, prefs, new Hints.Display() {
+                @Override
                 public void showHint(int viewResource) {
                     LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                             Context.LAYOUT_INFLATER_SERVICE);
@@ -519,6 +519,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
     public void switchToRecognitionStatusView(final boolean configurationChanging) {
         final boolean configChanged = configurationChanging;
         mHandler.post(new Runnable() {
+            @Override
             public void run() {
                 mContext.setCandidatesViewShown(false);
                 mRecognizing = true;
diff --git a/java/src/com/android/inputmethod/voice/VoiceInput.java b/java/src/com/android/inputmethod/voice/VoiceInput.java
index 6d45ef97cc6e2484e34626b4d6feeb11b88837e5..d51d8694dae00ae13a363ad7da00cb4ec6571dd5 100644
--- a/java/src/com/android/inputmethod/voice/VoiceInput.java
+++ b/java/src/com/android/inputmethod/voice/VoiceInput.java
@@ -86,6 +86,7 @@ public class VoiceInput implements OnClickListener {
     private static final String ALTERNATES_BUNDLE = "alternates_bundle";
 
     //  This is copied from the VoiceSearch app.
+    @SuppressWarnings("unused")
     private static final class AlternatesBundleKeys {
         public static final String ALTERNATES = "alternates";
         public static final String CONFIDENCE = "confidence";
@@ -405,6 +406,7 @@ public class VoiceInput implements OnClickListener {
     /**
      * Handle the cancel button.
      */
+    @Override
     public void onClick(View view) {
         switch(view.getId()) {
             case R.id.button:
@@ -556,36 +558,43 @@ public class VoiceInput implements OnClickListener {
         int mSpeechStart;
         private boolean mEndpointed = false;
 
+        @Override
         public void onReadyForSpeech(Bundle noiseParams) {
             mRecognitionView.showListening();
         }
 
+        @Override
         public void onBeginningOfSpeech() {
             mEndpointed = false;
             mSpeechStart = mWaveBuffer.size();
         }
 
+        @Override
         public void onRmsChanged(float rmsdB) {
             mRecognitionView.updateVoiceMeter(rmsdB);
         }
 
+        @Override
         public void onBufferReceived(byte[] buf) {
             try {
                 mWaveBuffer.write(buf);
             } catch (IOException e) {}
         }
 
+        @Override
         public void onEndOfSpeech() {
             mEndpointed = true;
             mState = WORKING;
             mRecognitionView.showWorking(mWaveBuffer, mSpeechStart, mWaveBuffer.size());
         }
 
+        @Override
         public void onError(int errorType) {
             mState = ERROR;
             VoiceInput.this.onError(errorType, mEndpointed);
         }
 
+        @Override
         public void onResults(Bundle resultsBundle) {
             List<String> results = resultsBundle
                     .getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
@@ -638,10 +647,12 @@ public class VoiceInput implements OnClickListener {
             mRecognitionView.finish();
         }
 
+        @Override
         public void onPartialResults(final Bundle partialResults) {
             // currently - do nothing
         }
 
+        @Override
         public void onEvent(int eventType, Bundle params) {
             // do nothing - reserved for events that might be added in the future
         }
diff --git a/java/src/com/android/inputmethod/voice/VoiceInputLogger.java b/java/src/com/android/inputmethod/voice/VoiceInputLogger.java
index ec0ae649a8427d3317259a13dffee253fb47dec1..3e65434a23c82aaf4016ed51b317c85314635995 100644
--- a/java/src/com/android/inputmethod/voice/VoiceInputLogger.java
+++ b/java/src/com/android/inputmethod/voice/VoiceInputLogger.java
@@ -31,6 +31,7 @@ import android.content.Intent;
  * on on the VoiceSearch side.
  */
 public class VoiceInputLogger {
+    @SuppressWarnings("unused")
     private static final String TAG = VoiceInputLogger.class.getSimpleName();
 
     private static VoiceInputLogger sVoiceInputLogger;
diff --git a/tests/src/com/android/inputmethod/latin/EventRingBufferTests.java b/tests/src/com/android/inputmethod/latin/EventRingBufferTests.java
index 620f036db3bb16575861bfe5491cb03e297b70ec..869781f3d167aca134e333141779306d3a55c2a3 100644
--- a/tests/src/com/android/inputmethod/latin/EventRingBufferTests.java
+++ b/tests/src/com/android/inputmethod/latin/EventRingBufferTests.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.latin;
 
-import com.android.inputmethod.latin.SwipeTracker.EventRingBuffer;
+import com.android.inputmethod.keyboard.SwipeTracker.EventRingBuffer;
 
 import android.test.AndroidTestCase;