Move PointerTracker.DrawingProxy to MainKeyboardView (step 1)

Change-Id: I86014de147416453503b6412eb862a2d172426aa
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 528361e..fa83378 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -57,12 +57,6 @@
         <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. -->
-        <attr name="keyPreviewHeight" format="dimension" />
-        <!-- Delay after key releasing and key press feedback dismissing in millisecond -->
-        <attr name="keyPreviewLingerTimeout" format="integer" />
 
         <!-- Amount to offset the touch Y coordinate by, for bias correction. -->
         <attr name="verticalCorrection" format="dimension" />
@@ -123,6 +117,12 @@
         <attr name="longPressShiftKeyTimeout" format="integer" />
         <!-- Ignore special key timeout while typing in millisecond. -->
         <attr name="ignoreAltCodeKeyTimeout" format="integer" />
+        <!-- Vertical offset of the key press feedback from the key. -->
+        <attr name="keyPreviewOffset" format="dimension" />
+        <!-- Height of the key press feedback popup. -->
+        <attr name="keyPreviewHeight" format="dimension" />
+        <!-- Delay after key releasing and key press feedback dismissing in millisecond -->
+        <attr name="keyPreviewLingerTimeout" format="integer" />
         <!-- Layout resource for more keys keyboard -->
         <attr name="moreKeysKeyboardLayout" format="reference" />
         <!-- More keys keyboard will shown at touched point. -->
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 9bbc5f3..5a50c16 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -55,10 +55,7 @@
         <item name="keyShiftedLetterHintPadding">@dimen/key_uppercase_letter_padding</item>
         <item name="keyPreviewLayout">@layout/key_preview</item>
         <item name="keyPreviewTextColor">@color/key_text_color_default</item>
-        <item name="keyPreviewOffset">@dimen/key_preview_offset</item>
-        <item name="keyPreviewHeight">@dimen/key_preview_height</item>
         <item name="keyPreviewTextRatio">@fraction/key_preview_text_ratio</item>
-        <item name="keyPreviewLingerTimeout">@integer/config_key_preview_linger_timeout</item>
         <item name="verticalCorrection">@dimen/keyboard_vertical_correction</item>
         <item name="keyTextShadowColor">@color/key_text_shadow_color_default</item>
         <item name="keyTextShadowRadius">2.75</item>
@@ -88,6 +85,9 @@
         <item name="longPressKeyTimeout">@integer/config_long_press_key_timeout</item>
         <item name="longPressShiftKeyTimeout">@integer/config_long_press_shift_key_timeout</item>
         <item name="ignoreAltCodeKeyTimeout">@integer/config_ignore_alt_code_key_timeout</item>
+        <item name="keyPreviewOffset">@dimen/key_preview_offset</item>
+        <item name="keyPreviewHeight">@dimen/key_preview_height</item>
+        <item name="keyPreviewLingerTimeout">@integer/config_key_preview_linger_timeout</item>
         <item name="moreKeysKeyboardLayout">@layout/more_keys_keyboard</item>
         <item name="showMoreKeysKeyboardAtTouchedPoint">@bool/config_show_more_keys_keyboard_at_touched_point</item>
         <item name="languageOnSpacebarFinalAlpha">@integer/config_language_on_spacebar_final_alpha</item>
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 5f62be8..068cfac 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -33,14 +33,12 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 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;
@@ -49,7 +47,6 @@
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
-import com.android.inputmethod.latin.StringUtils;
 import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.define.ProductionFlag;
 import com.android.inputmethod.research.ResearchLogger;
@@ -61,9 +58,6 @@
  *
  * @attr ref R.styleable#KeyboardView_keyBackground
  * @attr ref R.styleable#KeyboardView_keyPreviewLayout
- * @attr ref R.styleable#KeyboardView_keyPreviewOffset
- * @attr ref R.styleable#KeyboardView_keyPreviewHeight
- * @attr ref R.styleable#KeyboardView_keyPreviewLingerTimeout
  * @attr ref R.styleable#KeyboardView_keyLabelHorizontalPadding
  * @attr ref R.styleable#KeyboardView_keyHintLetterPadding
  * @attr ref R.styleable#KeyboardView_keyPopupHintLetterPadding
