merge in jb-mr1-release history after reset to jb-mr1-dev
diff --git a/java/res/drawable/keyboard_key_feedback.xml b/java/res/drawable/keyboard_key_feedback.xml
index 159ba86..397e948 100644
--- a/java/res/drawable/keyboard_key_feedback.xml
+++ b/java/res/drawable/keyboard_key_feedback.xml
@@ -14,9 +14,11 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_long_pressable="true"
-            android:drawable="@drawable/keyboard_key_feedback_more_background" />
-
+<selector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <item latin:state_has_morekeys="true"
+          android:drawable="@drawable/keyboard_key_feedback_more_background" />
     <item android:drawable="@drawable/keyboard_key_feedback_background" />
 </selector>
diff --git a/java/res/drawable/keyboard_key_feedback_ics.xml b/java/res/drawable/keyboard_key_feedback_ics.xml
index 04c8679..3c8850e 100644
--- a/java/res/drawable/keyboard_key_feedback_ics.xml
+++ b/java/res/drawable/keyboard_key_feedback_ics.xml
@@ -14,8 +14,23 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_long_pressable="true"
-            android:drawable="@drawable/keyboard_key_feedback_more_background_holo" />
+<selector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <!-- Left edge -->
+    <item latin:state_left_edge="true" latin:state_has_morekeys="true"
+          android:drawable="@drawable/keyboard_key_feedback_left_more_background_holo" />
+    <item latin:state_left_edge="true"
+          android:drawable="@drawable/keyboard_key_feedback_left_background_holo" />
+
+    <!-- Right edge -->
+    <item latin:state_right_edge="true" latin:state_has_morekeys="true"
+          android:drawable="@drawable/keyboard_key_feedback_right_more_background_holo" />
+    <item latin:state_right_edge="true"
+          android:drawable="@drawable/keyboard_key_feedback_right_background_holo" />
+
+    <item latin:state_has_morekeys="true"
+          android:drawable="@drawable/keyboard_key_feedback_more_background_holo" />
     <item android:drawable="@drawable/keyboard_key_feedback_background_holo" />
 </selector>
diff --git a/java/res/drawable/keyboard_key_feedback_left_ics.xml b/java/res/drawable/keyboard_key_feedback_left_ics.xml
deleted file mode 100644
index b68b37f..0000000
--- a/java/res/drawable/keyboard_key_feedback_left_ics.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_long_pressable="true"
-            android:drawable="@drawable/keyboard_key_feedback_left_more_background_holo" />
-    <item android:drawable="@drawable/keyboard_key_feedback_left_background_holo" />
-</selector>
diff --git a/java/res/drawable/keyboard_key_feedback_right_ics.xml b/java/res/drawable/keyboard_key_feedback_right_ics.xml
deleted file mode 100644
index 830678a..0000000
--- a/java/res/drawable/keyboard_key_feedback_right_ics.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_long_pressable="true"
-            android:drawable="@drawable/keyboard_key_feedback_right_more_background_holo" />
-    <item android:drawable="@drawable/keyboard_key_feedback_right_background_holo" />
-</selector>
diff --git a/java/res/layout/key_preview.xml b/java/res/layout/key_preview.xml
index 6ed892e..2fcd0c4 100644
--- a/java/res/layout/key_preview.xml
+++ b/java/res/layout/key_preview.xml
@@ -20,8 +20,8 @@
 
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="80dp"
-    android:textSize="40dp"
+    android:layout_height="wrap_content"
+    android:background="@drawable/keyboard_key_feedback"
     android:minWidth="32dp"
     android:gravity="center"
-    />
+/>
diff --git a/java/res/layout/key_preview_ics.xml b/java/res/layout/key_preview_ics.xml
new file mode 100644
index 0000000..222e884
--- /dev/null
+++ b/java/res/layout/key_preview_ics.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@drawable/keyboard_key_feedback_ics"
+    android:minWidth="32dp"
+    android:gravity="center"
+/>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index c8f6435..05c53a4 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -54,12 +54,10 @@
 
         <!-- Layout resource for key press feedback.-->
         <attr name="keyPreviewLayout" format="reference" />
