diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index b659763ea5092b8407690cb1e96e007c358b5eed..c8f64351c051a7412d83c84b60c456451c0c8327 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -49,6 +49,8 @@
         <attr name="keyPopupHintLetterPadding" format="dimension" />
         <!-- Right padding of shifted letter hint to the edge of the key.-->
         <attr name="keyShiftedLetterHintPadding" format="dimension" />
+        <!-- Blur radius of key text shadow. -->
+        <attr name="keyTextShadowRadius" format="float" />
 
         <!-- Layout resource for key press feedback.-->
         <attr name="keyPreviewLayout" format="reference" />
@@ -320,7 +322,6 @@
         <!-- Color to use for the label in a key. -->
         <attr name="keyTextColor" format="color" />
         <attr name="keyTextShadowColor" format="color" />
-        <attr name="keyTextShadowRadius" format="float" />
         <!-- Color to use for the label in a key when in inactivated state. -->
         <attr name="keyTextInactivatedColor" format="color" />
         <!-- Key hint letter (= one character hint label) color -->
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index e941cc7fcd10a6db09c57f38848769c07dc8202b..03c216474059bee63d4c162ab89d9703d8db0ab2 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -31,8 +31,10 @@ import android.text.TextUtils;
 import android.util.Log;
 import android.util.Xml;
 
+import com.android.inputmethod.keyboard.internal.KeyDrawParams;
 import com.android.inputmethod.keyboard.internal.KeySpecParser;
 import com.android.inputmethod.keyboard.internal.KeyStyle;
+import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
 import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.keyboard.internal.KeyboardRow;
