diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
index b512f5ac76e183a9960609b89284e05dd44fec17..b5b623223f3458618810b1eccf7e82df995bcfaa 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
@@ -21,6 +21,7 @@ import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.os.Message;
+import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.GestureDetector;
@@ -65,7 +66,8 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
 
     // Mini keyboard
     private PopupWindow mPopupWindow;
-    private PopupPanel mPopupMiniKeyboardPanel;
+    private PopupPanel mPopupPanel;
+    private int mPopupPanelPointerTrackerId;
     private final WeakHashMap<Key, PopupPanel> mPopupPanelCache =
             new WeakHashMap<Key, PopupPanel>();
 
@@ -363,15 +365,13 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
             return false;
         }
 
+        // Check if we are already displaying popup panel.
+        if (mPopupPanel != null)
+            return false;
         final Key parentKey = tracker.getKey(keyIndex);
         if (parentKey == null)
             return false;
-        boolean result = onLongPress(parentKey, tracker);
-        if (result) {
-            dismissAllKeyPreviews();
-            tracker.onLongPressed();
-        }
-        return result;
+        return onLongPress(parentKey, tracker);
     }
 
     private void onLongPressShiftKey(PointerTracker tracker) {
@@ -398,35 +398,6 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
 
         final PopupMiniKeyboardView miniKeyboardView =
                 (PopupMiniKeyboardView)container.findViewById(R.id.mini_keyboard_view);
-        miniKeyboardView.setKeyboardActionListener(new KeyboardActionListener() {
-            @Override
-            public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
-                mKeyboardActionListener.onCodeInput(primaryCode, keyCodes, x, y);
-                dismissMiniKeyboard();
-            }
-
-            @Override
-            public void onTextInput(CharSequence text) {
-                mKeyboardActionListener.onTextInput(text);
-                dismissMiniKeyboard();
-            }
-
-            @Override
-            public void onCancelInput() {
-                mKeyboardActionListener.onCancelInput();
-                dismissMiniKeyboard();
-            }
-
-            @Override
-            public void onPress(int primaryCode, boolean withSliding) {
-                mKeyboardActionListener.onPress(primaryCode, withSliding);
-            }
-            @Override
-            public void onRelease(int primaryCode, boolean withSliding) {
-                mKeyboardActionListener.onRelease(primaryCode, withSliding);
-            }
-        });
-
         final Keyboard parentKeyboard = getKeyboard();
         final Keyboard miniKeyboard = new MiniKeyboardBuilder(
                 this, parentKeyboard.getPopupKeyboardResId(), parentKey, parentKeyboard).build();