-        <!-- The background for key press feedback. -->
-        <attr name="keyPreviewBackground" format="reference" />
-        <!-- The background for the left edge key press feedback. -->
-        <attr name="keyPreviewLeftBackground" format="reference" />
-        <!-- The background for the right edge key press feedback. -->
-        <attr name="keyPreviewRightBackground" format="reference" />
+        <!-- Key preview background states -->
+        <attr name="state_left_edge" format="boolean" />
+        <attr name="state_right_edge" format="boolean" />
+        <attr name="state_has_morekeys" format="boolean" />
         <!-- Vertical offset of the key press feedback from the key. -->
         <attr name="keyPreviewOffset" format="dimension" />
         <!-- Height of the key press feedback popup. -->
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 634b32a..40f1ff3 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -54,9 +54,6 @@
         <item name="keyPopupHintLetterPadding">@dimen/key_popup_hint_letter_padding</item>
         <item name="keyShiftedLetterHintPadding">@dimen/key_uppercase_letter_padding</item>
         <item name="keyPreviewLayout">@layout/key_preview</item>
-        <item name="keyPreviewBackground">@drawable/keyboard_key_feedback</item>
-        <item name="keyPreviewLeftBackground">@null</item>
-        <item name="keyPreviewRightBackground">@null</item>
         <item name="keyPreviewTextColor">#FFFFFFFF</item>
         <item name="keyPreviewOffset">@dimen/key_preview_offset</item>
         <item name="keyPreviewHeight">@dimen/key_preview_height</item>
@@ -323,9 +320,7 @@
         <item name="keyHintLabelColor">#A0FFFFFF</item>
         <item name="keyShiftedLetterHintInactivatedColor">#66E0E4E5</item>
         <item name="keyShiftedLetterHintActivatedColor">#FFFFFFFF</item>
-        <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_ics</item>
-        <item name="keyPreviewLeftBackground">@drawable/keyboard_key_feedback_left_ics</item>
-        <item name="keyPreviewRightBackground">@drawable/keyboard_key_feedback_right_ics</item>
+        <item name="keyPreviewLayout">@layout/key_preview_ics</item>
         <item name="keyPreviewTextColor">#FFFFFFFF</item>
         <item name="keyPreviewOffset">@dimen/key_preview_offset_ics</item>
         <item name="keyTextShadowColor">#00000000</item>
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 5b02f9f..ce7d797 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -59,9 +59,6 @@
  * @attr ref R.styleable#KeyboardView_keyBackground
  * @attr ref R.styleable#KeyboardView_moreKeysLayout
  * @attr ref R.styleable#KeyboardView_keyPreviewLayout
- * @attr ref R.styleable#KeyboardView_keyPreviewBackground
- * @attr ref R.styleable#KeyboardView_keyPreviewLeftBackground
- * @attr ref R.styleable#KeyboardView_keyPreviewRightBackground
  * @attr ref R.styleable#KeyboardView_keyPreviewOffset
  * @attr ref R.styleable#KeyboardView_keyPreviewHeight
  * @attr ref R.styleable#KeyboardView_keyPreviewLingerTimeout