@@ -102,8 +96,7 @@
  * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintActivatedColor
  * @attr ref R.styleable#Keyboard_Key_keyPreviewTextColor
  */
-// TODO: Move PointerTracker.DrawingProxy to MainKeyboardView
-public class KeyboardView extends View implements PointerTracker.DrawingProxy {
+public class KeyboardView extends View {
     private static final String TAG = KeyboardView.class.getSimpleName();
 
     // XML attributes
@@ -139,42 +132,14 @@
     private final int[] mOriginCoords = CoordinateUtils.newInstance();
 
     // Key preview
-    private static final int PREVIEW_ALPHA = 240;
-    private final int mKeyPreviewLayoutId;
-    private final int mKeyPreviewOffset;
-    private final int mKeyPreviewHeight;
-    private final SparseArray<TextView> mKeyPreviewTexts = CollectionUtils.newSparseArray();
-    protected final KeyPreviewDrawParams mKeyPreviewDrawParams = new KeyPreviewDrawParams();
-    private boolean mShowKeyPreviewPopup = true;
-    private int mKeyPreviewLingerTimeout;
+    // TODO: Move these variable to MainKeyboardView
+    protected final int mKeyPreviewLayoutId;
+    protected final SparseArray<TextView> mKeyPreviewTexts = CollectionUtils.newSparseArray();
 
     // Gesture floating preview text
     // TODO: Make this parameter customizable by user via settings.
     private int mGestureFloatingPreviewTextLingerTimeout;
 
-    // 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. */
     private boolean mNeedsToDimEntireKeyboard;
@@ -199,7 +164,8 @@
     private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' };
     private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' };
 
-    private final DrawingHandler mDrawingHandler = new DrawingHandler(this);
+    // TODO: Move this to MainKeyboardView
+    protected final DrawingHandler mDrawingHandler = new DrawingHandler(this);
 
     public static class DrawingHandler extends StaticInnerHandlerWrapper<KeyboardView> {
         private static final int MSG_DISMISS_KEY_PREVIEW = 0;
@@ -259,12 +225,6 @@
                 R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
         mKeyBackground = keyboardViewAttr.getDrawable(R.styleable.KeyboardView_keyBackground);
         mKeyBackground.getPadding(mKeyBackgroundPadding);
-        mKeyPreviewOffset = keyboardViewAttr.getDimensionPixelOffset(
-                R.styleable.KeyboardView_keyPreviewOffset, 0);
-        mKeyPreviewHeight = keyboardViewAttr.getDimensionPixelSize(
-                R.styleable.KeyboardView_keyPreviewHeight, 80);
-        mKeyPreviewLingerTimeout = keyboardViewAttr.getInt(
-                R.styleable.KeyboardView_keyPreviewLingerTimeout, 0);
         mKeyLabelHorizontalPadding = keyboardViewAttr.getDimensionPixelOffset(
                 R.styleable.KeyboardView_keyLabelHorizontalPadding, 0);
         mKeyHintLetterPadding = keyboardViewAttr.getDimension(
@@ -277,9 +237,6 @@
                 R.styleable.KeyboardView_keyTextShadowRadius, 0.0f);
         mKeyPreviewLayoutId = keyboardViewAttr.getResourceId(
                 R.styleable.KeyboardView_keyPreviewLayout, 0);
-        if (mKeyPreviewLayoutId == 0) {
-            mShowKeyPreviewPopup = false;
-        }
         mVerticalCorrection = keyboardViewAttr.getDimension(
                 R.styleable.KeyboardView_verticalCorrection, 0);
         mBackgroundDimAlpha = keyboardViewAttr.getInt(
@@ -329,27 +286,7 @@
         return mKeyboard;
     }
 
-    /**
-     * Enables or disables the key feedback popup. This is a popup that shows a magnified
-     * version of the depressed key. By default the preview is enabled.
-     * @param previewEnabled whether or not to enable the key feedback preview
-     * @param delay the delay after which the preview is dismissed
-     * @see #isKeyPreviewPopupEnabled()
-     */
-    public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
-        mShowKeyPreviewPopup = previewEnabled;
-        mKeyPreviewLingerTimeout = delay;
-    }
-
-    /**
-     * Returns the enabled state of the key feedback preview
-     * @return whether or not the key feedback preview is enabled
-     * @see #setKeyPreviewPopupEnabled(boolean, int)
-     */
-    public boolean isKeyPreviewPopupEnabled() {
-        return mShowKeyPreviewPopup;
-    }
-
+    // TODO: Move this method to MainKeyboardView
     public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail,
             final boolean drawsGestureFloatingPreviewText) {
         mPreviewPlacerView.setGesturePreviewMode(
@@ -458,6 +395,7 @@
             }
         }
 
+        // TODO: Move this code block to MainKeyboardView.onDraw
         // Overlay a dark rectangle to dim.
         if (mNeedsToDimEntireKeyboard) {
             paint.setColor(Color.BLACK);
@@ -477,6 +415,7 @@
         mInvalidateAllKeys = false;
     }
 
+    // TODO: Move this method to MainKeyboardView.
     public void dimEntireKeyboard(final boolean dimmed) {
         final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed;
         mNeedsToDimEntireKeyboard = dimmed;
@@ -798,7 +737,8 @@
         mDrawingHandler.cancelAllMessages();
     }
 
-    private TextView getKeyPreviewText(final int pointerId) {
+    // TODO: Move this method to MainKeyboardView.
+    protected TextView getKeyPreviewText(final int pointerId) {
         TextView previewText = mKeyPreviewTexts.get(pointerId);
         if (previewText != null) {
             return previewText;
@@ -813,6 +753,7 @@
         return previewText;
     }
 
+    // TODO: Move this method to MainKeyboardView.
     private void dismissAllKeyPreviews() {
         final int pointerCount = mKeyPreviewTexts.size();
         for (int id = 0; id < pointerCount; id++) {
@@ -824,18 +765,15 @@
         PointerTracker.setReleasedKeyGraphicsToAllKeys();
     }
 
-    @Override
-    public void dismissKeyPreview(final PointerTracker tracker) {
-        mDrawingHandler.dismissKeyPreview(mKeyPreviewLingerTimeout, tracker);
-    }
-
-    private void addKeyPreview(final TextView keyPreview) {
+    // TODO: Move this to MainKeyboardView
+    protected void addKeyPreview(final TextView keyPreview) {
         locatePreviewPlacerView();
         mPreviewPlacerView.addView(
                 keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0));
     }
 
-    private void locatePreviewPlacerView() {
+    // TODO: Move this to MainKeyboardView
+    protected void locatePreviewPlacerView() {
         if (mPreviewPlacerView.getParent() != null) {
             return;
         }
@@ -866,17 +804,6 @@
         }
     }
 
-    @Override
-    public void showSlidingKeyInputPreview(final PointerTracker tracker) {
-        locatePreviewPlacerView();
-        mPreviewPlacerView.showSlidingKeyInputPreview(tracker);
-    }
-
-    @Override
-    public void dismissSlidingKeyInputPreview() {
-        mPreviewPlacerView.dismissSlidingKeyInputPreview();
-    }
-
     public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) {
         locatePreviewPlacerView();
         mPreviewPlacerView.setGestureFloatingPreviewText(suggestedWords);
@@ -887,107 +814,13 @@
         mDrawingHandler.dismissGestureFloatingPreviewText(mGestureFloatingPreviewTextLingerTimeout);
     }
 
-    @Override
+    // TODO: Move This to MainKeyboardView
     public void showGesturePreviewTrail(final PointerTracker tracker,
             final boolean isOldestTracker) {
         locatePreviewPlacerView();
         mPreviewPlacerView.invalidatePointer(tracker, isOldestTracker);
     }
 
-    @Override
-    public void showKeyPreview(final PointerTracker tracker) {
-        final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
-        if (!mShowKeyPreviewPopup) {
-            previewParams.mPreviewVisibleOffset = -mKeyboard.mVerticalGap;
-            return;
-        }
-
-        final TextView previewText = getKeyPreviewText(tracker.mPointerId);
-        // If the key preview has no parent view yet, add it to the ViewGroup which can place
-        // key preview absolutely in SoftInputWindow.
-        if (previewText.getParent() == null) {
-            addKeyPreview(previewText);
-        }
-
-        mDrawingHandler.cancelDismissKeyPreview(tracker);
-        final Key key = tracker.getKey();
-        // 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) {
-            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) {
-            // TODO Should take care of temporaryShiftLabel here.
-            previewText.setCompoundDrawables(null, null, null, null);
-            if (StringUtils.codePointCount(label) > 1) {
-                previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, drawParams.mLetterSize);
-                previewText.setTypeface(Typeface.DEFAULT_BOLD);
-            } else {
-                previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, drawParams.mPreviewTextSize);
-                previewText.setTypeface(key.selectTypeface(drawParams));
-            }
-            previewText.setText(label);
-        } else {
-            previewText.setCompoundDrawables(null, null, null,
-                    key.getPreviewIcon(mKeyboard.mIconsSet));
-            previewText.setText(null);
-        }
-
-        previewText.measure(
-                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-        final int keyDrawWidth = key.getDrawWidth();
-        final int previewWidth = previewText.getMeasuredWidth();
-        final int previewHeight = mKeyPreviewHeight;
-        // 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.
-        previewParams.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft()
-                - previewText.getPaddingRight();
-        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.
-        previewParams.mPreviewVisibleOffset = mKeyPreviewOffset - previewText.getPaddingBottom();
-        getLocationInWindow(mOriginCoords);
-        // 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.
-        final int statePosition;
-        int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
-                + CoordinateUtils.x(mOriginCoords);
-        if (previewX < 0) {
-            previewX = 0;
-            statePosition = STATE_LEFT;
-        } else if (previewX > getWidth() - previewWidth) {
-            previewX = getWidth() - previewWidth;
-            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 + mKeyPreviewOffset
-                + CoordinateUtils.y(mOriginCoords);
-
-        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);
-    }
-
     /**
      * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient
      * because the keyboard renders the keys to an off-screen buffer and an invalidate() only
@@ -1007,7 +840,6 @@
      * @param key key in the attached {@link Keyboard}.
      * @see #invalidateAllKeys
      */
-    @Override
     public void invalidateKey(final Key key) {
         if (mInvalidateAllKeys) return;
         if (key == null) return;
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index f8a5ea6..4202187 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -33,12 +33,14 @@
 import android.preference.PreferenceManager;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodSubtype;
+import android.widget.TextView;
 
 import com.android.inputmethod.accessibility.AccessibilityUtils;
 import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
@@ -46,6 +48,7 @@
 import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
 import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
 import com.android.inputmethod.keyboard.internal.KeyDrawParams;
+import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
 import com.android.inputmethod.keyboard.internal.TouchScreenRegulator;
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.CoordinateUtils;
@@ -85,6 +88,9 @@
  * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout
  * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout
  * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout
+ * @attr ref R.styleable#MainKeyboardView_keyPreviewOffset
+ * @attr ref R.styleable#MainKeyboardView_keyPreviewHeight
+ * @attr ref R.styleable#MainKeyboardView_keyPreviewLingerTimeout
  * @attr ref R.styleable#MainKeyboardView_moreKeysKeyboardLayout
  * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
  * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping
@@ -100,7 +106,8 @@
  * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
  */
 public final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
-        MoreKeysPanel.Controller, TouchScreenRegulator.ProcessMotionEvent {
+        PointerTracker.DrawingProxy, MoreKeysPanel.Controller,
+        TouchScreenRegulator.ProcessMotionEvent {
     private static final String TAG = MainKeyboardView.class.getSimpleName();
 
     // TODO: Kill process when the usability study mode was changed.
@@ -135,6 +142,15 @@
     private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
     private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
 
+    // Key preview
+    private static final int PREVIEW_ALPHA = 240;
+    private final int mKeyPreviewOffset;
+    private final int mKeyPreviewHeight;
+    private final KeyPreviewDrawParams mKeyPreviewDrawParams = new KeyPreviewDrawParams();
+    private boolean mShowKeyPreviewPopup = true;
+    private int mKeyPreviewLingerTimeout;
+    private final int[] mOriginCoords = CoordinateUtils.newInstance();
+
     // More keys keyboard
     private final WeakHashMap<Key, MoreKeysPanel> mMoreKeysPanelCache =
             new WeakHashMap<Key, MoreKeysPanel>();
@@ -436,6 +452,15 @@
         mKeyDetector = new KeyDetector(
                 keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
         mKeyTimerHandler = new KeyTimerHandler(this, mainKeyboardViewAttr);
+        mKeyPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset(
+                R.styleable.MainKeyboardView_keyPreviewOffset, 0);
+        mKeyPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize(
+                R.styleable.MainKeyboardView_keyPreviewHeight, 0);
+        mKeyPreviewLingerTimeout = mainKeyboardViewAttr.getInt(
+                R.styleable.MainKeyboardView_keyPreviewLingerTimeout, 0);
+        if (mKeyPreviewLayoutId == 0) {
+            mShowKeyPreviewPopup = false;
+        }
         mMoreKeysLayout = mainKeyboardViewAttr.getResourceId(
                 R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0);
         mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean(
@@ -546,6 +571,161 @@
         AccessibleKeyboardViewProxy.getInstance().setKeyboard();
     }
 
+    /**
+     * Enables or disables the key feedback popup. This is a popup that shows a magnified
+     * version of the depressed key. By default the preview is enabled.
+     * @param previewEnabled whether or not to enable the key feedback preview
+     * @param delay the delay after which the preview is dismissed
+     * @see #isKeyPreviewPopupEnabled()
+     */
+    public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
+        mShowKeyPreviewPopup = previewEnabled;
+        mKeyPreviewLingerTimeout = delay;
+    }
+
+    /**
+     * Returns the enabled state of the key feedback preview
+     * @return whether or not the key feedback preview is enabled
+     * @see #setKeyPreviewPopupEnabled(boolean, int)
+     */
+    public boolean isKeyPreviewPopupEnabled() {
+        return mShowKeyPreviewPopup;
+    }
+
+    // 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];
+
+    @Override
+    public void showKeyPreview(final PointerTracker tracker) {
+        final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
+        final Keyboard keyboard = getKeyboard();
+        if (!mShowKeyPreviewPopup) {
+            previewParams.mPreviewVisibleOffset = -keyboard.mVerticalGap;
+            return;
+        }
+
+        final TextView previewText = getKeyPreviewText(tracker.mPointerId);
+        // If the key preview has no parent view yet, add it to the ViewGroup which can place
+        // key preview absolutely in SoftInputWindow.
+        if (previewText.getParent() == null) {
+            addKeyPreview(previewText);
+        }
+
+        mDrawingHandler.cancelDismissKeyPreview(tracker);
+        final Key key = tracker.getKey();
+        // 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) {
+            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) {
+            // TODO Should take care of temporaryShiftLabel here.
+            previewText.setCompoundDrawables(null, null, null, null);
+            if (StringUtils.codePointCount(label) > 1) {
+                previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, drawParams.mLetterSize);
+                previewText.setTypeface(Typeface.DEFAULT_BOLD);
+            } else {
+                previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, drawParams.mPreviewTextSize);
+                previewText.setTypeface(key.selectTypeface(drawParams));
+            }
+            previewText.setText(label);
+        } else {
+            previewText.setCompoundDrawables(null, null, null,
+                    key.getPreviewIcon(keyboard.mIconsSet));
+            previewText.setText(null);
+        }
+
+        previewText.measure(
+                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+        final int keyDrawWidth = key.getDrawWidth();
+        final int previewWidth = previewText.getMeasuredWidth();
+        final int previewHeight = mKeyPreviewHeight;
+        // 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.
+        previewParams.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft()
+                - previewText.getPaddingRight();
+        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.
+        previewParams.mPreviewVisibleOffset = mKeyPreviewOffset - previewText.getPaddingBottom();
+        getLocationInWindow(mOriginCoords);
+        // 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.
+        final int statePosition;
+        int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
+                + CoordinateUtils.x(mOriginCoords);
+        if (previewX < 0) {
+            previewX = 0;
+            statePosition = STATE_LEFT;
+        } else if (previewX > getWidth() - previewWidth) {
+            previewX = getWidth() - previewWidth;
+            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 + mKeyPreviewOffset
+                + CoordinateUtils.y(mOriginCoords);
+
+        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);
+    }
+
+    @Override
+    public void dismissKeyPreview(final PointerTracker tracker) {
+        mDrawingHandler.dismissKeyPreview(mKeyPreviewLingerTimeout, tracker);
+    }
+
+    @Override
+    public void showSlidingKeyInputPreview(final PointerTracker tracker) {
+        locatePreviewPlacerView();
+        mPreviewPlacerView.showSlidingKeyInputPreview(tracker);
+    }
+
+    @Override
+    public void dismissSlidingKeyInputPreview() {
+        mPreviewPlacerView.dismissSlidingKeyInputPreview();
+    }
+
     // Note that this method is called from a non-UI thread.
     public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
         PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
@@ -611,7 +791,8 @@
 
         final MoreKeysKeyboardView moreKeysKeyboardView =
                 (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
-        final Keyboard moreKeysKeyboard = new MoreKeysKeyboard.Builder(container, parentKey, this)
+        final Keyboard moreKeysKeyboard = new MoreKeysKeyboard.Builder(
+                container, parentKey, this, mKeyPreviewDrawParams)
                 .build();
         moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
         container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
index 3826a39..5c15c4a 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
@@ -21,6 +21,7 @@
 import android.view.View;
 
 import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
 import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
 import com.android.inputmethod.keyboard.internal.KeyboardParams;
@@ -262,9 +263,11 @@
          * @param containerView the container of {@link MoreKeysKeyboardView}.
          * @param parentKey the {@link Key} that invokes more keys keyboard.
          * @param parentKeyboardView the {@link KeyboardView} that contains the parentKey.
+         * @param keyPreviewDrawParams the parameter to place key preview.
          */
         public Builder(final View containerView, final Key parentKey,
-                final KeyboardView parentKeyboardView) {
+                final MainKeyboardView parentKeyboardView,
+                final KeyPreviewDrawParams keyPreviewDrawParams) {
             super(containerView.getContext(), new MoreKeysKeyboardParams());
             final Keyboard parentKeyboard = parentKeyboardView.getKeyboard();
             load(parentKeyboard.mMoreKeysTemplate, parentKeyboard.mId);
@@ -285,8 +288,8 @@
                 // left/right/top paddings. The bottom paddings of both backgrounds don't need to
                 // be considered because the vertical positions of both backgrounds were already
                 // adjusted with their bottom paddings deducted.
-                width = parentKeyboardView.mKeyPreviewDrawParams.mPreviewVisibleWidth;
-                height = parentKeyboardView.mKeyPreviewDrawParams.mPreviewVisibleHeight
+                width = keyPreviewDrawParams.mPreviewVisibleWidth;
+                height = keyPreviewDrawParams.mPreviewVisibleHeight
                         + mParams.mVerticalGap;
             } else {
                 width = getMaxKeyWidth(parentKeyboardView, parentKey, mParams.mDefaultKeyWidth);
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index 8a5b7da..0d42ab2 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -53,7 +53,6 @@
         final Resources res = context.getResources();
         mKeyDetector = new MoreKeysDetector(
                 res.getDimension(R.dimen.more_keys_keyboard_slide_allowance));
-        setKeyPreviewPopupEnabled(false, 0);
     }
 
     @Override
@@ -76,13 +75,6 @@
     }
 
     @Override
-    public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
-        // More keys keyboard needs no pop-up key preview displayed, so we pass always false with a
-        // delay of 0. The delay does not matter actually since the popup is not shown anyway.
-        super.setKeyPreviewPopupEnabled(false, 0);
-    }
-
-    @Override
     public void showMoreKeysPanel(final View parentView, final Controller controller,
             final int pointX, final int pointY, final KeyboardActionListener listener) {
         mController = controller;