@@ -440,7 +411,7 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
 
     @Override
     protected boolean needsToDimKeyboard() {
-        return mPopupMiniKeyboardPanel != null;
+        return mPopupPanel != null;
     }
 
     /**
@@ -466,8 +437,14 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
             // Allow popup window to be drawn off the screen.
             mPopupWindow.setClippingEnabled(false);
         }
-        mPopupMiniKeyboardPanel = popupPanel;
+        mPopupPanel = popupPanel;
+        mPopupPanelPointerTrackerId = tracker.mPointerId;
+
+        tracker.onLongPressed();
         popupPanel.showPanel(this, parentKey, tracker, mPopupWindow);
+        final int translatedX = popupPanel.translateX(tracker.getLastX());
+        final int translatedY = popupPanel.translateY(tracker.getLastY());
+        tracker.onDownEvent(translatedX, translatedY, SystemClock.uptimeMillis(), popupPanel);
 
         invalidateAllKeys();
         return true;
@@ -476,15 +453,12 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
     private PointerTracker getPointerTracker(final int id) {
         final ArrayList<PointerTracker> pointers = mPointerTrackers;
         final KeyboardActionListener listener = mKeyboardActionListener;
-        final Keyboard keyboard = getKeyboard();
 
         // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
         for (int i = pointers.size(); i <= id; i++) {
             final PointerTracker tracker =
                 new PointerTracker(i, getContext(), mKeyTimerHandler, mKeyDetector, this,
                         mPointerQueue);
-            if (keyboard != null)
-                tracker.setKeyDetector(mKeyDetector);
             if (listener != null)
                 tracker.setKeyboardActionListener(listener);
             pointers.add(tracker);
@@ -494,10 +468,12 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
     }
 
     public boolean isInSlidingKeyInput() {
-        if (mPopupMiniKeyboardPanel != null) {
-            return mPopupMiniKeyboardPanel.isInSlidingKeyInput();
-        } else {
+        if (mPopupPanel != null) {
+            return true;
+        } else if (mPointerQueue != null) {
             return mPointerQueue.isInSlidingKeyInput();
+        } else {
+            return getPointerTracker(0).isInSlidingKeyInput();
         }
     }
 
@@ -521,7 +497,7 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
         }
 
         // Gesture detector must be enabled only when mini-keyboard is not on the screen.
-        if (mPopupMiniKeyboardPanel == null && mGestureDetector != null
+        if (mPopupPanel == null && mGestureDetector != null
                 && mGestureDetector.onTouchEvent(me)) {
             dismissAllKeyPreviews();
             mKeyTimerHandler.cancelKeyTimers();
@@ -531,13 +507,13 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
         final long eventTime = me.getEventTime();
         final int index = me.getActionIndex();
         final int id = me.getPointerId(index);
-        final int x = (int)me.getX(index);
-        final int y = (int)me.getY(index);
-
-        // Needs to be called after the gesture detector gets a turn, as it may have displayed the
-        // mini keyboard
-        if (mPopupMiniKeyboardPanel != null) {
-            return mPopupMiniKeyboardPanel.onTouchEvent(me);
+        final int x, y;
+        if (mPopupPanel != null && id == mPopupPanelPointerTrackerId) {
+            x = mPopupPanel.translateX((int)me.getX(index));
+            y = mPopupPanel.translateY((int)me.getY(index));
+        } else {
+            x = (int)me.getX(index);
+            y = (int)me.getY(index);
         }
 
         if (mKeyTimerHandler.isInKeyRepeat()) {
@@ -585,7 +561,15 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
         if (action == MotionEvent.ACTION_MOVE) {
             for (int i = 0; i < pointerCount; i++) {
                 final PointerTracker tracker = getPointerTracker(me.getPointerId(i));
-                tracker.onMoveEvent((int)me.getX(i), (int)me.getY(i), eventTime);
+                final int px, py;
+                if (mPopupPanel != null && tracker.mPointerId == mPopupPanelPointerTrackerId) {
+                    px = mPopupPanel.translateX((int)me.getX(i));
+                    py = mPopupPanel.translateY((int)me.getY(i));
+                } else {
+                    px = (int)me.getX(i);
+                    py = (int)me.getY(i);
+                }
+                tracker.onMoveEvent(px, py, eventTime);
             }
         } else {
             processMotionEvent(getPointerTracker(id), action, x, y, eventTime, this);
@@ -621,10 +605,11 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
         mPopupPanelCache.clear();
     }
 
-    private boolean dismissMiniKeyboard() {
+    public boolean dismissMiniKeyboard() {
         if (mPopupWindow != null && mPopupWindow.isShowing()) {
             mPopupWindow.dismiss();
-            mPopupMiniKeyboardPanel = null;
+            mPopupPanel = null;
+            mPopupPanelPointerTrackerId = -1;
             invalidateAllKeys();
             return true;
         }
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index 39d607d957c0826b9664a2f1940e2a00cb43c06f..5f5475ce828ea915a60f4df935d83d60710f5826 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -96,8 +96,10 @@ public class LatinKeyboardView extends LatinKeyboardBaseView {
     protected boolean onLongPress(Key key, PointerTracker tracker) {
         int primaryCode = key.mCode;
         if (primaryCode == Keyboard.CODE_SETTINGS) {
+            tracker.onLongPressed();
             return invokeOnKey(Keyboard.CODE_SETTINGS_LONGPRESS);
         } else if (primaryCode == '0' && getLatinKeyboard().isPhoneKeyboard()) {
+            tracker.onLongPressed();
             // Long pressing on 0 in phone number keypad gives you a '+'.
             return invokeOnKey('+');
         } else {
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 29a575ad0681bef7a2982d7e581170e85064a942..d23fb4aad3f21566570027acdf3bd2af8070208d 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -18,7 +18,6 @@ package com.android.inputmethod.keyboard;
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.os.SystemClock;
 import android.util.Log;
 
 import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
@@ -361,6 +360,7 @@ public class PointerTracker {
             printTouchEvent("onDownEvent:", x, y, eventTime);
 
         mDrawingProxy = handler.getDrawingProxy();
+        mTimerProxy = handler.getTimerProxy();
         setKeyboardActionListener(handler.getKeyboardActionListener());
         setKeyDetectorInner(handler.getKeyDetector());
         // Naive up-to-down noise filter.
@@ -598,10 +598,10 @@ public class PointerTracker {
 
     public void onLongPressed() {
         mKeyAlreadyProcessed = true;
+        setReleasedKeyGraphics();
+        dismissKeyPreview();
         final PointerTrackerQueue queue = mPointerTrackerQueue;
         if (queue != null) {
-            // TODO: Support chording + long-press input.
-            queue.releaseAllPointersExcept(this, SystemClock.uptimeMillis(), true);
             queue.remove(this);
         }
     }
diff --git a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
index a3d9c0465332681abe7e27c209189d0d4ff4ac06..af8e5956899c82b32c05bd9513aef798485cb26f 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
@@ -18,26 +18,73 @@ package com.android.inputmethod.keyboard;
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.os.SystemClock;
+import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.view.Gravity;
-import android.view.MotionEvent;
 import android.view.View;
 import android.widget.PopupWindow;
 
+import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
+import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
 import com.android.inputmethod.latin.R;
 
 /**
  * A view that renders a virtual {@link MiniKeyboard}. It handles rendering of keys and detecting
  * key presses and touch movements.
  */