@@ -108,9 +105,6 @@
 public class KeyboardView extends View implements PointerTracker.DrawingProxy {
     private static final String TAG = KeyboardView.class.getSimpleName();
 
-    // Miscellaneous constants
-    private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
-
     // XML attributes
     private final KeyVisualAttributes mKeyVisualAttributes;
     private final int mKeyLabelHorizontalPadding;
@@ -139,12 +133,13 @@
     private Keyboard mKeyboard;
     protected final KeyDrawParams mKeyDrawParams = new KeyDrawParams();
 
+    // Preview placer view
+    private final PreviewPlacerView mPreviewPlacerView;
+    private final int[] mCoordinates = new int[2];
+
     // 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;
@@ -152,7 +147,28 @@
     protected final KeyPreviewDrawParams mKeyPreviewDrawParams = new KeyPreviewDrawParams();
     private boolean mShowKeyPreviewPopup = true;
     private int mDelayAfterPreview;
-    private final PreviewPlacerView mPreviewPlacerView;
+    // Background state set
+    private static final int[][][] KEY_PREVIEW_BACKGROUND_STATE_TABLE = {
+        { // STATE_MIDDLE
+            EMPTY_STATE_SET,
+            { R.attr.state_has_morekeys }
+        },
+        { // STATE_LEFT
+            { R.attr.state_left_edge },
+            { R.attr.state_left_edge, R.attr.state_has_morekeys }
+        },
+        { // STATE_RIGHT
+            { R.attr.state_right_edge },
+            { R.attr.state_right_edge, R.attr.state_has_morekeys }
+        }
+    };
+    private static final int STATE_MIDDLE = 0;
+    private static final int STATE_LEFT = 1;
+    private static final int STATE_RIGHT = 2;
+    private static final int STATE_NORMAL = 0;
+    private static final int STATE_HAS_MOREKEYS = 1;
+    private static final int[] KEY_PREVIEW_BACKGROUND_DEFAULT_STATE =
+            KEY_PREVIEW_BACKGROUND_STATE_TABLE[STATE_MIDDLE][STATE_NORMAL];
 
     // Drawing
     /** True if the entire keyboard needs to be dimmed. */
@@ -230,15 +246,6 @@
                 R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
         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(
@@ -278,11 +285,6 @@
         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,
@@ -498,7 +500,7 @@
     }
 
     // Draw key background.
-    protected void onDrawKeyBackground(Key key, Canvas canvas) {
+    protected void onDrawKeyBackground(final Key key, final Canvas canvas) {
         final Rect padding = mKeyBackgroundPadding;
         final int bgWidth = key.getDrawWidth() + padding.left + padding.right;
         final int bgHeight = key.mHeight + padding.top + padding.bottom;
@@ -520,7 +522,8 @@
     }
 
     // Draw key top visuals.
-    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) {
         final int keyWidth = key.getDrawWidth();
         final int keyHeight = key.mHeight;
         final float centerX = keyWidth * 0.5f;
@@ -676,7 +679,8 @@
     }
 
     // Draw popup hint "..." at the bottom right corner of the key.
-    protected void drawKeyPopupHint(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
+    protected void drawKeyPopupHint(final Key key, final Canvas canvas, final Paint paint,
+            final KeyDrawParams params) {
         final int keyWidth = key.getDrawWidth();
         final int keyHeight = key.mHeight;
 
@@ -696,7 +700,7 @@
         }
     }
 
-    private static int getCharGeometryCacheKey(char referenceChar, Paint paint) {
+    private static int getCharGeometryCacheKey(final char referenceChar, final Paint paint) {
         final int labelSize = (int)paint.getTextSize();
         final Typeface face = paint.getTypeface();
         final int codePointOffset = referenceChar << 15;
@@ -714,7 +718,7 @@
     // Working variable for the following methods.
     private final Rect mTextBounds = new Rect();
 
-    private float getCharHeight(char[] referenceChar, Paint paint) {
+    private float getCharHeight(final char[] referenceChar, final Paint paint) {
         final int key = getCharGeometryCacheKey(referenceChar[0], paint);
         final Float cachedValue = sTextHeightCache.get(key);
         if (cachedValue != null)
@@ -726,7 +730,7 @@
         return height;
     }
 
-    private float getCharWidth(char[] referenceChar, Paint paint) {
+    private float getCharWidth(final char[] referenceChar, final Paint paint) {
         final int key = getCharGeometryCacheKey(referenceChar[0], paint);
         final Float cachedValue = sTextWidthCache.get(key);
         if (cachedValue != null)
@@ -738,36 +742,37 @@
         return width;
     }
 
-    public float getLabelWidth(String label, Paint paint) {
-        paint.getTextBounds(label.toString(), 0, label.length(), mTextBounds);
+    public float getLabelWidth(final String label, final Paint paint) {
+        paint.getTextBounds(label, 0, label.length(), mTextBounds);
         return mTextBounds.width();
     }
 
-    protected static void drawIcon(Canvas canvas, Drawable icon, int x, int y, int width,
-            int height) {
+    protected static void drawIcon(final Canvas canvas, final Drawable icon, final int x,
+            final int y, final int width, final int height) {
         canvas.translate(x, y);
         icon.setBounds(0, 0, width, height);
         icon.draw(canvas);
         canvas.translate(-x, -y);
     }
 
-    private static void drawHorizontalLine(Canvas canvas, float y, float w, int color,
-            Paint paint) {
+    private static void drawHorizontalLine(final Canvas canvas, final float y, final float w,
+            final int color, final Paint paint) {
         paint.setStyle(Paint.Style.STROKE);
         paint.setStrokeWidth(1.0f);
         paint.setColor(color);
         canvas.drawLine(0, y, w, y, paint);
     }
 
-    private static void drawVerticalLine(Canvas canvas, float x, float h, int color, Paint paint) {
+    private static void drawVerticalLine(final Canvas canvas, final float x, final float h,
+            final int color, final Paint paint) {
         paint.setStyle(Paint.Style.STROKE);
         paint.setStrokeWidth(1.0f);
         paint.setColor(color);
         canvas.drawLine(x, 0, x, h, paint);
     }
 
-    private static void drawRectangle(Canvas canvas, float x, float y, float w, float h, int color,
-            Paint paint) {
+    private static void drawRectangle(final Canvas canvas, final float x, final float y,
+            final float w, final float h, final int color, final Paint paint) {
         paint.setStyle(Paint.Style.STROKE);
         paint.setStrokeWidth(1.0f);
         paint.setColor(color);
@@ -849,7 +854,7 @@
         }
     }
 
-    public void showGestureFloatingPreviewText(String gestureFloatingPreviewText) {
+    public void showGestureFloatingPreviewText(final String gestureFloatingPreviewText) {
         locatePreviewPlacerView();
         mPreviewPlacerView.setGestureFloatingPreviewText(gestureFloatingPreviewText);
     }
@@ -865,7 +870,6 @@
         mPreviewPlacerView.invalidatePointer(tracker);
     }
 
-    @SuppressWarnings("deprecation") // setBackgroundDrawable is replaced by setBackground in API16
     @Override
     public void showKeyPreview(final PointerTracker tracker) {
         final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
@@ -886,10 +890,17 @@
         // If key is invalid or IME is already closed, we must not show key preview.
         // Trying to show key preview while root window is closed causes
         // WindowManager.BadTokenException.
-        if (key == null)
+        if (key == null) {
             return;
+        }
 
         final KeyDrawParams drawParams = mKeyDrawParams;
+        previewText.setTextColor(drawParams.mPreviewTextColor);
+        final Drawable background = previewText.getBackground();
+        if (background != null) {
+            background.setState(KEY_PREVIEW_BACKGROUND_DEFAULT_STATE);
+            background.setAlpha(PREVIEW_ALPHA);
+        }
         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) {
@@ -908,7 +919,6 @@
                     key.getPreviewIcon(mKeyboard.mIconsSet));
             previewText.setText(null);
         }
-        previewText.setBackgroundDrawable(mPreviewBackground);
 
         previewText.measure(
                 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
@@ -924,32 +934,29 @@
         // The distance between the top edge of the parent key and the bottom of the visible part
         // of the key preview background.
         previewParams.mPreviewVisibleOffset = mPreviewOffset - previewText.getPaddingBottom();
-        getLocationInWindow(previewParams.mCoordinates);
+        getLocationInWindow(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
-                + previewParams.mCoordinates[0];
+        final int statePosition;
+        int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2 + mCoordinates[0];
         if (previewX < 0) {
             previewX = 0;
-            if (mPreviewLeftBackground != null) {
-                previewText.setBackgroundDrawable(mPreviewLeftBackground);
-            }
+            statePosition = STATE_LEFT;
         } else if (previewX > getWidth() - previewWidth) {
             previewX = getWidth() - previewWidth;
-            if (mPreviewRightBackground != null) {
-                previewText.setBackgroundDrawable(mPreviewRightBackground);
-            }
+            statePosition = STATE_RIGHT;
+        } else {
+            statePosition = STATE_MIDDLE;
         }
         // The key preview is placed vertically above the top edge of the parent key with an
         // arbitrary offset.
-        final int previewY = key.mY - previewHeight + mPreviewOffset
-                + previewParams.mCoordinates[1];
+        final int previewY = key.mY - previewHeight + mPreviewOffset + mCoordinates[1];
 
-        // Set the preview background state
-        previewText.getBackground().setState(
-                key.mMoreKeys != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
-        previewText.setTextColor(drawParams.mPreviewTextColor);
+        if (background != null) {
+            final int hasMoreKeys = (key.mMoreKeys != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL;
+            background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]);
+        }
         ViewLayoutUtils.placeViewAt(
                 previewText, previewX, previewY, previewWidth, previewHeight);
         previewText.setVisibility(VISIBLE);
@@ -975,7 +982,7 @@
      * @see #invalidateAllKeys
      */
     @Override
-    public void invalidateKey(Key key) {
+    public void invalidateKey(final Key key) {
         if (mInvalidateAllKeys) return;
         if (key == null) return;
         mInvalidatedKeys.add(key);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
index 203bab6..5dcd842 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
@@ -20,7 +20,7 @@
 
 import com.android.inputmethod.latin.ResourceUtils;
 
-public class KeyDrawParams {
+public final class KeyDrawParams {
     public Typeface mTypeface;
 
     public int mLetterSize;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
index 996a722..609d1a5 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.keyboard.internal;
 
-public class KeyPreviewDrawParams {
+public final class KeyPreviewDrawParams {
     // The graphical geometry of the key preview.
     // <-width->
     // +-------+   ^
@@ -41,6 +41,4 @@
     // offset between the top edge of parent key and the bottom of the visible part of key
     // preview background.
     public int mPreviewVisibleOffset;
-
-    public final int[] mCoordinates = new int[2];
 }
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 8a509be..def978e 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -89,6 +89,10 @@
     /** Controls access to the local binary dictionary for this instance. */
     private final DictionaryController mLocalDictionaryController = new DictionaryController();
 
+    private static final int BINARY_DICT_VERSION = 1;
+    private static final BinaryDictInputOutput.FormatOptions FORMAT_OPTIONS =
+            new BinaryDictInputOutput.FormatOptions(BINARY_DICT_VERSION);
+
     /**
      * Abstract method for loading the unigrams and bigrams of a given dictionary in a background
      * thread.
@@ -310,7 +314,7 @@
         FileOutputStream out = null;
         try {
             out = new FileOutputStream(tempFile);
-            BinaryDictInputOutput.writeDictionaryBinary(out, mFusionDictionary, 1);
+            BinaryDictInputOutput.writeDictionaryBinary(out, mFusionDictionary, FORMAT_OPTIONS);
             out.flush();
             out.close();
             tempFile.renameTo(file);
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
index 942c828..81d61e9 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
@@ -19,6 +19,7 @@
 import android.util.Log;
 
 import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
+import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FormatOptions;
 import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
 import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
@@ -97,12 +98,12 @@
      */
     public static void writeDictionaryBinary(final OutputStream destination,
             final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams,
-            final int version) {
+            final FormatOptions formatOptions) {
 
         final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams);
 
         try {
-            BinaryDictInputOutput.writeDictionaryBinary(destination, fusionDict, version);
+            BinaryDictInputOutput.writeDictionaryBinary(destination, fusionDict, formatOptions);
         } catch (IOException e) {
             Log.e(TAG, "IO exception while writing file: " + e);
         } catch (UnsupportedFormatException e) {
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index abc39d9..7de5cf3 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -242,6 +242,31 @@
     }
 
     /**
+     * Options about file format.
+     */
+    public static class FormatOptions {
+        public final int mVersion;
+        public FormatOptions(final int version) {
+            mVersion = version;
+        }
+    }
+
+    /**
+     * Class representing file header.
+     */
+    private static final class FileHeader {
+        public final int mHeaderSize;
+        public final DictionaryOptions mDictionaryOptions;
+        public final FormatOptions mFormatOptions;
+        public FileHeader(final int headerSize, final DictionaryOptions dictionaryOptions,
+                final FormatOptions formatOptions) {
+            mHeaderSize = headerSize;
+            mDictionaryOptions = dictionaryOptions;
+            mFormatOptions = formatOptions;
+        }
+    }
+
+    /**
      * A class grouping utility function for our specific character encoding.
      */
     private static class CharEncoding {
@@ -1051,10 +1076,10 @@
      *
      * @param destination the stream to write the binary data to.
      * @param dict the dictionary to write.
-     * @param version the version of the format to write, currently either 1 or 2.
+     * @param formatOptions the options of file format.
      */
     public static void writeDictionaryBinary(final OutputStream destination,
-            final FusionDictionary dict, final int version)
+            final FusionDictionary dict, final FormatOptions formatOptions)
             throws IOException, UnsupportedFormatException {
 
         // Addresses are limited to 3 bytes, but since addresses can be relative to each node, the
@@ -1063,6 +1088,7 @@
         // does not have a size limit, each node must still be within 16MB of all its children and
         // parents. As long as this is ensured, the dictionary file may grow to any size.
 
+        final int version = formatOptions.mVersion;
         if (version < MINIMUM_SUPPORTED_VERSION || version > MAXIMUM_SUPPORTED_VERSION) {
             throw new UnsupportedFormatException("Requested file format version " + version
                     + ", but this implementation only supports versions "
@@ -1471,12 +1497,11 @@
             final Map<Integer, ArrayList<PendingAttribute>> bigrams) throws IOException,
             UnsupportedFormatException {
         // Read header
-        final int version = checkFormatVersion(buffer);
-        final int optionsFlags = buffer.readUnsignedShort();
-        final HashMap<String, String> options = new HashMap<String, String>();
-        final int headerSize = readHeader(buffer, options, version);
+        FormatOptions formatOptions = null;
+        DictionaryOptions dictionaryOptions = null;
+        final FileHeader header = readHeader(buffer);
 
-        readUnigramsAndBigramsBinaryInner(buffer, headerSize, words, frequencies, bigrams);
+        readUnigramsAndBigramsBinaryInner(buffer, header.mHeaderSize, words, frequencies, bigrams);
     }
 
     /**
@@ -1510,25 +1535,35 @@
 
     /**
      * Reads a header from a buffer.
+     * @param buffer the buffer to read.
      * @throws IOException
      * @throws UnsupportedFormatException
      */
-    private static int readHeader(final FusionDictionaryBufferInterface buffer,
-            final HashMap<String, String> options, final int version)
+    private static FileHeader readHeader(final FusionDictionaryBufferInterface buffer)
             throws IOException, UnsupportedFormatException {
+        final int version = checkFormatVersion(buffer);
+        final int optionsFlags = buffer.readUnsignedShort();
+
+        final HashMap<String, String> attributes = new HashMap<String, String>();
         final int headerSize;
         if (version < FIRST_VERSION_WITH_HEADER_SIZE) {
             headerSize = buffer.position();
         } else {
             headerSize = buffer.readInt();
-            populateOptions(buffer, headerSize, options);
+            populateOptions(buffer, headerSize, attributes);
             buffer.position(headerSize);
         }
 
         if (headerSize < 0) {
             throw new UnsupportedFormatException("header size can't be negative.");
         }
-        return headerSize;
+
+        final FileHeader header = new FileHeader(headerSize,
+                new FusionDictionary.DictionaryOptions(attributes,
+                        0 != (optionsFlags & GERMAN_UMLAUT_PROCESSING_FLAG),
+                        0 != (optionsFlags & FRENCH_LIGATURE_PROCESSING_FLAG)),
+                new FormatOptions(version));
+        return header;
     }
 
     /**
@@ -1569,21 +1604,14 @@
         wordCache.clear();
 
         // Read header
-        final int version = checkFormatVersion(buffer);
-        final int optionsFlags = buffer.readUnsignedShort();
-
-        final HashMap<String, String> options = new HashMap<String, String>();
-        final int headerSize = readHeader(buffer, options, version);
+        final FileHeader header = readHeader(buffer);
 
         Map<Integer, Node> reverseNodeMapping = new TreeMap<Integer, Node>();
         Map<Integer, CharGroup> reverseGroupMapping = new TreeMap<Integer, CharGroup>();
-        final Node root = readNode(
-                buffer, headerSize, reverseNodeMapping, reverseGroupMapping);
+        final Node root = readNode(buffer, header.mHeaderSize, reverseNodeMapping,
+                reverseGroupMapping);
 
-        FusionDictionary newDict = new FusionDictionary(root,
-                new FusionDictionary.DictionaryOptions(options,
-                        0 != (optionsFlags & GERMAN_UMLAUT_PROCESSING_FLAG),
-                        0 != (optionsFlags & FRENCH_LIGATURE_PROCESSING_FLAG)));
+        FusionDictionary newDict = new FusionDictionary(root, header.mDictionaryOptions);
         if (null != dict) {
             for (final Word w : dict) {
                 if (w.mIsBlacklistEntry) {
diff --git a/native/jni/src/bigram_dictionary.cpp b/native/jni/src/bigram_dictionary.cpp
index 3eea51a..dade4f1 100644
--- a/native/jni/src/bigram_dictionary.cpp
+++ b/native/jni/src/bigram_dictionary.cpp
@@ -113,7 +113,7 @@
     }
     // If still no bigrams, we really don't have them!
     if (0 == pos) return 0;
-    int bigramFlags;
+    uint8_t bigramFlags;
     int bigramCount = 0;
     do {
         bigramFlags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
@@ -153,7 +153,7 @@
             forceLowerCaseSearch);
 
     if (NOT_VALID_WORD == pos) return 0;
-    const int flags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
+    const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
     if (0 == (flags & BinaryFormat::FLAG_HAS_BIGRAMS)) return 0;
     if (0 == (flags & BinaryFormat::FLAG_HAS_MULTIPLE_CHARS)) {
         BinaryFormat::getCodePointAndForwardPointer(root, &pos);
@@ -179,7 +179,7 @@
     }
     if (0 == pos) return;
 
-    int bigramFlags;
+    uint8_t bigramFlags;
     do {
         bigramFlags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
         const int frequency = BinaryFormat::MASK_ATTRIBUTE_FREQUENCY & bigramFlags;
@@ -215,7 +215,7 @@
     int nextWordPos = BinaryFormat::getTerminalPosition(root, word2, length2,
             false /* forceLowerCaseSearch */);
     if (NOT_VALID_WORD == nextWordPos) return false;
-    int bigramFlags;
+    uint8_t bigramFlags;
     do {
         bigramFlags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
         const int bigramPos = BinaryFormat::getAttributeAddressAndForwardPointer(root, bigramFlags,
diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp
index c9a1ed0..b363dcc 100644
--- a/native/jni/src/proximity_info_state.cpp
+++ b/native/jni/src/proximity_info_state.cpp
@@ -391,11 +391,17 @@
 
 float ProximityInfoState::getPointToKeyLength(int inputIndex, int codePoint, float scale) {
     const int keyId = mProximityInfo->getKeyIndexOf(codePoint);
-    if (keyId >= 0) {
+    if (keyId != NOT_AN_INDEX) {
         const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
         return min(mDistanceCache[index] * scale, mMaxPointToKeyLength);
     }
-    return 0;
+    // TODO: Do not hardcode here
+    // No penalty to ' and -
+    if (codePoint == '\'' || codePoint == '-') {
+        return 0;
+    }
+    // If the char is not a key on the keyboard then return the max length.
+    return MAX_POINT_TO_KEY_LENGTH;
 }
 
 int ProximityInfoState::getKeyKeyDistance(int key0, int key1) {
diff --git a/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java
index 8f0551b..5fa740f 100644
--- a/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java
@@ -18,6 +18,7 @@
 
 import com.android.inputmethod.latin.UserHistoryDictIOUtils.BigramDictionaryInterface;
 import com.android.inputmethod.latin.UserHistoryDictIOUtils.OnAddWordListener;
+import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
 import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
 
@@ -44,6 +45,8 @@
     private static final int UNIGRAM_FREQUENCY = 50;
     private static final int BIGRAM_FREQUENCY = 100;
     private static final ArrayList<String> NOT_HAVE_BIGRAM = new ArrayList<String>();
+    private static final BinaryDictInputOutput.FormatOptions FORMAT_OPTIONS =
+            new BinaryDictInputOutput.FormatOptions(2);
 
     /**
      * Return same frequency for all words and bigrams
@@ -132,7 +135,7 @@
             final UserHistoryDictionaryBigramList bigramList) {
         try {
             final FileOutputStream out = new FileOutputStream(file);
-            UserHistoryDictIOUtils.writeDictionaryBinary(out, this, bigramList, 2);
+            UserHistoryDictIOUtils.writeDictionaryBinary(out, this, bigramList, FORMAT_OPTIONS);
             out.flush();
             out.close();
         } catch (IOException e) {
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictIOTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
similarity index 97%
rename from tests/src/com/android/inputmethod/latin/BinaryDictIOTests.java
rename to tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
index 9b7f4a7..6036562 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictIOTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.makedict;
 
+import com.android.inputmethod.latin.CollectionUtils;
 import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
 import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
@@ -52,6 +53,9 @@
     private static final int BIGRAM_FREQ = 50;
     private static final int TOLERANCE_OF_BIGRAM_FREQ = 5;
 
+    private static final BinaryDictInputOutput.FormatOptions VERSION2 =
+            new BinaryDictInputOutput.FormatOptions(2);
+
     private static final String[] CHARACTERS =
         {
         "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
@@ -112,7 +116,7 @@
             final FileOutputStream out = new FileOutputStream(file);
 
             now = System.currentTimeMillis();
-            BinaryDictInputOutput.writeDictionaryBinary(out, dict, 2);
+            BinaryDictInputOutput.writeDictionaryBinary(out, dict, VERSION2);
             diff = System.currentTimeMillis() - now;
 
             out.flush();
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java b/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java
index fbfc1da..f723852 100644
--- a/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java
+++ b/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java
@@ -317,8 +317,10 @@
             final FusionDictionary dict, final int version)
             throws FileNotFoundException, IOException, UnsupportedFormatException {
         final File outputFile = new File(outputFilename);
+        final BinaryDictInputOutput.FormatOptions formatOptions =
+                new BinaryDictInputOutput.FormatOptions(version);
         BinaryDictInputOutput.writeDictionaryBinary(new FileOutputStream(outputFilename), dict,
-                version);
+                formatOptions);
     }
 
     /**