Merge "Add element predicator to <switch><case> condition of Keyboard"
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 3a9423f..b3c5ed7 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -52,9 +52,9 @@
     public final int mAltCode;
 
     /** Label to display */
-    public final CharSequence mLabel;
+    public final String mLabel;
     /** Hint label to display on the key in conjunction with the label */
-    public final CharSequence mHintLabel;
+    public final String mHintLabel;
     /** Flags of the label */
     private final int mLabelFlags;
     private static final int LABEL_FLAGS_ALIGN_LEFT = 0x01;
@@ -187,7 +187,7 @@
     /**
      * This constructor is being used only for key in popup suggestions pane.
      */
-    public Key(Keyboard.Params params, CharSequence label, CharSequence hintLabel, Drawable icon,
+    public Key(Keyboard.Params params, String label, String hintLabel, Drawable icon,
             int code, CharSequence outputText, int x, int y, int width, int height) {
         mHeight = height - params.mVerticalGap;
         mHorizontalGap = params.mHorizontalGap;
@@ -260,7 +260,7 @@
         // Update row to have current x coordinate.
         row.setXPos(keyXPos + keyWidth);
 
-        final String[] moreKeys = style.getTextArray(keyAttr,
+        final String[] moreKeys = style.getStringArray(keyAttr,
                 R.styleable.Keyboard_Key_moreKeys);
         // In Arabic symbol layouts, we'd like to keep digits in more keys regardless of
         // config_digit_more_keys_enabled.
@@ -291,11 +291,11 @@
         final int disabledIconAttrId = KeyboardIconsSet.getIconAttrId(style.getInt(keyAttr,
                 R.styleable.Keyboard_Key_keyIconDisabled, KeyboardIconsSet.ICON_UNDEFINED));
         mDisabledIcon = iconsSet.getIconByAttrId(disabledIconAttrId);
-        mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
+        mHintLabel = style.getString(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
 
-        mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel);
+        mLabel = style.getString(keyAttr, R.styleable.Keyboard_Key_keyLabel);
         mLabelFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags, 0);
-        mOutputText = style.getText(keyAttr, R.styleable.Keyboard_Key_keyOutputText);
+        mOutputText = style.getString(keyAttr, R.styleable.Keyboard_Key_keyOutputText);
         // Choose the first letter of the label as primary code if not
         // specified.
         final int code = style.getInt(keyAttr, R.styleable.Keyboard_Key_code,
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index a09232b..72fc338 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -245,10 +245,10 @@
     }
 
     // TODO: Remove this method.
-    public CharSequence adjustLabelCase(CharSequence label) {
+    public String adjustLabelCase(String label) {
         if (mId.isAlphabetKeyboard() && isShiftedOrShiftLocked() && !TextUtils.isEmpty(label)
                 && label.length() < 3 && Character.isLowerCase(label.charAt(0))) {
-            return label.toString().toUpperCase(mId.mLocale);
+            return label.toUpperCase(mId.mLocale);
         }
         return label;
     }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index abc220e..732c8af 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -373,7 +373,7 @@
     }
 
     // Read fraction value in TypedArray as float.
-    private static float getRatio(TypedArray a, int index) {
+    /* package */ static float getRatio(TypedArray a, int index) {
         return a.getFraction(index, 1000, 1000, 1) / 1000.0f;
     }
 
@@ -519,7 +519,7 @@
     }
 
     // Draw key background.
-    /* package */ void onDrawKeyBackground(Key key, Canvas canvas, KeyDrawParams params) {
+    protected void onDrawKeyBackground(Key key, Canvas canvas, KeyDrawParams params) {
         final int bgWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight
                 + params.mPadding.left + params.mPadding.right;
         final int bgHeight = key.mHeight + params.mPadding.top + params.mPadding.bottom;
@@ -541,8 +541,7 @@
     }
 
     // Draw key top visuals.
-    /* package */ void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint,
-            KeyDrawParams params) {
+    protected void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
         final int keyWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
         final int keyHeight = key.mHeight;
         final float centerX = keyWidth * 0.5f;
@@ -557,7 +556,7 @@
         float positionX = centerX;
         if (key.mLabel != null) {
             // Switch the character to uppercase if shift is pressed
-            final CharSequence label = mKeyboard.adjustLabelCase(key.mLabel);
+            final String label = mKeyboard.adjustLabelCase(key.mLabel);
             // For characters, use large font. For labels like "Done", use smaller font.
             paint.setTypeface(key.selectTypeface(params.mKeyTextStyle));
             final int labelSize = key.selectTextSize(params.mKeyLetterSize,
@@ -639,7 +638,7 @@
 
         // Draw hint label.
         if (key.mHintLabel != null) {
-            final CharSequence hint = key.mHintLabel;
+            final String hint = key.mHintLabel;
             final int hintColor;
             final int hintSize;
             if (key.hasHintLabel()) {
@@ -718,7 +717,7 @@
     }
 
     // Draw popup hint "..." at the bottom right corner of the key.
-    /* package */ void drawKeyPopupHint(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
+    protected void drawKeyPopupHint(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
         final int keyWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
         final int keyHeight = key.mHeight;
 
@@ -737,8 +736,6 @@
         }
     }
 
-    private static final Rect sTextBounds = new Rect();
-
     private static int getCharGeometryCacheKey(char reference, Paint paint) {
         final int labelSize = (int)paint.getTextSize();
         final Typeface face = paint.getTypeface();
@@ -754,42 +751,45 @@
         }
     }
 
-    private static float getCharHeight(char[] character, Paint paint) {
+    // Working variable for the following methods.
+    private final Rect mTextBounds = new Rect();
+
+    private float getCharHeight(char[] character, Paint paint) {
         final Integer key = getCharGeometryCacheKey(character[0], paint);
         final Float cachedValue = sTextHeightCache.get(key);
         if (cachedValue != null)
             return cachedValue;
 
-        paint.getTextBounds(character, 0, 1, sTextBounds);
-        final float height = sTextBounds.height();
+        paint.getTextBounds(character, 0, 1, mTextBounds);
+        final float height = mTextBounds.height();
         sTextHeightCache.put(key, height);
         return height;
     }
 
-    private static float getCharWidth(char[] character, Paint paint) {
+    private float getCharWidth(char[] character, Paint paint) {
         final Integer key = getCharGeometryCacheKey(character[0], paint);
         final Float cachedValue = sTextWidthCache.get(key);
         if (cachedValue != null)
             return cachedValue;
 
-        paint.getTextBounds(character, 0, 1, sTextBounds);
-        final float width = sTextBounds.width();
+        paint.getTextBounds(character, 0, 1, mTextBounds);
+        final float width = mTextBounds.width();
         sTextWidthCache.put(key, width);
         return width;
     }
 
-    private static float getLabelWidth(CharSequence label, Paint paint) {
-        paint.getTextBounds(label.toString(), 0, label.length(), sTextBounds);
-        return sTextBounds.width();
+    protected float getLabelWidth(CharSequence label, Paint paint) {
+        paint.getTextBounds(label.toString(), 0, label.length(), mTextBounds);
+        return mTextBounds.width();
     }
 
-    public float getDefaultLabelWidth(CharSequence label, Paint paint) {
+    public float getDefaultLabelWidth(String label, Paint paint) {
         paint.setTextSize(mKeyDrawParams.mKeyLabelSize);
         paint.setTypeface(mKeyDrawParams.mKeyTextStyle);
         return getLabelWidth(label, paint);
     }
 
-    private static void drawIcon(Canvas canvas, Drawable icon, int x, int y, int width,
+    protected static void drawIcon(Canvas canvas, Drawable icon, int x, int y, int width,
             int height) {
         canvas.translate(x, y);
         icon.setBounds(0, 0, width, height);
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index aa0f975..f5b282d 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -20,14 +20,11 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Paint.Align;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
+import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.os.Message;
 import android.text.TextUtils;
@@ -54,8 +51,6 @@
 import com.android.inputmethod.latin.Utils;
 import com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
 
-import java.util.Arrays;
-import java.util.HashMap;
 import java.util.Locale;
 import java.util.WeakHashMap;
 
@@ -72,38 +67,33 @@
 
     private static final boolean ENABLE_CAPSLOCK_BY_DOUBLETAP = true;
 
-    /* Space key and its icons, drawables and colors. */
+    // TODO: Kill process when the usability study mode was changed.
+    private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
+
+    /** Listener for {@link KeyboardActionListener}. */
+    private KeyboardActionListener mKeyboardActionListener;
+
+    /* Space key and its icons */
     private Key mSpaceKey;
     private Drawable mSpaceIcon;
-    private final boolean mIsSpacebarTriggeringPopupByLongPress;
-    private static final int SPACE_LED_LENGTH_PERCENT = 80;
-    private final boolean mAutoCorrectionSpacebarLedEnabled;
-    private final Drawable mAutoCorrectionSpacebarLedIcon;
+    // Stuff to draw language name on spacebar.
+    private boolean mNeedsToDisplayLanguage;
+    private Locale mSpacebarLocale;
+    private float mSpacebarTextFadeFactor = 0.0f;
     private final float mSpacebarTextRatio;
     private float mSpacebarTextSize;
     private final int mSpacebarTextColor;
     private final int mSpacebarTextShadowColor;
-    private final HashMap<Integer, BitmapDrawable> mSpacebarDrawableCache =
-            new HashMap<Integer, BitmapDrawable>();
-
-    private boolean mAutoCorrectionSpacebarLedOn;
-    private boolean mNeedsToDisplayLanguage;
-    private Locale mSpacebarLocale;
-    private float mSpacebarTextFadeFactor = 0.0f;
-
     // Height in space key the language name will be drawn. (proportional to space key height)
-    public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f;
+    private 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 final SuddenJumpingTouchEventHandler mTouchScreenRegulator;
-
-    // Timing constants
-    private final int mKeyRepeatInterval;
-
-    // TODO: Kill process when the usability study mode was changed.
-    private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
+    // Stuff to draw auto correction LED on spacebar.
+    private boolean mAutoCorrectionSpacebarLedOn;
+    private final boolean mAutoCorrectionSpacebarLedEnabled;
+    private final Drawable mAutoCorrectionSpacebarLedIcon;
+    private static final int SPACE_LED_LENGTH_PERCENT = 80;
 
     // Mini keyboard
     private PopupWindow mMoreKeysWindow;
@@ -111,17 +101,16 @@
     private int mMoreKeysPanelPointerTrackerId;
     private final WeakHashMap<Key, MoreKeysPanel> mMoreKeysPanelCache =
             new WeakHashMap<Key, MoreKeysPanel>();
+    private final boolean mConfigShowMiniKeyboardAtTouchedPoint;
 
-    /** Listener for {@link KeyboardActionListener}. */
-    private KeyboardActionListener mKeyboardActionListener;
+    private final boolean mIsSpacebarTriggeringPopupByLongPress;
+    private final SuddenJumpingTouchEventHandler mTouchScreenRegulator;
 
+    protected KeyDetector mKeyDetector;
     private boolean mHasDistinctMultitouch;
     private int mOldPointerCount = 1;
     private Key mOldKey;
 
-    private final boolean mConfigShowMiniKeyboardAtTouchedPoint;
-    protected KeyDetector mKeyDetector;
-
     // To detect double tap.
     protected GestureDetector mGestureDetector;
 
@@ -134,10 +123,14 @@
         private static final int MSG_IGNORE_DOUBLE_TAP = 3;
         private static final int MSG_KEY_TYPED = 4;
 
+        private final int mKeyRepeatInterval;
         private boolean mInKeyRepeat;
 
         public KeyTimerHandler(LatinKeyboardView outerInstance) {
             super(outerInstance);
+            // TODO: This should be the attribute of LatinKeyboardView.
+            final Resources res = outerInstance.getContext().getResources();
+            mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
         }
 
         @Override
@@ -147,7 +140,7 @@
             switch (msg.what) {
             case MSG_REPEAT_KEY:
                 tracker.onRepeatKey(tracker.getKey());
-                startKeyRepeatTimer(keyboardView.mKeyRepeatInterval, tracker);
+                startKeyRepeatTimer(mKeyRepeatInterval, tracker);
                 break;
             case MSG_LONGPRESS_KEY:
                 keyboardView.openMiniKeyboardIfRequired(tracker.getKey(), tracker);
@@ -213,7 +206,7 @@
         }
     }
 
-    private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
+    class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
         private boolean mProcessingShiftDoubleTapEvent = false;
 
         @Override
@@ -222,7 +215,8 @@
             if (ENABLE_CAPSLOCK_BY_DOUBLETAP && keyboard.mId.isAlphabetKeyboard()) {
                 final int pointerIndex = firstDown.getActionIndex();
                 final int id = firstDown.getPointerId(pointerIndex);
-                final PointerTracker tracker = getPointerTracker(id);
+                final PointerTracker tracker = PointerTracker.getPointerTracker(
+                        id, LatinKeyboardView.this);
                 final Key key = tracker.getKeyOn((int)firstDown.getX(), (int)firstDown.getY());
                 // If the first down event is on shift key.
                 if (key != null && key.isShift()) {
@@ -241,7 +235,8 @@
                 final MotionEvent secondDown = secondTap;
                 final int pointerIndex = secondDown.getActionIndex();
                 final int id = secondDown.getPointerId(pointerIndex);
-                final PointerTracker tracker = getPointerTracker(id);
+                final PointerTracker tracker = PointerTracker.getPointerTracker(
+                        id, LatinKeyboardView.this);
                 final Key key = tracker.getKeyOn((int)secondDown.getX(), (int)secondDown.getY());
                 // If the second down event is also on shift key.
                 if (key != null && key.isShift()) {
@@ -268,8 +263,10 @@
         mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
 
         final Resources res = getResources();
+        // TODO: This should be the attribute of LatinKeyboardView.
         mConfigShowMiniKeyboardAtTouchedPoint = res.getBoolean(
                 R.bool.config_show_mini_keyboard_at_touched_point);
+        // TODO: This should be the attribute of LatinKeyboardView.
         final float keyHysteresisDistance = res.getDimension(R.dimen.key_hysteresis_distance);
         mKeyDetector = new KeyDetector(keyHysteresisDistance);
 
@@ -280,10 +277,10 @@
 
         mHasDistinctMultitouch = context.getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
-        mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
 
         PointerTracker.init(mHasDistinctMultitouch, getContext());
 
+        // TODO: This should be the attribute of LatinKeyboardView.
         final int longPressSpaceKeyTimeout =
                 res.getInteger(R.integer.config_long_press_space_key_timeout);
         mIsSpacebarTriggeringPopupByLongPress = (longPressSpaceKeyTimeout > 0);
@@ -360,7 +357,6 @@
         final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
         mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
         mSpacebarLocale = keyboard.mId.mLocale;
-        clearSpacebarDrawableCache();
     }
 
     /**
@@ -522,10 +518,6 @@
         return true;
     }
 
-    private PointerTracker getPointerTracker(final int id) {
-        return PointerTracker.getPointerTracker(id, this);
-    }
-
     public boolean isInSlidingKeyInput() {
         if (mMoreKeysPanel != null) {
             return true;
@@ -609,7 +601,7 @@
         }
 
         if (mKeyTimerHandler.isInKeyRepeat()) {
-            final PointerTracker tracker = getPointerTracker(id);
+            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
             // Key repeating timer will be canceled if 2 or more keys are in action, and current
             // event (UP or DOWN) is non-modifier key.
             if (pointerCount > 1 && !tracker.isModifier()) {
@@ -623,7 +615,7 @@
         // multi-touch panel.
         if (nonDistinctMultitouch) {
             // Use only main (id=0) pointer tracker.
-            PointerTracker tracker = getPointerTracker(0);
+            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
             if (pointerCount == 1 && oldPointerCount == 2) {
                 // Multi-touch to single touch transition.
                 // Send a down event for the latest pointer if the key is different from the
@@ -652,7 +644,8 @@
 
         if (action == MotionEvent.ACTION_MOVE) {
             for (int i = 0; i < pointerCount; i++) {
-                final PointerTracker tracker = getPointerTracker(me.getPointerId(i));
+                final PointerTracker tracker = PointerTracker.getPointerTracker(
+                        me.getPointerId(i), this);
                 final int px, py;
                 if (mMoreKeysPanel != null
                         && tracker.mPointerId == mMoreKeysPanelPointerTrackerId) {
@@ -669,7 +662,8 @@
                 }
             }
         } else {
-            getPointerTracker(id).processMotionEvent(action, x, y, eventTime, this);
+            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
+            tracker.processMotionEvent(action, x, y, eventTime, this);
         }
 
         return true;
@@ -739,7 +733,7 @@
      */
     public boolean dispatchHoverEvent(MotionEvent event) {
         if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
-            final PointerTracker tracker = getPointerTracker(0);
+            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
             return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
         }
 
@@ -759,44 +753,30 @@
     public void updateSpacebar(float fadeFactor, boolean needsToDisplayLanguage) {
         mSpacebarTextFadeFactor = fadeFactor;
         mNeedsToDisplayLanguage = needsToDisplayLanguage;
-        updateSpacebarIcon();
         invalidateKey(mSpaceKey);
     }
 
     public void updateAutoCorrectionState(boolean isAutoCorrection) {
         if (!mAutoCorrectionSpacebarLedEnabled) return;
         mAutoCorrectionSpacebarLedOn = isAutoCorrection;
-        updateSpacebarIcon();
         invalidateKey(mSpaceKey);
     }
 
     @Override
-    /* package */ void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint,
-            KeyDrawParams params) {
+    protected void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
         super.onDrawKeyTopVisuals(key, canvas, paint, params);
 
         if (key.mCode == Keyboard.CODE_SPACE) {
+            drawSpacebar(key, canvas, paint);
+
             // Whether space key needs to show the "..." popup hint for special purposes
             if (mIsSpacebarTriggeringPopupByLongPress
                     && Utils.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) {
-                super.drawKeyPopupHint(key, canvas, paint, params);
+                drawKeyPopupHint(key, canvas, paint, params);
             }
         }
     }
 
-    // TODO: Get rid of this method and draw spacebar locale and auto correction spacebar LED
-    // in onDrawKeyTopVisuals.
-    private void updateSpacebarIcon() {
-        if (mSpaceKey == null) return;
-        if (mNeedsToDisplayLanguage) {
-            mSpaceKey.setIcon(getSpaceDrawable(mSpacebarLocale));
-        } else if (mAutoCorrectionSpacebarLedOn) {
-            mSpaceKey.setIcon(getSpaceDrawable(null));
-        } else {
-            mSpaceKey.setIcon(mSpaceIcon);
-        }
-    }
-
     private static int getSpacebarTextColor(int color, float fadeFactor) {
         final int newColor = Color.argb((int)(Color.alpha(color) * fadeFactor),
                 Color.red(color), Color.green(color), Color.blue(color));
@@ -804,24 +784,23 @@
     }
 
     // Compute width of text with specified text size using paint.
-    private static int getTextWidth(Paint paint, String text, float textSize, Rect bounds) {
+    private int getTextWidth(Paint paint, String text, float textSize) {
         paint.setTextSize(textSize);
-        paint.getTextBounds(text, 0, text.length(), bounds);
-        return bounds.width();
+        return (int)getLabelWidth(text, paint);
     }
 
     // Layout locale language name on spacebar.
-    private static String layoutSpacebar(Paint paint, Locale locale, int width,
+    private String layoutLanguageOnSpacebar(Paint paint, Locale locale, int width,
             float origTextSize) {
-        final Rect bounds = new Rect();
-
+        paint.setTextAlign(Align.CENTER);
+        paint.setTypeface(Typeface.DEFAULT);
         // Estimate appropriate language name text size to fit in maxTextWidth.
         String language = Utils.getFullDisplayName(locale, true);
-        int textWidth = getTextWidth(paint, language, origTextSize, bounds);
+        int textWidth = getTextWidth(paint, language, origTextSize);
         // Assuming text width and text size are proportional to each other.
         float textSize = origTextSize * Math.min(width / textWidth, 1.0f);
         // allow variable text size
-        textWidth = getTextWidth(paint, language, textSize, bounds);
+        textWidth = getTextWidth(paint, language, textSize);
         // If text size goes too small or text does not fit, use middle or short name
         final boolean useMiddleName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME)
                 || (textWidth > width);
@@ -829,7 +808,7 @@
         final boolean useShortName;
         if (useMiddleName) {
             language = Utils.getMiddleDisplayLanguage(locale);
-            textWidth = getTextWidth(paint, language, origTextSize, bounds);
+            textWidth = getTextWidth(paint, language, origTextSize);
             textSize = origTextSize * Math.min(width / textWidth, 1.0f);
             useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME)
                     || (textWidth > width);
@@ -839,7 +818,7 @@
 
         if (useShortName) {
             language = Utils.getShortDisplayLanguage(locale);
-            textWidth = getTextWidth(paint, language, origTextSize, bounds);
+            textWidth = getTextWidth(paint, language, origTextSize);
             textSize = origTextSize * Math.min(width / textWidth, 1.0f);
         }
         paint.setTextSize(textSize);
@@ -847,50 +826,14 @@
         return language;
     }
 
-    private Integer getSpaceDrawableKey(Locale locale) {
-        return Arrays.hashCode(new Object[] {
-                locale,
-                mAutoCorrectionSpacebarLedOn,
-                mSpacebarTextFadeFactor
-        });
-    }
-
-    private void clearSpacebarDrawableCache() {
-        for (final BitmapDrawable drawable : mSpacebarDrawableCache.values()) {
-            final Bitmap bitmap = drawable.getBitmap();
-            bitmap.recycle();
-        }
-        mSpacebarDrawableCache.clear();
-    }
-
-    private BitmapDrawable getSpaceDrawable(Locale locale) {
-        final Integer hashCode = getSpaceDrawableKey(locale);
-        final BitmapDrawable cached = mSpacebarDrawableCache.get(hashCode);
-        if (cached != null) {
-            return cached;
-        }
-        final BitmapDrawable drawable = new BitmapDrawable(getResources(), drawSpacebar(
-                locale, mAutoCorrectionSpacebarLedOn, mSpacebarTextFadeFactor));
-        mSpacebarDrawableCache.put(hashCode, drawable);
-        return drawable;
-    }
-
-    private Bitmap drawSpacebar(Locale inputLocale, boolean isAutoCorrection,
-            float textFadeFactor) {
-        final int width = mSpaceKey.mWidth;
-        final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : mSpaceKey.mHeight;
-        final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        final Canvas canvas = new Canvas(buffer);
-        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
+    private void drawSpacebar(Key key, Canvas canvas, Paint paint) {
+        final int width = key.mWidth;
+        final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : key.mHeight;
 
         // If application locales are explicitly selected.
-        if (inputLocale != null) {
-            final Paint paint = new Paint();
-            paint.setAntiAlias(true);
-            paint.setTextAlign(Align.CENTER);
-
-            final String language = layoutSpacebar(paint, inputLocale, width, mSpacebarTextSize);
-
+        if (mNeedsToDisplayLanguage) {
+            final String language = layoutLanguageOnSpacebar(paint, mSpacebarLocale, width,
+                    mSpacebarTextSize);
             // Draw language text with shadow
             // In case there is no space icon, we will place the language text at the center of
             // spacebar.
@@ -898,28 +841,25 @@
             final float textHeight = -paint.ascent() + descent;
             final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE
                     : height / 2 + textHeight / 2;
-            paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, textFadeFactor));
+            paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, mSpacebarTextFadeFactor));
             canvas.drawText(language, width / 2, baseline - descent - 1, paint);
-            paint.setColor(getSpacebarTextColor(mSpacebarTextColor, textFadeFactor));
+            paint.setColor(getSpacebarTextColor(mSpacebarTextColor, mSpacebarTextFadeFactor));
             canvas.drawText(language, width / 2, baseline - descent, paint);
         }
 
         // Draw the spacebar icon at the bottom
-        if (isAutoCorrection) {
+        if (mAutoCorrectionSpacebarLedOn) {
             final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100;
             final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight();
             int x = (width - iconWidth) / 2;
             int y = height - iconHeight;
-            mAutoCorrectionSpacebarLedIcon.setBounds(x, y, x + iconWidth, y + iconHeight);
-            mAutoCorrectionSpacebarLedIcon.draw(canvas);
+            drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight);
         } else if (mSpaceIcon != null) {
             final int iconWidth = mSpaceIcon.getIntrinsicWidth();
             final int iconHeight = mSpaceIcon.getIntrinsicHeight();
             int x = (width - iconWidth) / 2;
             int y = height - iconHeight;
-            mSpaceIcon.setBounds(x, y, x + iconWidth, y + iconHeight);
-            mSpaceIcon.draw(canvas);
+            drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight);
         }
-        return buffer;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
index faea389..b7215ec 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
@@ -40,8 +40,8 @@
     private static final KeyStyle EMPTY_KEY_STYLE = new EmptyKeyStyle();
 
     public interface KeyStyle {
-        public String[] getTextArray(TypedArray a, int index);
-        public CharSequence getText(TypedArray a, int index);
+        public String[] getStringArray(TypedArray a, int index);
+        public String getString(TypedArray a, int index);
         public int getInt(TypedArray a, int index, int defaultValue);
         public int getFlag(TypedArray a, int index, int defaultValue);
     }
@@ -52,13 +52,13 @@
         }
 
         @Override
-        public String[] getTextArray(TypedArray a, int index) {
-            return parseTextArray(a, index);
+        public String[] getStringArray(TypedArray a, int index) {
+            return parseStringArray(a, index);
         }
 
         @Override
-        public CharSequence getText(TypedArray a, int index) {
-            return a.getText(index);
+        public String getString(TypedArray a, int index) {
+            return a.getString(index);
         }
 
         @Override
@@ -71,16 +71,15 @@
             return a.getInt(index, defaultValue);
         }
 
-        protected static String[] parseTextArray(TypedArray a, int index) {
+        protected static String[] parseStringArray(TypedArray a, int index) {
             if (!a.hasValue(index))
                 return null;
-            final CharSequence text = a.getText(index);
-            return parseCsvText(text.toString(), a.getResources(), R.string.english_ime_name);
+            return parseCsvString(a.getString(index), a.getResources(), R.string.english_ime_name);
         }
     }
 
     /* package for test */
-    static String[] parseCsvText(String rawText, Resources res, int packageNameResId) {
+    static String[] parseCsvString(String rawText, Resources res, int packageNameResId) {
         final String text = Utils.resolveStringResource(rawText, res, packageNameResId);
         final int size = text.length();
         if (size == 0) {
@@ -139,15 +138,15 @@
         private final HashMap<Integer, Object> mAttributes = new HashMap<Integer, Object>();
 
         @Override
-        public String[] getTextArray(TypedArray a, int index) {
+        public String[] getStringArray(TypedArray a, int index) {
             return a.hasValue(index)
-                    ? super.getTextArray(a, index) : (String[])mAttributes.get(index);
+                    ? super.getStringArray(a, index) : (String[])mAttributes.get(index);
         }
 
         @Override
-        public CharSequence getText(TypedArray a, int index) {
+        public String getString(TypedArray a, int index) {
             return a.hasValue(index)
-                    ? super.getText(a, index) : (CharSequence)mAttributes.get(index);
+                    ? super.getString(a, index) : (String)mAttributes.get(index);
         }
 
         @Override
@@ -170,10 +169,10 @@
             // TODO: Currently not all Key attributes can be declared as style.
             readInt(keyAttr, R.styleable.Keyboard_Key_code);
             readInt(keyAttr, R.styleable.Keyboard_Key_altCode);
-            readText(keyAttr, R.styleable.Keyboard_Key_keyLabel);
-            readText(keyAttr, R.styleable.Keyboard_Key_keyOutputText);
-            readText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
-            readTextArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
+            readString(keyAttr, R.styleable.Keyboard_Key_keyLabel);
+            readString(keyAttr, R.styleable.Keyboard_Key_keyOutputText);
+            readString(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
+            readStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
             readFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags);
             readInt(keyAttr, R.styleable.Keyboard_Key_keyIcon);
             readInt(keyAttr, R.styleable.Keyboard_Key_keyIconDisabled);
@@ -183,9 +182,9 @@
             readFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
         }
 
-        private void readText(TypedArray a, int index) {
+        private void readString(TypedArray a, int index) {
             if (a.hasValue(index))
-                mAttributes.put(index, a.getText(index));
+                mAttributes.put(index, a.getString(index));
         }
 
         private void readInt(TypedArray a, int index) {
@@ -199,8 +198,8 @@
                 mAttributes.put(index, a.getInt(index, 0) | (value != null ? value : 0));
         }
 
-        private void readTextArray(TypedArray a, int index) {
-            final CharSequence[] value = parseTextArray(a, index);
+        private void readStringArray(TypedArray a, int index) {
+            final String[] value = parseStringArray(a, index);
             if (value != null)
                 mAttributes.put(index, value);
         }
diff --git a/java/src/com/android/inputmethod/latin/ComposingStateManager.java b/java/src/com/android/inputmethod/latin/ComposingStateManager.java
index 8811f20..27f509a 100644
--- a/java/src/com/android/inputmethod/latin/ComposingStateManager.java
+++ b/java/src/com/android/inputmethod/latin/ComposingStateManager.java
@@ -53,6 +53,13 @@
         }
     }
 
+    public synchronized boolean isComposing() {
+        // TODO: use the composing flag in WordComposer instead of maintaining it
+        // here separately. Even better, do away with this class and manage the auto
+        // correction indicator in the same place as the suggestions.
+        return mIsComposing;
+    }
+
     public synchronized boolean isAutoCorrectionIndicatorOn() {
         return mAutoCorrectionIndicatorOn;
     }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 31cbc4e..d59497d 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1702,10 +1702,9 @@
                 if (DEBUG) {
                     Log.d(TAG, "Flip the indicator. " + oldAutoCorrectionIndicator
                             + " -> " + newAutoCorrectionIndicator);
-                    if (newAutoCorrectionIndicator
+                    if (mComposingStateManager.isComposing() && newAutoCorrectionIndicator
                             != mComposingStateManager.isAutoCorrectionIndicatorOn()) {
-                        throw new RuntimeException("Couldn't flip the indicator! We are not "
-                                + "composing a word right now.");
+                        throw new RuntimeException("Couldn't flip the indicator!");
                     }
                 }
                 final CharSequence textWithUnderline =
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
index 2bc2cfd..db35449 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
@@ -22,7 +22,8 @@
 import java.util.TreeMap;
 
 public class SpellCheckerProximityInfo {
-    final private static int NUL = KeyDetector.NOT_A_CODE;
+    /* public for test */
+    final public static int NUL = KeyDetector.NOT_A_CODE;
 
     // This must be the same as MAX_PROXIMITY_CHARS_SIZE else it will not work inside
     // native code - this value is passed at creation of the binary object and reused
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index 3d26d97..f42b8e6 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -72,7 +72,7 @@
                 int pos = fromPos, rowStartPos = fromPos;
                 final int size = Math.min(suggestions.size(), SuggestionsView.MAX_SUGGESTIONS);
                 while (pos < size) {
-                    final CharSequence word = suggestions.getWord(pos);
+                    final String word = suggestions.getWord(pos).toString();
                     // TODO: Should take care of text x-scaling.
                     mWidths[pos] = (int)view.getDefaultLabelWidth(word, paint) + padding;
                     final int numColumn = pos - rowStartPos + 1;
diff --git a/native/src/defines.h b/native/src/defines.h
index 119a7d7..096f1fb 100644
--- a/native/src/defines.h
+++ b/native/src/defines.h
@@ -169,6 +169,7 @@
 #define NOT_VALID_WORD -99
 #define NOT_A_CHARACTER -1
 #define NOT_A_DISTANCE -1
+#define NOT_A_COORDINATE -1
 #define EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO -2
 #define PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO -3
 #define NOT_A_INDEX -1
diff --git a/native/src/proximity_info.cpp b/native/src/proximity_info.cpp
index b91957c..e0e9380 100644
--- a/native/src/proximity_info.cpp
+++ b/native/src/proximity_info.cpp
@@ -165,6 +165,9 @@
     if (!hasSweetSpotData(keyIndex)) {
         return NOT_A_DISTANCE_FLOAT;
     }
+    if (NOT_A_COORDINATE == mInputXCoordinates[inputIndex]) {
+        return NOT_A_DISTANCE_FLOAT;
+    }
     const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(keyIndex, inputIndex);
     const float squaredRadius = square(mSweetSpotRadii[keyIndex]);
     return squaredDistance / squaredRadius;
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyStylesTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyStylesTests.java
index 29881d9..2ae8027 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyStylesTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyStylesTests.java
@@ -39,7 +39,7 @@
     }
 
     private void assertTextArray(String message, String value, String ... expected) {
-        final String actual[] = KeyStyles.parseCsvText(value, mTestResources,
+        final String actual[] = KeyStyles.parseCsvString(value, mTestResources,
                 R.string.empty_string);
         if (expected.length == 0) {
             assertNull(message, actual);
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 0d5e42b..59ca22d 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -34,6 +34,11 @@
 
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardActionListener;
+import com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService; // for proximity info
+import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo;
+
+import java.util.Arrays;
+import java.util.HashMap;
 
 public class InputLogicTests extends ServiceTestCase<LatinIME> {
 
@@ -42,9 +47,29 @@
     private LatinIME mLatinIME;
     private TextView mTextView;
     private InputConnection mInputConnection;
+    private HashMap<Integer, int[]> mProximity;
 
     public InputLogicTests() {
         super(LatinIME.class);
+        mProximity = createProximity();
+    }
+
+    private static HashMap<Integer, int[]> createProximity() {
+        final HashMap<Integer, int[]> proximity = new HashMap<Integer, int[]>();
+        final int[] testProximity = SpellCheckerProximityInfo.getProximityForScript(
+                AndroidSpellCheckerService.SCRIPT_LATIN);
+        final int ROW_SIZE = SpellCheckerProximityInfo.ROW_SIZE;
+        final int NUL = SpellCheckerProximityInfo.NUL;
+        for (int row = 0; row * ROW_SIZE < testProximity.length; ++row) {
+            final int rowBase = row * ROW_SIZE;
+            int column;
+            for (column = 1; NUL != testProximity[rowBase + column]; ++column) {
+                // Do nothing, just search for a NUL element
+            }
+            proximity.put(testProximity[row * ROW_SIZE],
+                    Arrays.copyOfRange(testProximity, rowBase, rowBase + column));
+        }
+        return proximity;
     }
 
     // returns the previous setting value
@@ -73,7 +98,9 @@
         mLatinIME.onCreate();
         setDebugMode(previousDebugSetting);
         final EditorInfo ei = new EditorInfo();
+        ei.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
         final InputConnection ic = mTextView.onCreateInputConnection(ei);
+        ei.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
         final LayoutInflater inflater =
                 (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         final ViewGroup vg = new FrameLayout(getContext());
@@ -95,7 +122,11 @@
         // to keep these tests as pinpoint as possible and avoid bringing it too many dependencies,
         // but keep them in mind if something breaks. Commenting them out as is should work.
         //mLatinIME.onPressKey(codePoint);
-        mLatinIME.onCodeInput(codePoint, new int[] { codePoint },
+        int[] proximityKeys = mProximity.get(codePoint);
+        if (null == proximityKeys) {
+            proximityKeys = new int[] { codePoint };
+        }
+        mLatinIME.onCodeInput(codePoint, proximityKeys,
                 KeyboardActionListener.NOT_A_TOUCH_COORDINATE,
                 KeyboardActionListener.NOT_A_TOUCH_COORDINATE);
         //mLatinIME.onReleaseKey(codePoint, false);
@@ -139,4 +170,11 @@
         type(Keyboard.CODE_DELETE);
         assertEquals("delete selection", EXPECTED_RESULT, mTextView.getText().toString());
     }
+
+    public void testAutoCorrect() {
+        final String STRING_TO_TYPE = "tgis ";
+        final String EXPECTED_RESULT = "this ";
+        type(STRING_TO_TYPE);
+        assertEquals("simple auto-correct", EXPECTED_RESULT, mTextView.getText().toString());
+    }
 }