-public class PopupMiniKeyboardView extends LatinKeyboardBaseView implements PopupPanel {
+public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
     private final int[] mCoordinates = new int[2];
     private final boolean mConfigShowMiniKeyboardAtTouchedPoint;
 
+    private final KeyDetector mKeyDetector;
+    private final int mVerticalCorrection;
+
+    private LatinKeyboardBaseView mParentKeyboardView;
     private int mOriginX;
     private int mOriginY;
-    private long mDownTime;
+
+    private static final TimerProxy EMPTY_TIMER_PROXY = new TimerProxy() {
+        @Override
+        public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {}
+        @Override
+        public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {}
+        @Override
+        public void startLongPressShiftTimer(long delay, int keyIndex, PointerTracker tracker) {}
+        @Override
+        public void cancelLongPressTimers() {}
+        @Override
+        public void cancelKeyTimers() {}
+    };
+
+    private final KeyboardActionListener mListner = new KeyboardActionListener() {
+        @Override
+        public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
+            mParentKeyboardView.getKeyboardActionListener()
+                    .onCodeInput(primaryCode, keyCodes, x, y);
+            mParentKeyboardView.dismissMiniKeyboard();
+        }
+
+        @Override
+        public void onTextInput(CharSequence text) {
+            mParentKeyboardView.getKeyboardActionListener().onTextInput(text);
+            mParentKeyboardView.dismissMiniKeyboard();
+        }
+
+        @Override
+        public void onCancelInput() {
+            mParentKeyboardView.getKeyboardActionListener().onCancelInput();
+            mParentKeyboardView.dismissMiniKeyboard();
+        }
+
+        @Override
+        public void onPress(int primaryCode, boolean withSliding) {
+            mParentKeyboardView.getKeyboardActionListener().onPress(primaryCode, withSliding);
+        }
+        @Override
+        public void onRelease(int primaryCode, boolean withSliding) {
+            mParentKeyboardView.getKeyboardActionListener().onRelease(primaryCode, withSliding);
+        }
+    };
 
     public PopupMiniKeyboardView(Context context, AttributeSet attrs) {
         this(context, attrs, R.attr.popupMiniKeyboardViewStyle);
@@ -46,6 +93,12 @@ public class PopupMiniKeyboardView extends LatinKeyboardBaseView implements Popu
     public PopupMiniKeyboardView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
+        final TypedArray a = context.obtainStyledAttributes(
+                attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
+        mVerticalCorrection = a.getDimensionPixelOffset(
+                R.styleable.KeyboardView_verticalCorrection, 0);
+        a.recycle();
+
         final Resources res = context.getResources();
         mConfigShowMiniKeyboardAtTouchedPoint = res.getBoolean(
                 R.bool.config_show_mini_keyboard_at_touched_point);
@@ -53,10 +106,36 @@ public class PopupMiniKeyboardView extends LatinKeyboardBaseView implements Popu
         mKeyDetector = new MiniKeyboardKeyDetector(res.getDimension(
                 R.dimen.mini_keyboard_slide_allowance));
         // Remove gesture detector on mini-keyboard
-        mGestureDetector = null;
         setKeyPreviewPopupEnabled(false, 0);
     }
 
+    @Override
+    public void setKeyboard(Keyboard keyboard) {
+        super.setKeyboard(keyboard);
+        mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
+                -getPaddingTop() + mVerticalCorrection);
+    }
+
+    @Override
+    public KeyDetector getKeyDetector() {
+        return mKeyDetector;
+    }
+
+    @Override
+    public KeyboardActionListener getKeyboardActionListener() {
+        return mListner;
+    }
+
+    @Override
+    public DrawingProxy getDrawingProxy() {
+        return  this;
+    }
+
+    @Override
+    public TimerProxy getTimerProxy() {
+        return EMPTY_TIMER_PROXY;
+    }
+
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         // Do nothing for the mini keyboard.
@@ -70,8 +149,9 @@ public class PopupMiniKeyboardView extends LatinKeyboardBaseView implements Popu
     }
 
     @Override