@@ -132,6 +134,8 @@ public class Key {
     private static final int ACTION_FLAGS_ALT_CODE_WHILE_TYPING = 0x04;
     private static final int ACTION_FLAGS_ENABLE_LONG_PRESS = 0x08;
 
+    public final KeyVisualAttributes mKeyVisualAttributes;
+
     private final OptionalAttributes mOptionalAttributes;
 
     private static class OptionalAttributes {
@@ -202,6 +206,7 @@ public class Key {
         mX = x + params.mHorizontalGap / 2;
         mY = y;
         mHitBox.set(x, y, x + width + 1, y + height);
+        mKeyVisualAttributes = null;
 
         mHashCode = computeHashCode(this);
     }
@@ -357,11 +362,9 @@ public class Key {
                     disabledIconId, previewIconId,
                     visualInsetsLeft, visualInsetsRight);
         }
-
-        mHashCode = computeHashCode(this);
-
+        mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
         keyAttr.recycle();
-
+        mHashCode = computeHashCode(this);
         if (hasShiftedLetterHint() && TextUtils.isEmpty(mHintLabel)) {
             Log.w(TAG, "hasShiftedLetterHint specified without keyHintLabel: " + this);
         }
@@ -478,133 +481,161 @@ public class Key {
         return this instanceof Spacer;
     }
 
-    public boolean isShift() {
+    public final boolean isShift() {
         return mCode == CODE_SHIFT;
     }
 
-    public boolean isModifier() {
+    public final boolean isModifier() {
         return mCode == CODE_SHIFT || mCode == CODE_SWITCH_ALPHA_SYMBOL;
     }
 
-    public boolean isRepeatable() {
+    public final boolean isRepeatable() {
         return (mActionFlags & ACTION_FLAGS_IS_REPEATABLE) != 0;
     }
 
-    public boolean noKeyPreview() {
+    public final boolean noKeyPreview() {
         return (mActionFlags & ACTION_FLAGS_NO_KEY_PREVIEW) != 0;
     }
 
-    public boolean altCodeWhileTyping() {
+    public final boolean altCodeWhileTyping() {
         return (mActionFlags & ACTION_FLAGS_ALT_CODE_WHILE_TYPING) != 0;
     }
 
-    public boolean isLongPressEnabled() {
+    public final boolean isLongPressEnabled() {
         // We need not start long press timer on the key which has activated shifted letter.
         return (mActionFlags & ACTION_FLAGS_ENABLE_LONG_PRESS) != 0
                 && (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) == 0;
     }
 
-    public Typeface selectTypeface(final Typeface defaultTypeface) {
+    public final Typeface selectTypeface(final KeyDrawParams params) {
         // TODO: Handle "bold" here too?
         if ((mLabelFlags & LABEL_FLAGS_FONT_NORMAL) != 0) {
             return Typeface.DEFAULT;
         } else if ((mLabelFlags & LABEL_FLAGS_FONT_MONO_SPACE) != 0) {
             return Typeface.MONOSPACE;
         } else {
-            return defaultTypeface;
+            return params.mTypeface;
         }
     }
 
-    public int selectTextSize(final int letterSize, final int largeLetterSize, final int labelSize,
-            final int largeLabelSize, final int hintLabelSize) {
+    public final int selectTextSize(final KeyDrawParams params) {
         switch (mLabelFlags & LABEL_FLAGS_FOLLOW_KEY_TEXT_RATIO_MASK) {
         case LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO:
-            return letterSize;
+            return params.mLetterSize;
         case LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO:
-            return largeLetterSize;
+            return params.mLargeLetterSize;
         case LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO:
-            return labelSize;
+            return params.mLabelSize;
         case LABEL_FLAGS_FOLLOW_KEY_LARGE_LABEL_RATIO:
-            return largeLabelSize;
+            return params.mLargeLabelSize;
         case LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO:
-            return hintLabelSize;
+            return params.mHintLabelSize;
         default: // No follow key ratio flag specified.
-            return StringUtils.codePointCount(mLabel) == 1 ? letterSize : labelSize;
+            return StringUtils.codePointCount(mLabel) == 1 ? params.mLetterSize : params.mLabelSize;
+        }
+    }
+
+    public final int selectTextColor(final KeyDrawParams params) {
+        return isShiftedLetterActivated() ? params.mTextInactivatedColor : params.mTextColor;
+    }
+
+    public final int selectHintTextSize(final KeyDrawParams params) {
+        if (hasHintLabel()) {
+            return params.mHintLabelSize;
+        } else if (hasShiftedLetterHint()) {
+            return params.mShiftedLetterHintSize;
+        } else {
+            return params.mHintLetterSize;
         }
     }
 
-    public boolean isAlignLeft() {
+    public final int selectHintTextColor(final KeyDrawParams params) {
+        if (hasHintLabel()) {
+            return params.mHintLabelColor;
+        } else if (hasShiftedLetterHint()) {
+            return isShiftedLetterActivated() ? params.mShiftedLetterHintActivatedColor
+                    : params.mShiftedLetterHintInactivatedColor;
+        } else {
+            return params.mHintLetterColor;
+        }
+    }
+
+    public final int selectMoreKeyTextSize(final KeyDrawParams params) {
+        return hasLabelsInMoreKeys() ? params.mLabelSize : params.mLetterSize;
+    }
+
+    public final boolean isAlignLeft() {
         return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT) != 0;
     }
 
-    public boolean isAlignRight() {
+    public final boolean isAlignRight() {
         return (mLabelFlags & LABEL_FLAGS_ALIGN_RIGHT) != 0;
     }
 
-    public boolean isAlignLeftOfCenter() {
+    public final boolean isAlignLeftOfCenter() {
         return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT_OF_CENTER) != 0;
     }
 
-    public boolean hasPopupHint() {
+    public final boolean hasPopupHint() {
         return (mLabelFlags & LABEL_FLAGS_HAS_POPUP_HINT) != 0;
     }
 
-    public boolean hasShiftedLetterHint() {
+    public final boolean hasShiftedLetterHint() {
         return (mLabelFlags & LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT) != 0;
     }
 
-    public boolean hasHintLabel() {
+    public final boolean hasHintLabel() {
         return (mLabelFlags & LABEL_FLAGS_HAS_HINT_LABEL) != 0;
     }
 
-    public boolean hasLabelWithIconLeft() {
+    public final boolean hasLabelWithIconLeft() {
         return (mLabelFlags & LABEL_FLAGS_WITH_ICON_LEFT) != 0;
     }
 
-    public boolean hasLabelWithIconRight() {
+    public final boolean hasLabelWithIconRight() {
         return (mLabelFlags & LABEL_FLAGS_WITH_ICON_RIGHT) != 0;
     }
 
-    public boolean needsXScale() {
+    public final boolean needsXScale() {
         return (mLabelFlags & LABEL_FLAGS_AUTO_X_SCALE) != 0;
     }
 
-    public boolean isShiftedLetterActivated() {
+    public final boolean isShiftedLetterActivated() {
         return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0;
     }
 
-    public int getMoreKeysColumn() {
+    public final int getMoreKeysColumn() {
         return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_MASK;
     }
 
-    public boolean isFixedColumnOrderMoreKeys() {
+    public final boolean isFixedColumnOrderMoreKeys() {
         return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER) != 0;
     }
 
-    public boolean hasLabelsInMoreKeys() {
+    public final boolean hasLabelsInMoreKeys() {
         return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_HAS_LABELS) != 0;
     }
 
-    public int getMoreKeyLabelFlags() {
+    public final int getMoreKeyLabelFlags() {
         return hasLabelsInMoreKeys()
                 ? LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO
                 : LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO;
     }
 
-    public boolean needsDividersInMoreKeys() {
+    public final boolean needsDividersInMoreKeys() {
         return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_NEEDS_DIVIDERS) != 0;
     }
 
-    public boolean hasEmbeddedMoreKey() {
+    public final boolean hasEmbeddedMoreKey() {
         return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_EMBEDDED_MORE_KEY) != 0;
     }
 
-    public String getOutputText() {
+    public final String getOutputText() {
         final OptionalAttributes attrs = mOptionalAttributes;
         return (attrs != null) ? attrs.mOutputText : null;
     }
 
-    public int getAltCode() {
+    public final int getAltCode() {
         final OptionalAttributes attrs = mOptionalAttributes;
         return (attrs != null) ? attrs.mAltCode : CODE_UNSPECIFIED;
     }
@@ -627,12 +658,12 @@ public class Key {
                 ? iconSet.getIconDrawable(previewIconId) : iconSet.getIconDrawable(mIconId);
     }
 
-    public int getDrawX() {
+    public final int getDrawX() {
         final OptionalAttributes attrs = mOptionalAttributes;
         return (attrs == null) ? mX : mX + attrs.mVisualInsetsLeft;
     }
 
-    public int getDrawWidth() {
+    public final int getDrawWidth() {
         final OptionalAttributes attrs = mOptionalAttributes;
         return (attrs == null) ? mWidth
                 : mWidth - attrs.mVisualInsetsLeft - attrs.mVisualInsetsRight;
@@ -656,7 +687,7 @@ public class Key {
         mPressed = false;
     }
 
-    public boolean isEnabled() {
+    public final boolean isEnabled() {
         return mEnabled;
     }
 
@@ -748,7 +779,7 @@ public class Key {
      * @return the drawable state of the key.
      * @see android.graphics.drawable.StateListDrawable#setState(int[])
      */
-    public int[] getCurrentDrawableState() {
+    public final int[] getCurrentDrawableState() {
         switch (mBackgroundType) {
         case BACKGROUND_TYPE_FUNCTIONAL:
             return mPressed ? KEY_STATE_FUNCTIONAL_PRESSED : KEY_STATE_FUNCTIONAL_NORMAL;
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index c1b007d2e015fb41a6dd177c6a734b95d86b62a8..261d1eba75a90cfa6786fd6112273288fea6ee54 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -16,10 +16,10 @@
 
 package com.android.inputmethod.keyboard;
 
-import android.graphics.Typeface;
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
 import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.latin.CollectionUtils;
@@ -97,11 +97,7 @@ public class Keyboard {
     public final int mVerticalGap;
 
     /** Per keyboard key visual parameters */
-    public final Typeface mKeyTypeface;
-    public final float mKeyLetterRatio;
-    public final int mKeyLetterSize;
-    public final float mKeyHintLetterRatio;
-    public final float mKeyShiftedLetterHintRatio;
+    public final KeyVisualAttributes mKeyVisualAttributes;
 
     public final int mMostCommonKeyHeight;
     public final int mMostCommonKeyWidth;
@@ -132,13 +128,7 @@ public class Keyboard {
         mMostCommonKeyWidth = params.mMostCommonKeyWidth;
         mMoreKeysTemplate = params.mMoreKeysTemplate;
         mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn;
-
-        mKeyTypeface = params.mKeyTypeface;
-        mKeyLetterRatio = params.mKeyLetterRatio;
-        mKeyLetterSize = params.mKeyLetterSize;
-        mKeyHintLetterRatio = params.mKeyHintLetterRatio;
-        mKeyShiftedLetterHintRatio = params.mKeyShiftedLetterHintRatio;
-
+        mKeyVisualAttributes = params.mKeyVisualAttributes;
         mTopPadding = params.mTopPadding;
         mVerticalGap = params.mVerticalGap;
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 4e47eab26130acf902fd2635cdc622cd6baa7466..0b4d706321f26145500a92d71af45fa19b6aa747 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -40,6 +40,7 @@ import android.widget.TextView;
 
 import com.android.inputmethod.keyboard.internal.KeyDrawParams;
 import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
+import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
 import com.android.inputmethod.keyboard.internal.PreviewPlacerView;
 import com.android.inputmethod.latin.CollectionUtils;
 import com.android.inputmethod.latin.Constants;
@@ -68,6 +69,7 @@ import java.util.HashSet;
  * @attr ref R.styleable#KeyboardView_keyHintLetterPadding
  * @attr ref R.styleable#KeyboardView_keyPopupHintLetterPadding
  * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintPadding
+ * @attr ref R.styleable#KeyboardView_keyTextShadowRadius
  * @attr ref R.styleable#KeyboardView_backgroundDimAlpha
  * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewTextSize
  * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewTextColor
@@ -97,7 +99,6 @@ import java.util.HashSet;
  * @attr ref R.styleable#Keyboard_Key_keyTextColor
  * @attr ref R.styleable#Keyboard_Key_keyTextColorDisabled
  * @attr ref R.styleable#Keyboard_Key_keyTextShadowColor
- * @attr ref R.styleable#Keyboard_Key_keyTextShadowRadius
  * @attr ref R.styleable#Keyboard_Key_keyHintLetterColor
  * @attr ref R.styleable#Keyboard_Key_keyHintLabelColor
  * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintInactivatedColor
@@ -111,12 +112,16 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
     private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
 
     // XML attributes
+    private final KeyVisualAttributes mKeyVisualAttributes;
     private final int mKeyLabelHorizontalPadding;
     private final float mKeyHintLetterPadding;
     private final float mKeyPopupHintLetterPadding;
     private final float mKeyShiftedLetterHintPadding;
+    private final float mKeyTextShadowRadius;
     protected final float mVerticalCorrection;
     protected final int mMoreKeysLayout;
+    protected final Drawable mKeyBackground;
+    protected final Rect mKeyBackgroundPadding = new Rect();
     private final int mBackgroundDimAlpha;
 
     // HORIZONTAL ELLIPSIS "...", character for popup hint.
@@ -132,12 +137,19 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
 
     // Main keyboard
     private Keyboard mKeyboard;
-    protected final KeyDrawParams mKeyDrawParams;
+    protected final KeyDrawParams mKeyDrawParams = new KeyDrawParams();
 
     // Key preview
+    private static final int PREVIEW_ALPHA = 240;
     private final int mKeyPreviewLayoutId;
+    private final Drawable mPreviewBackground;
+    private final Drawable mPreviewLeftBackground;
+    private final Drawable mPreviewRightBackground;
+    private final int mPreviewOffset;
+    private final int mPreviewHeight;
+    private final int mPreviewLingerTimeout;
     private final SparseArray<TextView> mKeyPreviewTexts = CollectionUtils.newSparseArray();
-    protected final KeyPreviewDrawParams mKeyPreviewDrawParams;
+    protected final KeyPreviewDrawParams mKeyPreviewDrawParams = new KeyPreviewDrawParams();
     private boolean mShowKeyPreviewPopup = true;
     private int mDelayAfterPreview;
     private final PreviewPlacerView mPreviewPlacerView;
@@ -171,12 +183,12 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
     public static class DrawingHandler extends StaticInnerHandlerWrapper<KeyboardView> {
         private static final int MSG_DISMISS_KEY_PREVIEW = 0;
 
-        public DrawingHandler(KeyboardView outerInstance) {
+        public DrawingHandler(final KeyboardView outerInstance) {
             super(outerInstance);
         }
 
         @Override
-        public void handleMessage(Message msg) {
+        public void handleMessage(final Message msg) {
             final KeyboardView keyboardView = getOuterInstance();
             if (keyboardView == null) return;
             final PointerTracker tracker = (PointerTracker) msg.obj;
@@ -190,11 +202,11 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
             }
         }
 
-        public void dismissKeyPreview(long delay, PointerTracker tracker) {
+        public void dismissKeyPreview(final long delay, final PointerTracker tracker) {
             sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, tracker), delay);
         }
 
-        public void cancelDismissKeyPreview(PointerTracker tracker) {
+        public void cancelDismissKeyPreview(final PointerTracker tracker) {
             removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker);
         }
 
@@ -207,20 +219,33 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
         }
     }
 
-    public KeyboardView(Context context, AttributeSet attrs) {
+    public KeyboardView(final Context context, final AttributeSet attrs) {
         this(context, attrs, R.attr.keyboardViewStyle);
     }
 
-    public KeyboardView(Context context, AttributeSet attrs, int defStyle) {
+    public KeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
         super(context, attrs, defStyle);
 
         final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs,
                 R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
-        final TypedArray keyAttr = context.obtainStyledAttributes(attrs,
-                R.styleable.Keyboard_Key, defStyle, R.style.KeyboardView);
-        mKeyDrawParams = new KeyDrawParams(keyboardViewAttr, keyAttr);
-        mKeyPreviewDrawParams = new KeyPreviewDrawParams(keyboardViewAttr, keyAttr);
-        mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout;
+        mKeyBackground = keyboardViewAttr.getDrawable(R.styleable.KeyboardView_keyBackground);
+        mKeyBackground.getPadding(mKeyBackgroundPadding);
+        mPreviewBackground = keyboardViewAttr.getDrawable(
+                R.styleable.KeyboardView_keyPreviewBackground);
+        mPreviewLeftBackground = keyboardViewAttr.getDrawable(
+                R.styleable.KeyboardView_keyPreviewLeftBackground);
+        mPreviewRightBackground = keyboardViewAttr.getDrawable(
+                R.styleable.KeyboardView_keyPreviewRightBackground);
+        setAlpha(mPreviewBackground, PREVIEW_ALPHA);
+        setAlpha(mPreviewLeftBackground, PREVIEW_ALPHA);
+        setAlpha(mPreviewRightBackground, PREVIEW_ALPHA);
+        mPreviewOffset = keyboardViewAttr.getDimensionPixelOffset(
+                R.styleable.KeyboardView_keyPreviewOffset, 0);
+        mPreviewHeight = keyboardViewAttr.getDimensionPixelSize(
+                R.styleable.KeyboardView_keyPreviewHeight, 80);
+        mPreviewLingerTimeout = keyboardViewAttr.getInt(
+                R.styleable.KeyboardView_keyPreviewLingerTimeout, 0);
+        mDelayAfterPreview = mPreviewLingerTimeout;
         mKeyLabelHorizontalPadding = keyboardViewAttr.getDimensionPixelOffset(
                 R.styleable.KeyboardView_keyLabelHorizontalPadding, 0);
         mKeyHintLetterPadding = keyboardViewAttr.getDimension(
@@ -229,24 +254,41 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
                 R.styleable.KeyboardView_keyPopupHintLetterPadding, 0);
         mKeyShiftedLetterHintPadding = keyboardViewAttr.getDimension(
                 R.styleable.KeyboardView_keyShiftedLetterHintPadding, 0);
+        mKeyTextShadowRadius = keyboardViewAttr.getFloat(
+                R.styleable.KeyboardView_keyTextShadowRadius, 0.0f);
         mKeyPreviewLayoutId = keyboardViewAttr.getResourceId(
                 R.styleable.KeyboardView_keyPreviewLayout, 0);
         if (mKeyPreviewLayoutId == 0) {
             mShowKeyPreviewPopup = false;
         }
-        mVerticalCorrection = keyboardViewAttr.getDimensionPixelOffset(
+        mVerticalCorrection = keyboardViewAttr.getDimension(
                 R.styleable.KeyboardView_verticalCorrection, 0);
         mMoreKeysLayout = keyboardViewAttr.getResourceId(
                 R.styleable.KeyboardView_moreKeysLayout, 0);
         mBackgroundDimAlpha = keyboardViewAttr.getInt(
                 R.styleable.KeyboardView_backgroundDimAlpha, 0);
         keyboardViewAttr.recycle();
+
+        final TypedArray keyAttr = context.obtainStyledAttributes(attrs,
+                R.styleable.Keyboard_Key, defStyle, R.style.KeyboardView);
+        mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
         keyAttr.recycle();
 
         mPreviewPlacerView = new PreviewPlacerView(context, attrs);
         mPaint.setAntiAlias(true);
     }
 
+    private static void setAlpha(final Drawable drawable, final int alpha) {
+        if (drawable == null) return;
+        drawable.setAlpha(alpha);
+    }
+
+    private static void blendAlpha(final Paint paint, final int alpha) {
+        final int color = paint.getColor();
+        paint.setARGB((paint.getAlpha() * alpha) / Constants.Color.ALPHA_OPAQUE,
+                Color.red(color), Color.green(color), Color.blue(color));
+    }
+
     /**
      * 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.
@@ -254,13 +296,14 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
      * @see #getKeyboard()
      * @param keyboard the keyboard to display in this view
      */
-    public void setKeyboard(Keyboard keyboard) {
+    public void setKeyboard(final Keyboard keyboard) {
         mKeyboard = keyboard;
         LatinImeLogger.onSetKeyboard(keyboard);
         requestLayout();
         invalidateAllKeys();
-        mKeyDrawParams.updateParams(keyboard);
-        mKeyPreviewDrawParams.updateParams(keyboard, mKeyDrawParams);
+        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
+        mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes);
+        mKeyDrawParams.updateParams(keyHeight, keyboard.mKeyVisualAttributes);
     }
 
     /**
@@ -279,7 +322,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
      * @param delay the delay after which the preview is dismissed
      * @see #isKeyPreviewPopupEnabled()
      */
-    public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
+    public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
         mShowKeyPreviewPopup = previewEnabled;
         mDelayAfterPreview = delay;
     }
@@ -293,14 +336,14 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
         return mShowKeyPreviewPopup;
     }
 
-    public void setGesturePreviewMode(boolean drawsGesturePreviewTrail,
-            boolean drawsGestureFloatingPreviewText) {
+    public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail,
+            final boolean drawsGestureFloatingPreviewText) {
         mPreviewPlacerView.setGesturePreviewMode(
                 drawsGesturePreviewTrail, drawsGestureFloatingPreviewText);
     }
 
     @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
         if (mKeyboard != null) {
             // The main keyboard expands to the display width.
             final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
@@ -311,7 +354,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
     }
 
     @Override
-    public void onDraw(Canvas canvas) {
+    public void onDraw(final Canvas canvas) {
         super.onDraw(canvas);
         if (canvas.isHardwareAccelerated()) {
             onDrawKeyboard(canvas);
@@ -366,7 +409,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
         final int width = getWidth();
         final int height = getHeight();
         final Paint paint = mPaint;
-        final KeyDrawParams params = mKeyDrawParams;
 
         // Calculate clip region and set.
         final boolean drawAllKeys = mInvalidateAllKeys || mInvalidatedKeys.isEmpty();
@@ -399,13 +441,13 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
         if (drawAllKeys || isHardwareAccelerated) {
             // Draw all keys.
             for (final Key key : mKeyboard.mKeys) {
-                onDrawKey(key, canvas, paint, params);
+                onDrawKey(key, canvas, paint);
             }
         } else {
             // Draw invalidated keys.
             for (final Key key : mInvalidatedKeys) {
                 if (mKeyboard.hasKey(key)) {
-                    onDrawKey(key, canvas, paint, params);
+                    onDrawKey(key, canvas, paint);
                 }
             }
         }
@@ -429,7 +471,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
         mInvalidateAllKeys = false;
     }
 
-    public void dimEntireKeyboard(boolean dimmed) {
+    public void dimEntireKeyboard(final boolean dimmed) {
         final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed;
         mNeedsToDimEntireKeyboard = dimmed;
         if (needsRedrawing) {
@@ -437,14 +479,18 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
         }
     }
 
-    private void onDrawKey(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
+    private void onDrawKey(final Key key, final Canvas canvas, final Paint paint) {
         final int keyDrawX = key.getDrawX() + getPaddingLeft();
         final int keyDrawY = key.mY + getPaddingTop();
         canvas.translate(keyDrawX, keyDrawY);
 
+        final int keyHeight = mKeyboard.mMostCommonKeyHeight - mKeyboard.mVerticalGap;
+        final KeyVisualAttributes attr = key.mKeyVisualAttributes;
+        final KeyDrawParams params = mKeyDrawParams.mayCloneAndUpdateParams(keyHeight, attr);
         params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE;
+
         if (!key.isSpacer()) {
-            onDrawKeyBackground(key, canvas, params);
+            onDrawKeyBackground(key, canvas);
         }
         onDrawKeyTopVisuals(key, canvas, paint, params);
 
@@ -452,13 +498,14 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
     }
 
     // Draw key background.
-    protected void onDrawKeyBackground(Key key, Canvas canvas, KeyDrawParams params) {
-        final int bgWidth = key.getDrawWidth() + params.mPadding.left + params.mPadding.right;
-        final int bgHeight = key.mHeight + params.mPadding.top + params.mPadding.bottom;
-        final int bgX = -params.mPadding.left;
-        final int bgY = -params.mPadding.top;
+    protected void onDrawKeyBackground(Key key, Canvas canvas) {
+        final Rect padding = mKeyBackgroundPadding;
+        final int bgWidth = key.getDrawWidth() + padding.left + padding.right;
+        final int bgHeight = key.mHeight + padding.top + padding.bottom;
+        final int bgX = -padding.left;
+        final int bgY = -padding.top;
         final int[] drawableState = key.getCurrentDrawableState();
-        final Drawable background = params.mKeyBackground;
+        final Drawable background = mKeyBackground;
         background.setState(drawableState);
         final Rect bounds = background.getBounds();
         if (bgWidth != bounds.right || bgHeight != bounds.bottom) {
@@ -488,12 +535,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
         float positionX = centerX;
         if (key.mLabel != null) {
             final String label = key.mLabel;
-            // For characters, use large font. For labels like "Done", use smaller font.
-            paint.setTypeface(key.selectTypeface(params.mKeyTypeface));
-            final int labelSize = key.selectTextSize(params.mKeyLetterSize,
-                    params.mKeyLargeLetterSize, params.mKeyLabelSize, params.mKeyLargeLabelSize,
-                    params.mKeyHintLabelSize);
-            paint.setTextSize(labelSize);
+            paint.setTypeface(key.selectTypeface(params));
+            paint.setTextSize(key.selectTextSize(params));
             final float labelCharHeight = getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint);
             final float labelCharWidth = getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint);
 
@@ -531,16 +574,15 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
                         Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) / getLabelWidth(label, paint)));
             }
 
-            paint.setColor(key.isShiftedLetterActivated()
-                    ? params.mKeyTextInactivatedColor : params.mKeyTextColor);
+            paint.setColor(key.selectTextColor(params));
             if (key.isEnabled()) {
                 // Set a drop shadow for the text
-                paint.setShadowLayer(params.mKeyTextShadowRadius, 0, 0, params.mKeyTextShadowColor);
+                paint.setShadowLayer(mKeyTextShadowRadius, 0, 0, params.mTextShadowColor);
             } else {
                 // Make label invisible
                 paint.setColor(Color.TRANSPARENT);
             }
-            params.blendAlpha(paint);
+            blendAlpha(paint, params.mAnimAlpha);
             canvas.drawText(label, 0, label.length(), positionX, baseline, paint);
             // Turn off drop shadow and reset x-scale.
             paint.setShadowLayer(0, 0, 0, 0);
@@ -568,25 +610,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
 
         // Draw hint label.
         if (key.mHintLabel != null) {
-            final String hint = key.mHintLabel;
-            final int hintColor;
-            final int hintSize;
-            if (key.hasHintLabel()) {
-                hintColor = params.mKeyHintLabelColor;
-                hintSize = params.mKeyHintLabelSize;
-                paint.setTypeface(Typeface.DEFAULT);
-            } else if (key.hasShiftedLetterHint()) {
-                hintColor = key.isShiftedLetterActivated()
-                        ? params.mKeyShiftedLetterHintActivatedColor
-                        : params.mKeyShiftedLetterHintInactivatedColor;
-                hintSize = params.mKeyShiftedLetterHintSize;
-            } else { // key.hasHintLetter()
-                hintColor = params.mKeyHintLetterColor;
-                hintSize = params.mKeyHintLetterSize;
-            }
-            paint.setColor(hintColor);
-            params.blendAlpha(paint);
-            paint.setTextSize(hintSize);
+            final String hintLabel = key.mHintLabel;
+            paint.setTextSize(key.selectHintTextSize(params));
+            paint.setColor(key.selectHintTextColor(params));
+            blendAlpha(paint, params.mAnimAlpha);
             final float hintX, hintY;
             if (key.hasHintLabel()) {
                 // The hint label is placed just right of the key label. Used mainly on
@@ -609,7 +636,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
                 hintY = -paint.ascent();
                 paint.setTextAlign(Align.CENTER);
             }
-            canvas.drawText(hint, 0, hint.length(), hintX, hintY, paint);
+            canvas.drawText(hintLabel, 0, hintLabel.length(), hintX, hintY, paint);
 
             if (LatinImeLogger.sVISUALDEBUG) {
                 final Paint line = new Paint();
@@ -653,9 +680,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
         final int keyWidth = key.getDrawWidth();
         final int keyHeight = key.mHeight;
 
-        paint.setTypeface(params.mKeyTypeface);
-        paint.setTextSize(params.mKeyHintLetterSize);
-        paint.setColor(params.mKeyHintLabelColor);
+        paint.setTypeface(params.mTypeface);
+        paint.setTextSize(params.mHintLetterSize);
+        paint.setColor(params.mHintLabelColor);
         paint.setTextAlign(Align.CENTER);
         final float hintX = keyWidth - mKeyHintLetterPadding
                 - getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2;
@@ -752,8 +779,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
     public Paint newDefaultLabelPaint() {
         final Paint paint = new Paint();
         paint.setAntiAlias(true);
-        paint.setTypeface(mKeyDrawParams.mKeyTypeface);
-        paint.setTextSize(mKeyDrawParams.mKeyLabelSize);
+        paint.setTypeface(mKeyDrawParams.mTypeface);
+        paint.setTextSize(mKeyDrawParams.mLabelSize);
         return paint;
     }
 
@@ -791,11 +818,11 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
     }
 
     @Override
-    public void dismissKeyPreview(PointerTracker tracker) {
+    public void dismissKeyPreview(final PointerTracker tracker) {
         mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker);
     }
 
-    private void addKeyPreview(TextView keyPreview) {
+    private void addKeyPreview(final TextView keyPreview) {
         locatePreviewPlacerView();
         mPreviewPlacerView.addView(
                 keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0));
@@ -833,17 +860,17 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
     }
 
     @Override
-    public void showGesturePreviewTrail(PointerTracker tracker) {
+    public void showGesturePreviewTrail(final PointerTracker tracker) {
         locatePreviewPlacerView();
         mPreviewPlacerView.invalidatePointer(tracker);
     }
 
     @SuppressWarnings("deprecation") // setBackgroundDrawable is replaced by setBackground in API16
     @Override
-    public void showKeyPreview(PointerTracker tracker) {
-        final KeyPreviewDrawParams params = mKeyPreviewDrawParams;
+    public void showKeyPreview(final PointerTracker tracker) {
+        final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
         if (!mShowKeyPreviewPopup) {
-            params.mPreviewVisibleOffset = -mKeyboard.mVerticalGap;
+            previewParams.mPreviewVisibleOffset = -mKeyboard.mVerticalGap;
             return;
         }
 
@@ -862,17 +889,18 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
         if (key == null)
             return;
 
+        final KeyDrawParams drawParams = mKeyDrawParams;
         final String label = key.isShiftedLetterActivated() ? key.mHintLabel : key.mLabel;
         // What we show as preview should match what we show on a key top in onDraw().
         if (label != null) {
             // TODO Should take care of temporaryShiftLabel here.
             previewText.setCompoundDrawables(null, null, null, null);
             if (StringUtils.codePointCount(label) > 1) {
-                previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mKeyLetterSize);
+                previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, drawParams.mLetterSize);
                 previewText.setTypeface(Typeface.DEFAULT_BOLD);
             } else {
-                previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mPreviewTextSize);
-                previewText.setTypeface(params.mKeyTypeface);
+                previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, drawParams.mPreviewTextSize);
+                previewText.setTypeface(key.selectTypeface(drawParams));
             }
             previewText.setText(label);
         } else {
@@ -880,47 +908,48 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
                     key.getPreviewIcon(mKeyboard.mIconsSet));
             previewText.setText(null);
         }
-        previewText.setBackgroundDrawable(params.mPreviewBackground);
+        previewText.setBackgroundDrawable(mPreviewBackground);
 
         previewText.measure(
                 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
         final int keyDrawWidth = key.getDrawWidth();
         final int previewWidth = previewText.getMeasuredWidth();
-        final int previewHeight = params.mPreviewHeight;
+        final int previewHeight = mPreviewHeight;
         // The width and height of visible part of the key preview background. The content marker
         // of the background 9-patch have to cover the visible part of the background.
-        params.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft()
+        previewParams.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft()
                 - previewText.getPaddingRight();
-        params.mPreviewVisibleHeight = previewHeight - previewText.getPaddingTop()
+        previewParams.mPreviewVisibleHeight = previewHeight - previewText.getPaddingTop()
                 - previewText.getPaddingBottom();
         // The distance between the top edge of the parent key and the bottom of the visible part
         // of the key preview background.
-        params.mPreviewVisibleOffset = params.mPreviewOffset - previewText.getPaddingBottom();
-        getLocationInWindow(params.mCoordinates);
+        previewParams.mPreviewVisibleOffset = mPreviewOffset - previewText.getPaddingBottom();
+        getLocationInWindow(previewParams.mCoordinates);
         // The key preview is horizontally aligned with the center of the visible part of the
         // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and
         // the left/right background is used if such background is specified.
-        int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2 + params.mCoordinates[0];
+        int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
+                + previewParams.mCoordinates[0];
         if (previewX < 0) {
             previewX = 0;
-            if (params.mPreviewLeftBackground != null) {
-                previewText.setBackgroundDrawable(params.mPreviewLeftBackground);
+            if (mPreviewLeftBackground != null) {
+                previewText.setBackgroundDrawable(mPreviewLeftBackground);
             }
         } else if (previewX > getWidth() - previewWidth) {
             previewX = getWidth() - previewWidth;
-            if (params.mPreviewRightBackground != null) {
-                previewText.setBackgroundDrawable(params.mPreviewRightBackground);
+            if (mPreviewRightBackground != null) {
+                previewText.setBackgroundDrawable(mPreviewRightBackground);
             }
         }
         // The key preview is placed vertically above the top edge of the parent key with an
         // arbitrary offset.
-        final int previewY = key.mY - previewHeight + params.mPreviewOffset
-                + params.mCoordinates[1];
+        final int previewY = key.mY - previewHeight + mPreviewOffset
+                + previewParams.mCoordinates[1];
 
         // Set the preview background state
         previewText.getBackground().setState(
                 key.mMoreKeys != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
-        previewText.setTextColor(params.mPreviewTextColor);
+        previewText.setTextColor(drawParams.mPreviewTextColor);
         ViewLayoutUtils.placeViewAt(
                 previewText, previewX, previewY, previewWidth, previewHeight);
         previewText.setVisibility(VISIBLE);
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 2da2f6dc6cc24ac900c4aabbaf7203fcd4776c8e..06973ef990cb5d96261e3e59b787cc5b924a716a 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -63,9 +63,25 @@ import java.util.WeakHashMap;
 /**
  * A view that is responsible for detecting key presses and touch movements.
  *
- * @attr ref R.styleable#KeyboardView_keyHysteresisDistance
- * @attr ref R.styleable#KeyboardView_verticalCorrection
- * @attr ref R.styleable#KeyboardView_popupLayout
+ * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled
+ * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon
+ * @attr ref R.styleable#MainKeyboardView_spacebarTextRatio
+ * @attr ref R.styleable#MainKeyboardView_spacebarTextColor
+ * @attr ref R.styleable#MainKeyboardView_spacebarTextShadowColor
+ * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha
+ * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator
+ * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator
+ * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator
+ * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance
+ * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime
+ * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance
+ * @attr ref R.styleable#MainKeyboardView_slidingKeyInputEnable
+ * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout
+ * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval
+ * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout
+ * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout
+ * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout
+ * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
  */
 public class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
         SuddenJumpingTouchEventHandler.ProcessMotionEvent {
@@ -150,7 +166,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         }
 
         @Override
-        public void handleMessage(Message msg) {
+        public void handleMessage(final Message msg) {
             final MainKeyboardView keyboardView = getOuterInstance();
             final PointerTracker tracker = (PointerTracker) msg.obj;
             switch (msg.what) {
@@ -174,14 +190,14 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
             }
         }
 
-        private void startKeyRepeatTimer(PointerTracker tracker, long delay) {
+        private void startKeyRepeatTimer(final PointerTracker tracker, final long delay) {
             final Key key = tracker.getKey();
             if (key == null) return;
             sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
         }
 
         @Override
-        public void startKeyRepeatTimer(PointerTracker tracker) {
+        public void startKeyRepeatTimer(final PointerTracker tracker) {
             startKeyRepeatTimer(tracker, mKeyRepeatStartTimeout);
         }
 
@@ -195,7 +211,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         }
 
         @Override
-        public void startLongPressTimer(int code) {
+        public void startLongPressTimer(final int code) {
             cancelLongPressTimer();
             final int delay;
             switch (code) {
@@ -212,7 +228,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         }
 
         @Override
-        public void startLongPressTimer(PointerTracker tracker) {
+        public void startLongPressTimer(final PointerTracker tracker) {
             cancelLongPressTimer();
             if (tracker == null) {
                 return;
@@ -266,7 +282,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         }
 
         @Override
-        public void startTypingStateTimer(Key typedKey) {
+        public void startTypingStateTimer(final Key typedKey) {
             if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) {
                 return;
             }
@@ -322,11 +338,11 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         }
     }
 
-    public MainKeyboardView(Context context, AttributeSet attrs) {
+    public MainKeyboardView(final Context context, final AttributeSet attrs) {
         this(context, attrs, R.attr.mainKeyboardViewStyle);
     }
 
-    public MainKeyboardView(Context context, AttributeSet attrs, int defStyle) {
+    public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
         super(context, attrs, defStyle);
 
         mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
@@ -377,7 +393,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
                 altCodeKeyWhileTypingFadeinAnimatorResId, this);
     }
 
-    private ObjectAnimator loadObjectAnimator(int resId, Object target) {
+    private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
         if (resId == 0) return null;
         final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
                 getContext(), resId);
@@ -392,7 +408,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         return mLanguageOnSpacebarAnimAlpha;
     }
 
-    public void setLanguageOnSpacebarAnimAlpha(int alpha) {
+    public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
         mLanguageOnSpacebarAnimAlpha = alpha;
         invalidateKey(mSpaceKey);
     }
@@ -401,12 +417,12 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         return mAltCodeKeyWhileTypingAnimAlpha;
     }
 
-    public void setAltCodeKeyWhileTypingAnimAlpha(int alpha) {
+    public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
         mAltCodeKeyWhileTypingAnimAlpha = alpha;
         updateAltCodeKeyWhileTyping();
     }
 
-    public void setKeyboardActionListener(KeyboardActionListener listener) {
+    public void setKeyboardActionListener(final KeyboardActionListener listener) {
         mKeyboardActionListener = listener;
         PointerTracker.setKeyboardActionListener(listener);
     }
@@ -443,7 +459,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
      * @param keyboard the keyboard to display in this view
      */
     @Override
-    public void setKeyboard(Keyboard keyboard) {
+    public void setKeyboard(final Keyboard keyboard) {
         // Remove any pending messages, except dismissing preview and key repeat.
         mKeyTimerHandler.cancelLongPressTimer();
         super.setKeyboard(keyboard);
@@ -468,11 +484,11 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
     }
 
     // Note that this method is called from a non-UI thread.
-    public void setMainDictionaryAvailability(boolean mainDictionaryAvailable) {
+    public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
         PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
     }
 
-    public void setGestureHandlingEnabledByUser(boolean gestureHandlingEnabledByUser) {
+    public void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
         PointerTracker.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser);
     }
 
@@ -484,7 +500,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         return mHasDistinctMultitouch;
     }
 
-    public void setDistinctMultitouch(boolean hasDistinctMultitouch) {
+    public void setDistinctMultitouch(final boolean hasDistinctMultitouch) {
         mHasDistinctMultitouch = hasDistinctMultitouch;
     }
 
@@ -515,7 +531,8 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         super.cancelAllMessages();
     }
 
-    private boolean openMoreKeysKeyboardIfRequired(Key parentKey, PointerTracker tracker) {
+    private boolean openMoreKeysKeyboardIfRequired(final Key parentKey,
+            final PointerTracker tracker) {
         // Check if we have a popup layout specified first.
         if (mMoreKeysLayout == 0) {
             return false;
@@ -530,7 +547,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
     }
 
     // This default implementation returns a more keys panel.
-    protected MoreKeysPanel onCreateMoreKeysPanel(Key parentKey) {
+    protected MoreKeysPanel onCreateMoreKeysPanel(final Key parentKey) {
         if (parentKey.mMoreKeys == null)
             return null;
 
@@ -556,7 +573,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
      * @return true if the long press is handled, false otherwise. Subclasses should call the
      * method on the base class if the subclass doesn't wish to handle the call.
      */
-    protected boolean onLongPress(Key parentKey, PointerTracker tracker) {
+    protected boolean onLongPress(final Key parentKey, final PointerTracker tracker) {
         if (ProductionFlag.IS_EXPERIMENTAL) {
             ResearchLogger.mainKeyboardView_onLongPress();
         }
@@ -580,20 +597,20 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         return openMoreKeysPanel(parentKey, tracker);
     }
 
-    private boolean invokeCustomRequest(int code) {
+    private boolean invokeCustomRequest(final int code) {
         return mKeyboardActionListener.onCustomRequest(code);
     }
 
-    private void invokeCodeInput(int primaryCode) {
+    private void invokeCodeInput(final int primaryCode) {
         mKeyboardActionListener.onCodeInput(
                 primaryCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
     }
 
-    private void invokeReleaseKey(int primaryCode) {
+    private void invokeReleaseKey(final int primaryCode) {
         mKeyboardActionListener.onReleaseKey(primaryCode, false);
     }
 
-    private boolean openMoreKeysPanel(Key parentKey, PointerTracker tracker) {
+    private boolean openMoreKeysPanel(final Key parentKey, final PointerTracker tracker) {
         MoreKeysPanel moreKeysPanel = mMoreKeysPanelCache.get(parentKey);
         if (moreKeysPanel == null) {
             moreKeysPanel = onCreateMoreKeysPanel(parentKey);
@@ -644,7 +661,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
     }
 
     @Override
-    public boolean onTouchEvent(MotionEvent me) {
+    public boolean onTouchEvent(final MotionEvent me) {
         if (getKeyboard() == null) {
             return false;
         }
@@ -652,7 +669,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
     }
 
     @Override
-    public boolean processMotionEvent(MotionEvent me) {
+    public boolean processMotionEvent(final MotionEvent me) {
         final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
         final int action = me.getActionMasked();
         final int pointerCount = me.getPointerCount();
@@ -819,7 +836,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
      *         otherwise
      */
     @Override
-    public boolean dispatchHoverEvent(MotionEvent event) {
+    public boolean dispatchHoverEvent(final MotionEvent event) {
         if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
             final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
             return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
@@ -829,7 +846,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         return false;
     }
 
-    public void updateShortcutKey(boolean available) {
+    public void updateShortcutKey(final boolean available) {
         final Keyboard keyboard = getKeyboard();
         if (keyboard == null) return;
         final Key shortcutKey = keyboard.getKey(Keyboard.CODE_SHORTCUT);
@@ -846,8 +863,8 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         }
     }
 
-    public void startDisplayLanguageOnSpacebar(boolean subtypeChanged,
-            boolean needsToDisplayLanguage, boolean hasMultipleEnabledIMEsOrSubtypes) {
+    public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged,
+            final boolean needsToDisplayLanguage, final boolean hasMultipleEnabledIMEsOrSubtypes) {
         mNeedsToDisplayLanguage = needsToDisplayLanguage;
         mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes;
         final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
@@ -869,14 +886,15 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         invalidateKey(mSpaceKey);
     }
 
-    public void updateAutoCorrectionState(boolean isAutoCorrection) {
+    public void updateAutoCorrectionState(final boolean isAutoCorrection) {
         if (!mAutoCorrectionSpacebarLedEnabled) return;
         mAutoCorrectionSpacebarLedOn = isAutoCorrection;
         invalidateKey(mSpaceKey);
     }
 
     @Override
-    protected void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
+    protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
+            final KeyDrawParams params) {
         if (key.altCodeWhileTyping() && key.isEnabled()) {
             params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
         }
@@ -894,7 +912,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         }
     }
 
-    private boolean fitsTextIntoWidth(final int width, String text, Paint paint) {
+    private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
         paint.setTextScaleX(1.0f);
         final float textWidth = getLabelWidth(text, paint);
         if (textWidth < width) return true;
@@ -907,7 +925,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
     }
 
     // Layout language name on spacebar.
-    private String layoutLanguageOnSpacebar(Paint paint, InputMethodSubtype subtype,
+    private String layoutLanguageOnSpacebar(final Paint paint, final InputMethodSubtype subtype,
             final int width) {
         // Choose appropriate language name to fit into the width.
         String text = getFullDisplayName(subtype, getResources());
@@ -928,7 +946,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
         return "";
     }
 
-    private void drawSpacebar(Key key, Canvas canvas, Paint paint) {
+    private void drawSpacebar(final Key key, final Canvas canvas, final Paint paint) {
         final int width = key.mWidth;
         final int height = key.mHeight;
 
@@ -983,7 +1001,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
     //  zz    azerty T      AZERTY    AZERTY
 
     // Get InputMethodSubtype's full display name in its locale.
-    static String getFullDisplayName(InputMethodSubtype subtype, Resources res) {
+    static String getFullDisplayName(final InputMethodSubtype subtype, final Resources res) {
         if (SubtypeLocale.isNoLanguage(subtype)) {
             return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
         }
@@ -992,7 +1010,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
     }
 
     // Get InputMethodSubtype's short display name in its locale.
-    static String getShortDisplayName(InputMethodSubtype subtype) {
+    static String getShortDisplayName(final InputMethodSubtype subtype) {
         if (SubtypeLocale.isNoLanguage(subtype)) {
             return "";
         }
@@ -1001,7 +1019,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key
     }
 
     // Get InputMethodSubtype's middle display name in its locale.
-    static String getMiddleDisplayName(InputMethodSubtype subtype) {
+    static String getMiddleDisplayName(final InputMethodSubtype subtype) {
         if (SubtypeLocale.isNoLanguage(subtype)) {
             return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
         }
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
index 51b157c2814a78b4807d7f74adc3354eb2e4750d..c9af888f9f5d9dc76de7d1fbf669074985d65f00 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
@@ -311,9 +311,8 @@ public class MoreKeysKeyboard extends Keyboard {
                     .getDimension(R.dimen.more_keys_keyboard_key_horizontal_padding)
                     + (parentKey.hasLabelsInMoreKeys() ? minKeyWidth * LABEL_PADDING_RATIO : 0));
             final Paint paint = view.newDefaultLabelPaint();
-            paint.setTextSize(parentKey.hasLabelsInMoreKeys()
-                    ? view.mKeyDrawParams.mKeyLabelSize
-                    : view.mKeyDrawParams.mKeyLetterSize);
+            paint.setTypeface(parentKey.selectTypeface(view.mKeyDrawParams));
+            paint.setTextSize(parentKey.selectMoreKeyTextSize(view.mKeyDrawParams));
             int maxWidth = minKeyWidth;
             for (final MoreKeySpec spec : parentKey.mMoreKeys) {
                 final String label = spec.mLabel;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
index 6b106047bf13f1c2e731234aaf11ed4f39c5c01b..43b32372dce047e02c66d8843dafb11efd793d6b 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
@@ -16,113 +16,98 @@
 
 package com.android.inputmethod.keyboard.internal;
 
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
 import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
 
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.ResourceUtils;
 
 public class KeyDrawParams {
-    // XML attributes
-    public final int mKeyTextColor;
-    public final int mKeyTextInactivatedColor;
-    public final int mKeyTextShadowColor;
-    public final float mKeyTextShadowRadius;
-    public final Drawable mKeyBackground;
-    public final int mKeyHintLetterColor;
-    public final int mKeyHintLabelColor;
-    public final int mKeyShiftedLetterHintInactivatedColor;
-    public final int mKeyShiftedLetterHintActivatedColor;
-
-    private final Typeface mKeyTypefaceFromKeyboardView;
-    private final float mKeyLetterRatio;
-    private final int mKeyLetterSizeFromKeyboardView;
-    private final float mKeyLargeLetterRatio;
-    private final float mKeyLabelRatio;
-    private final float mKeyLargeLabelRatio;
-    private final float mKeyHintLetterRatio;
-    private final float mKeyShiftedLetterHintRatio;
-    private final float mKeyHintLabelRatio;
-
-    public final Rect mPadding = new Rect();
-    public Typeface mKeyTypeface;
-    public int mKeyLetterSize;
-    public int mKeyLargeLetterSize;
-    public int mKeyLabelSize;
-    public int mKeyLargeLabelSize;
-    public int mKeyHintLetterSize;
-    public int mKeyShiftedLetterHintSize;
-    public int mKeyHintLabelSize;
+    public Typeface mTypeface;
+
+    public int mLetterSize;
+    public int mLabelSize;
+    public int mLargeLetterSize;
+    public int mLargeLabelSize;
+    public int mHintLetterSize;
+    public int mShiftedLetterHintSize;
+    public int mHintLabelSize;
+    public int mPreviewTextSize;
+
+    public int mTextColor;
+    public int mTextInactivatedColor;
+    public int mTextShadowColor;
+    public int mHintLetterColor;
+    public int mHintLabelColor;
+    public int mShiftedLetterHintInactivatedColor;
+    public int mShiftedLetterHintActivatedColor;
+    public int mPreviewTextColor;
+
     public int mAnimAlpha;
 
-    public KeyDrawParams(final TypedArray keyboardViewAttr, final TypedArray keyAttr) {
-        mKeyBackground = keyboardViewAttr.getDrawable(R.styleable.KeyboardView_keyBackground);
-        mKeyBackground.getPadding(mPadding);
-
-        mKeyLetterRatio = ResourceUtils.getFraction(keyAttr,
-                R.styleable.Keyboard_Key_keyLetterSize);
-        mKeyLetterSizeFromKeyboardView = ResourceUtils.getDimensionPixelSize(keyAttr,
-                R.styleable.Keyboard_Key_keyLetterSize);
-        mKeyLabelRatio = ResourceUtils.getFraction(keyAttr,
-                R.styleable.Keyboard_Key_keyLabelSize);
-        mKeyLabelSize = ResourceUtils.getDimensionPixelSize(keyAttr,
-                R.styleable.Keyboard_Key_keyLabelSize);
-        mKeyLargeLabelRatio = ResourceUtils.getFraction(keyAttr,
-                R.styleable.Keyboard_Key_keyLargeLabelRatio);
-        mKeyLargeLetterRatio = ResourceUtils.getFraction(keyAttr,
-                R.styleable.Keyboard_Key_keyLargeLetterRatio);
-        mKeyHintLetterRatio = ResourceUtils.getFraction(keyAttr,
-                R.styleable.Keyboard_Key_keyHintLetterRatio);
-        mKeyShiftedLetterHintRatio = ResourceUtils.getFraction(keyAttr,
-                R.styleable.Keyboard_Key_keyShiftedLetterHintRatio);
-        mKeyHintLabelRatio = ResourceUtils.getFraction(keyAttr,
-                R.styleable.Keyboard_Key_keyHintLabelRatio);
-        mKeyTextColor = keyAttr.getColor(
-                R.styleable.Keyboard_Key_keyTextColor, Color.WHITE);
-        mKeyTextInactivatedColor = keyAttr.getColor(
-                R.styleable.Keyboard_Key_keyTextInactivatedColor, Color.WHITE);
-        mKeyHintLetterColor = keyAttr.getColor(
-                R.styleable.Keyboard_Key_keyHintLetterColor, Color.TRANSPARENT);
-        mKeyHintLabelColor = keyAttr.getColor(
-                R.styleable.Keyboard_Key_keyHintLabelColor, Color.TRANSPARENT);
-        mKeyShiftedLetterHintInactivatedColor = keyAttr.getColor(
-                R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor, Color.TRANSPARENT);
-        mKeyShiftedLetterHintActivatedColor = keyAttr.getColor(
-                R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor, Color.TRANSPARENT);
-        mKeyTypefaceFromKeyboardView = Typeface.defaultFromStyle(
-                keyAttr.getInt(R.styleable.Keyboard_Key_keyTypeface, Typeface.NORMAL));
-        mKeyTextShadowColor = keyAttr.getColor(
-                R.styleable.Keyboard_Key_keyTextShadowColor, Color.TRANSPARENT);
-        mKeyTextShadowRadius = keyAttr.getFloat(
-                R.styleable.Keyboard_Key_keyTextShadowRadius, 0f);
+    private KeyDrawParams(final KeyDrawParams copyFrom) {
+        mTypeface = copyFrom.mTypeface;
+
+        mLetterSize = copyFrom.mLetterSize;
+        mLabelSize = copyFrom.mLabelSize;
+        mLargeLetterSize = copyFrom.mLargeLetterSize;
+        mLargeLabelSize = copyFrom.mLargeLabelSize;
+        mHintLetterSize = copyFrom.mHintLetterSize;
+        mShiftedLetterHintSize = copyFrom.mShiftedLetterHintSize;
+        mHintLabelSize = copyFrom.mHintLabelSize;
+        mPreviewTextSize = copyFrom.mPreviewTextSize;
+
+        mTextColor = copyFrom.mTextColor;
+        mTextInactivatedColor = copyFrom.mTextInactivatedColor;
+        mTextShadowColor = copyFrom.mTextShadowColor;
+        mHintLetterColor = copyFrom.mHintLetterColor;
+        mHintLabelColor = copyFrom.mHintLabelColor;
+        mShiftedLetterHintInactivatedColor = copyFrom.mShiftedLetterHintInactivatedColor;
+        mShiftedLetterHintActivatedColor = copyFrom.mShiftedLetterHintActivatedColor;
+        mPreviewTextColor = copyFrom.mPreviewTextColor;
+
+        mAnimAlpha = copyFrom.mAnimAlpha;
     }
 
-    public void updateParams(final Keyboard keyboard) {
-        mKeyTypeface = (keyboard.mKeyTypeface != null)
-                ? keyboard.mKeyTypeface : mKeyTypefaceFromKeyboardView;
-        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
-        mKeyLetterSize = selectTextSizeFromDimensionOrRatio(keyHeight,
-                mKeyLetterSizeFromKeyboardView, mKeyLetterRatio,
-                mKeyLetterSizeFromKeyboardView);
-        // Override if size/ratio is specified in Keyboard.
-        mKeyLetterSize = selectTextSizeFromDimensionOrRatio(keyHeight, keyboard.mKeyLetterSize,
-                keyboard.mKeyLetterRatio, mKeyLetterSize);
-        if (ResourceUtils.isValidFraction(mKeyLabelRatio)) {
-            mKeyLabelSize = (int)(keyHeight * mKeyLabelRatio);
+    public void updateParams(final int keyHeight, final KeyVisualAttributes attr) {
+        if (attr == null) {
+            return;
+        }
+
+        if (attr.mTypeface != null) {
+            mTypeface = attr.mTypeface;
         }
-        mKeyLargeLabelSize = (int)(keyHeight * mKeyLargeLabelRatio);
-        mKeyLargeLetterSize = (int)(keyHeight * mKeyLargeLetterRatio);
-        mKeyHintLetterSize = selectTextSizeFromKeyboardOrView(keyHeight,
-                keyboard.mKeyHintLetterRatio, mKeyHintLetterRatio);
-        mKeyShiftedLetterHintSize = selectTextSizeFromKeyboardOrView(keyHeight,
-                keyboard.mKeyShiftedLetterHintRatio, mKeyShiftedLetterHintRatio);
-        mKeyHintLabelSize = (int)(keyHeight * mKeyHintLabelRatio);
+
+        mLetterSize = selectTextSizeFromDimensionOrRatio(keyHeight,
+                attr.mLetterSize, attr.mLetterRatio, mLetterSize);
+        mLabelSize = selectTextSizeFromDimensionOrRatio(keyHeight,
+                attr.mLabelSize, attr.mLabelRatio, mLabelSize);
+        mLargeLabelSize = selectTextSize(keyHeight, attr.mLargeLabelRatio, mLargeLabelSize);
+        mLargeLetterSize = selectTextSize(keyHeight, attr.mLargeLetterRatio, mLargeLetterSize);
+        mHintLetterSize = selectTextSize(keyHeight, attr.mHintLetterRatio, mHintLetterSize);
+        mShiftedLetterHintSize = selectTextSize(keyHeight,
+                attr.mShiftedLetterHintRatio, mShiftedLetterHintSize);
+        mHintLabelSize = selectTextSize(keyHeight, attr.mHintLabelRatio, mHintLabelSize);
+        mPreviewTextSize = selectTextSize(keyHeight, attr.mPreviewTextRatio, mPreviewTextSize);
+
+        mTextColor = selectColor(attr.mTextColor, mTextColor);
+        mTextInactivatedColor = selectColor(attr.mTextInactivatedColor, mTextInactivatedColor);
+        mTextShadowColor = selectColor(attr.mTextShadowColor, mTextShadowColor);
+        mHintLetterColor = selectColor(attr.mHintLetterColor, mHintLetterColor);
+        mHintLabelColor = selectColor(attr.mHintLabelColor, mHintLabelColor);
+        mShiftedLetterHintInactivatedColor = selectColor(
+                attr.mShiftedLetterHintInactivatedColor, mShiftedLetterHintInactivatedColor);
+        mShiftedLetterHintActivatedColor = selectColor(
+                attr.mShiftedLetterHintActivatedColor, mShiftedLetterHintActivatedColor);
+        mPreviewTextColor = selectColor(attr.mPreviewTextColor, mPreviewTextColor);
+    }
+
+    public KeyDrawParams mayCloneAndUpdateParams(final int keyHeight,
+            final KeyVisualAttributes attr) {
+        if (attr == null) {
+            return this;
+        }
+        final KeyDrawParams newParams = new KeyDrawParams(this);
+        newParams.updateParams(keyHeight, attr);
+        return newParams;
     }
 
     private static final int selectTextSizeFromDimensionOrRatio(final int keyHeight,
@@ -136,16 +121,18 @@ public class KeyDrawParams {
         return defaultDimens;
     }
 
-    private static final int selectTextSizeFromKeyboardOrView(final int keyHeight,
-            final float ratioFromKeyboard, final float ratioFromView) {
-        final float ratio = ResourceUtils.isValidFraction(ratioFromKeyboard)
-                ? ratioFromKeyboard : ratioFromView;
-        return (int)(keyHeight * ratio);
+    private static final int selectTextSize(final int keyHeight, final float ratio,
+            final int defaultSize) {
+        if (ResourceUtils.isValidFraction(ratio)) {
+            return (int)(keyHeight * ratio);
+        }
+        return defaultSize;
     }
 
-    public void blendAlpha(final Paint paint) {
-        final int color = paint.getColor();
-        paint.setARGB((paint.getAlpha() * mAnimAlpha) / Constants.Color.ALPHA_OPAQUE,
-                Color.red(color), Color.green(color), Color.blue(color));
+    private static final int selectColor(final int attrColor, final int defaultColor) {
+        if (attrColor != 0) {
+            return attrColor;
+        }
+        return defaultColor;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
index a3a38507f78e6619a9463447e7c784fdbca04ce2..996a722c09eea8c04bd1da1603dc614bf50da77b 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
@@ -16,26 +16,7 @@
 
 package com.android.inputmethod.keyboard.internal;
 
-import android.content.res.TypedArray;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResourceUtils;
-
 public class KeyPreviewDrawParams {
-    // XML attributes.
-    public final Drawable mPreviewBackground;
-    public final Drawable mPreviewLeftBackground;
-    public final Drawable mPreviewRightBackground;
-    public final int mPreviewTextColor;
-    public final int mPreviewOffset;
-    public final int mPreviewHeight;
-    public final int mLingerTimeout;
-
-    private final float mPreviewTextRatio;
-
     // The graphical geometry of the key preview.
     // <-width->
     // +-------+   ^
@@ -61,46 +42,5 @@ public class KeyPreviewDrawParams {
     // preview background.
     public int mPreviewVisibleOffset;
 
-    public Typeface mKeyTypeface;
-    public int mPreviewTextSize;
-    public int mKeyLetterSize;
     public final int[] mCoordinates = new int[2];
-
-    private static final int PREVIEW_ALPHA = 240;
-
-    public KeyPreviewDrawParams(final TypedArray keyboardViewAttr, final TypedArray keyAttr) {
-        mPreviewBackground = keyboardViewAttr.getDrawable(
-                R.styleable.KeyboardView_keyPreviewBackground);
-        mPreviewLeftBackground = keyboardViewAttr.getDrawable(
-                R.styleable.KeyboardView_keyPreviewLeftBackground);
-        mPreviewRightBackground = keyboardViewAttr.getDrawable(
-                R.styleable.KeyboardView_keyPreviewRightBackground);
-        setAlpha(mPreviewBackground, PREVIEW_ALPHA);
-        setAlpha(mPreviewLeftBackground, PREVIEW_ALPHA);
-        setAlpha(mPreviewRightBackground, PREVIEW_ALPHA);
-        mPreviewOffset = keyboardViewAttr.getDimensionPixelOffset(
-                R.styleable.KeyboardView_keyPreviewOffset, 0);
-        mPreviewHeight = keyboardViewAttr.getDimensionPixelSize(
-                R.styleable.KeyboardView_keyPreviewHeight, 80);
-        mLingerTimeout = keyboardViewAttr.getInt(
-                R.styleable.KeyboardView_keyPreviewLingerTimeout, 0);
-
-        mPreviewTextRatio = ResourceUtils.getFraction(keyAttr,
-                R.styleable.Keyboard_Key_keyPreviewTextRatio);
-        mPreviewTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyPreviewTextColor, 0);
-    }
-
-    public void updateParams(final Keyboard keyboard, final KeyDrawParams keyDrawParams) {
-        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
-        if (ResourceUtils.isValidFraction(mPreviewTextRatio)) {
-            mPreviewTextSize = (int)(keyHeight * mPreviewTextRatio);
-        }
-        mKeyLetterSize = keyDrawParams.mKeyLetterSize;
-        mKeyTypeface = keyDrawParams.mKeyTypeface;
-    }
-
-    private static void setAlpha(final Drawable drawable, final int alpha) {
-        if (drawable == null) return;
-        drawable.setAlpha(alpha);
-    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
new file mode 100644
index 0000000000000000000000000000000000000000..04cc152fe5add7663f5d16a83cecccbc70c34fc6
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import android.content.res.TypedArray;
+import android.graphics.Typeface;
+import android.util.SparseIntArray;
+
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.ResourceUtils;
+
+public class KeyVisualAttributes {
+    public final Typeface mTypeface;
+
+    public final float mLetterRatio;
+    public final int mLetterSize;
+    public final float mLabelRatio;
+    public final int mLabelSize;
+    public final float mLargeLetterRatio;
+    public final float mLargeLabelRatio;
+    public final float mHintLetterRatio;
+    public final float mShiftedLetterHintRatio;
+    public final float mHintLabelRatio;
+    public final float mPreviewTextRatio;
+
+    public final int mTextColor;
+    public final int mTextInactivatedColor;
+    public final int mTextShadowColor;
+    public final int mHintLetterColor;
+    public final int mHintLabelColor;
+    public final int mShiftedLetterHintInactivatedColor;
+    public final int mShiftedLetterHintActivatedColor;
+    public final int mPreviewTextColor;
+
+    private static final int[] VISUAL_ATTRIBUTE_IDS = {
+        R.styleable.Keyboard_Key_keyTypeface,
+        R.styleable.Keyboard_Key_keyLetterSize,
+        R.styleable.Keyboard_Key_keyLabelSize,
+        R.styleable.Keyboard_Key_keyLargeLetterRatio,
+        R.styleable.Keyboard_Key_keyLargeLabelRatio,
+        R.styleable.Keyboard_Key_keyHintLetterRatio,
+        R.styleable.Keyboard_Key_keyShiftedLetterHintRatio,
+        R.styleable.Keyboard_Key_keyHintLabelRatio,
+        R.styleable.Keyboard_Key_keyPreviewTextRatio,
+        R.styleable.Keyboard_Key_keyTextColor,
+        R.styleable.Keyboard_Key_keyTextInactivatedColor,
+        R.styleable.Keyboard_Key_keyTextShadowColor,
+        R.styleable.Keyboard_Key_keyHintLetterColor,
+        R.styleable.Keyboard_Key_keyHintLabelColor,
+        R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor,
+        R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor,
+        R.styleable.Keyboard_Key_keyPreviewTextColor,
+    };
+    private static final SparseIntArray sVisualAttributeIds = new SparseIntArray();
+    private static final int ATTR_DEFINED = 1;
+    private static final int ATTR_NOT_FOUND = 0;
+    static {
+        for (final int attrId : VISUAL_ATTRIBUTE_IDS) {
+            sVisualAttributeIds.put(attrId, ATTR_DEFINED);
+        }
+    }
+
+    public static KeyVisualAttributes newInstance(final TypedArray keyAttr) {
+        final int indexCount = keyAttr.getIndexCount();
+        for (int i = 0; i < indexCount; i++) {
+            final int attrId = keyAttr.getIndex(i);
+            if (sVisualAttributeIds.get(attrId, ATTR_NOT_FOUND) == ATTR_NOT_FOUND) {
+                continue;
+            }
+            return new KeyVisualAttributes(keyAttr);
+        }
+        return null;
+    }
+
+    private KeyVisualAttributes(final TypedArray keyAttr) {
+        if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyTypeface)) {
+            mTypeface = Typeface.defaultFromStyle(
+                    keyAttr.getInt(R.styleable.Keyboard_Key_keyTypeface, Typeface.NORMAL));
+        } else {
+            mTypeface = null;
+        }
+
+        mLetterRatio = ResourceUtils.getFraction(keyAttr,
+                R.styleable.Keyboard_Key_keyLetterSize);
+        mLetterSize = ResourceUtils.getDimensionPixelSize(keyAttr,
+                R.styleable.Keyboard_Key_keyLetterSize);
+        mLabelRatio = ResourceUtils.getFraction(keyAttr,
+                R.styleable.Keyboard_Key_keyLabelSize);
+        mLabelSize = ResourceUtils.getDimensionPixelSize(keyAttr,
+                R.styleable.Keyboard_Key_keyLabelSize);
+        mLargeLetterRatio = ResourceUtils.getFraction(keyAttr,
+                R.styleable.Keyboard_Key_keyLargeLetterRatio);
+        mLargeLabelRatio = ResourceUtils.getFraction(keyAttr,
+                R.styleable.Keyboard_Key_keyLargeLabelRatio);
+        mHintLetterRatio = ResourceUtils.getFraction(keyAttr,
+                R.styleable.Keyboard_Key_keyHintLetterRatio);
+        mShiftedLetterHintRatio = ResourceUtils.getFraction(keyAttr,
+                R.styleable.Keyboard_Key_keyShiftedLetterHintRatio);
+        mHintLabelRatio = ResourceUtils.getFraction(keyAttr,
+                R.styleable.Keyboard_Key_keyHintLabelRatio);
+        mPreviewTextRatio = ResourceUtils.getFraction(keyAttr,
+                R.styleable.Keyboard_Key_keyPreviewTextRatio);
+
+        mTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextColor, 0);
+        mTextInactivatedColor = keyAttr.getColor(
+                R.styleable.Keyboard_Key_keyTextInactivatedColor, 0);
+        mTextShadowColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextShadowColor, 0);
+        mHintLetterColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyHintLetterColor, 0);
+        mHintLabelColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyHintLabelColor, 0);
+        mShiftedLetterHintInactivatedColor = keyAttr.getColor(
+                R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor, 0);
+        mShiftedLetterHintActivatedColor = keyAttr.getColor(
+                R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor, 0);
+        mPreviewTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyPreviewTextColor, 0);
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index 3d1045f887e1cf667e683e4b8dbb0c8bfb4d8ee3..31c7cb565ef645d9248940f4902bbc5e00d73e7c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -20,7 +20,6 @@ import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.graphics.Typeface;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -289,18 +288,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
                     R.styleable.Keyboard_rowHeight, params.mBaseHeight,
                     params.mBaseHeight / DEFAULT_KEYBOARD_ROWS);
 
-            if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyTypeface)) {
-                params.mKeyTypeface = Typeface.defaultFromStyle(keyAttr.getInt(
-                        R.styleable.Keyboard_Key_keyTypeface, Typeface.NORMAL));
-            }
-            params.mKeyLetterRatio = ResourceUtils.getFraction(keyAttr,
-                    R.styleable.Keyboard_Key_keyLetterSize);
-            params.mKeyLetterSize = ResourceUtils.getDimensionPixelSize(keyAttr,
-                    R.styleable.Keyboard_Key_keyLetterSize);
-            params.mKeyHintLetterRatio = ResourceUtils.getFraction(keyAttr,
-                    R.styleable.Keyboard_Key_keyHintLetterRatio);
-            params.mKeyShiftedLetterHintRatio = ResourceUtils.getFraction(keyAttr,
-                    R.styleable.Keyboard_Key_keyShiftedLetterHintRatio);
+            params.mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
 
             params.mMoreKeysTemplate = keyboardAttr.getResourceId(
                     R.styleable.Keyboard_moreKeysTemplate, 0);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
index ff5d315280d41782d64b82f747fe69125a3294fa..ab5d31d42e305936522861bc7e2dd866a431828c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
@@ -16,14 +16,12 @@
 
 package com.android.inputmethod.keyboard.internal;
 
-import android.graphics.Typeface;
 import android.util.SparseIntArray;
 
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.latin.CollectionUtils;
-import com.android.inputmethod.latin.ResourceUtils;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -47,11 +45,7 @@ public class KeyboardParams {
     public int mHorizontalEdgesPadding;
     public int mHorizontalCenterPadding;
 
-    public Typeface mKeyTypeface = null;
-    public float mKeyLetterRatio = ResourceUtils.UNDEFINED_RATIO;
-    public int mKeyLetterSize = ResourceUtils.UNDEFINED_DIMENSION;
-    public float mKeyHintLetterRatio = ResourceUtils.UNDEFINED_RATIO;
-    public float mKeyShiftedLetterHintRatio = ResourceUtils.UNDEFINED_RATIO;
+    public KeyVisualAttributes mKeyVisualAttributes;
 
     public int mDefaultRowHeight;
     public int mDefaultKeyWidth;