-    public void showPanel(KeyboardView parentKeyboardView, Key parentKey,
+    public void showPanel(LatinKeyboardBaseView parentKeyboardView, Key parentKey,
             PointerTracker tracker, PopupWindow window) {
+        mParentKeyboardView = parentKeyboardView;
         final View container = (View)getParent();
         final MiniKeyboard miniKeyboard = (MiniKeyboard)getKeyboard();
         final Keyboard parentKeyboard = parentKeyboardView.getKeyboard();
@@ -99,19 +179,15 @@ public class PopupMiniKeyboardView extends LatinKeyboardBaseView implements Popu
 
         mOriginX = x + container.getPaddingLeft() - mCoordinates[0];
         mOriginY = y + container.getPaddingTop() - mCoordinates[1];
-        mDownTime = SystemClock.uptimeMillis();
-
-        // Inject down event on the key to mini keyboard.
-        final MotionEvent downEvent = MotionEvent.obtain(mDownTime, mDownTime,
-                MotionEvent.ACTION_DOWN, pointX - mOriginX,
-                pointY + parentKey.mHeight / 2 - mOriginY, 0);
-        onTouchEvent(downEvent);
-        downEvent.recycle();
     }
 
     @Override
-    public boolean onTouchEvent(MotionEvent me) {
-        me.offsetLocation(-mOriginX, -mOriginY);
-        return super.onTouchEvent(me);
+    public int translateX(int x) {
+        return x - mOriginX;
+    }
+
+    @Override
+    public int translateY(int y) {
+        return y - mOriginY;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/PopupPanel.java b/java/src/com/android/inputmethod/keyboard/PopupPanel.java
index 2d9130fcba5149bd686559940319ac617e047410..f94d1c562c14548363aa420b81a002b409b7ea0a 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupPanel.java
+++ b/java/src/com/android/inputmethod/keyboard/PopupPanel.java
@@ -16,7 +16,6 @@
 
 package com.android.inputmethod.keyboard;
 
-import android.view.MotionEvent;
 import android.widget.PopupWindow;
 
 public interface PopupPanel extends PointerTracker.KeyEventHandler {
@@ -27,19 +26,20 @@ public interface PopupPanel extends PointerTracker.KeyEventHandler {
      * @param tracker the pointer tracker that pressesd the parent key
      * @param window PopupWindow to be used to show this popup panel
      */
-    public void showPanel(KeyboardView parentKeyboardView, Key parentKey,
+    public void showPanel(LatinKeyboardBaseView parentKeyboardView, Key parentKey,
             PointerTracker tracker, PopupWindow window);
 
     /**
-     * Check if the pointer is in siding key input mode.
-     * @return true if the pointer is sliding key input mode.
+     * Translate X-coordinate of touch event to the local X-coordinate of this PopupPanel.
+     * @param x the global X-coordinate
+     * @return the local X-coordinate to this PopupPanel
      */
-    public boolean isInSlidingKeyInput();
+    public int translateX(int x);
 
     /**
-     * The motion event handler.
-     * @param me the MotionEvent to be processed.
-     * @return true if the motion event is processed and should be consumed.
+     * Translate Y-coordinate of touch event to the local Y-coordinate of this PopupPanel.
+     * @param y the global Y-coordinate
+     * @return the local Y-coordinate to this PopupPanel
      */
-    public boolean onTouchEvent(MotionEvent me);
+    public int translateY(int y);
 }