Merge "Change symbol on space bar when autocompletion is activated" into gingerbread
diff --git a/java/res/drawable-hdpi/btn_close_normal.png b/java/res/drawable-hdpi/btn_close_normal.png
deleted file mode 100644
index 38b49f1..0000000
--- a/java/res/drawable-hdpi/btn_close_normal.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_close_pressed.png b/java/res/drawable-hdpi/btn_close_pressed.png
deleted file mode 100644
index aa9ea49..0000000
--- a/java/res/drawable-hdpi/btn_close_pressed.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_close_selected.png b/java/res/drawable-hdpi/btn_close_selected.png
deleted file mode 100644
index 870c670..0000000
--- a/java/res/drawable-hdpi/btn_close_selected.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_close_normal.png b/java/res/drawable-mdpi/btn_close_normal.png
deleted file mode 100644
index 4c6e79d..0000000
--- a/java/res/drawable-mdpi/btn_close_normal.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_close_pressed.png b/java/res/drawable-mdpi/btn_close_pressed.png
deleted file mode 100644
index fc983af..0000000
--- a/java/res/drawable-mdpi/btn_close_pressed.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_close_selected.png b/java/res/drawable-mdpi/btn_close_selected.png
deleted file mode 100644
index f2bf91a..0000000
--- a/java/res/drawable-mdpi/btn_close_selected.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable/btn_close.xml b/java/res/drawable/btn_close.xml
deleted file mode 100644
index ee58138..0000000
--- a/java/res/drawable/btn_close.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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_pressed="false" android:state_focused="false"
-        android:drawable="@drawable/btn_close_normal" />
-
-    <item android:state_pressed="true"
-        android:drawable="@drawable/btn_close_pressed" />
-
-    <item android:state_focused="true"
-        android:drawable="@drawable/btn_close_selected" />
-</selector>
diff --git a/java/res/layout/input_gingerbread_popup.xml b/java/res/layout/input_gingerbread_popup.xml
index 0f0f080..0cc953e 100755
--- a/java/res/layout/input_gingerbread_popup.xml
+++ b/java/res/layout/input_gingerbread_popup.xml
@@ -36,13 +36,4 @@
             latin:keyBackground="@drawable/btn_keyboard_key_gingerbread"
             latin:popupLayout="@layout/input_gingerbread_popup"
         />
-    <ImageButton android:id="@+id/closeButton"
-        android:background="@android:color/transparent"
-        android:src="@drawable/btn_close"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:layout_marginLeft="8dp"
-        android:clickable="true"
-        />
 </LinearLayout>
diff --git a/java/res/layout/input_stone_popup.xml b/java/res/layout/input_stone_popup.xml
index 1efa56c..f159625 100755
--- a/java/res/layout/input_stone_popup.xml
+++ b/java/res/layout/input_stone_popup.xml
@@ -38,13 +38,4 @@
             latin:shadowColor="@color/latinkeyboard_key_color_white"
             latin:popupLayout="@layout/input_stone_popup"
         />
-    <ImageButton android:id="@+id/closeButton"
-        android:background="@android:color/transparent"
-        android:src="@drawable/btn_close"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:layout_marginLeft="8dp"
-        android:clickable="true"
-        />
 </LinearLayout>
diff --git a/java/res/layout/keyboard_popup_keyboard.xml b/java/res/layout/keyboard_popup_keyboard.xml
index 0b0046a..6d39850 100644
--- a/java/res/layout/keyboard_popup_keyboard.xml
+++ b/java/res/layout/keyboard_popup_keyboard.xml
@@ -36,13 +36,4 @@
             latin:keyPreviewLayout="@layout/keyboard_key_preview"
             latin:popupLayout="@layout/keyboard_popup_keyboard"
             />
-    <ImageButton android:id="@+id/closeButton"
-        android:background="@android:color/transparent"
-        android:src="@drawable/btn_close"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:layout_marginLeft="8dp"
-        android:clickable="true"
-        />
 </LinearLayout>
diff --git a/java/res/values-en/donottranslate.xml b/java/res/values-en/donottranslate.xml
new file mode 100644
index 0000000..f569273
--- /dev/null
+++ b/java/res/values-en/donottranslate.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Accented forms of "e" -->
+    <string name="alternates_for_e">èéêë3</string>
+    <!-- Accented forms of "i" -->
+    <string name="alternates_for_i">ìíîï8</string>
+    <!-- Accented forms of "o" -->
+    <string name="alternates_for_o">òóôõöœø9</string>
+    <!-- Accented forms of "u" -->
+    <string name="alternates_for_u">ùúûü7</string>
+    <!-- Accented forms of "y" -->
+    <string name="alternates_for_y">ýÿ6</string>
+</resources>
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index b7bfd9c..97b645c 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -24,16 +24,22 @@
     <string name="sentence_separators">.,!?)</string>
     <!-- Symbols that are suggested between words -->
     <string name="suggested_punctuations">!?,\u0022\u0027:()-/@_</string>
+    <!-- Accented characters related to "q" -->
+    <string name="alternates_for_q">1</string>
+    <!-- Accented characters related to "w" -->
+    <string name="alternates_for_w">2</string>
     <!-- Accented characters related to "d" -->
     <string name="alternates_for_d"></string>
     <!-- Accented characters related to "r" -->
-    <string name="alternates_for_r"></string>
+    <string name="alternates_for_r">4</string>
     <!-- Accented characters related to "t" -->
-    <string name="alternates_for_t"></string>
+    <string name="alternates_for_t">5</string>
     <!-- Accented characters related to "z" -->
     <string name="alternates_for_z"></string>
     <!-- Accented characters related to "l" -->
     <string name="alternates_for_l"></string>
     <!-- Accented characters related to "g" -->
     <string name="alternates_for_g"></string>
+    <!-- Accented characters related to "p" -->
+    <string name="alternates_for_p">0</string>
 </resources>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 24fee02..16478c8 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -22,7 +22,7 @@
         <item name="keyTextSize">@dimen/key_text_size</item>
         <item name="keyTextColor">#FFFFFFFF</item>
         <item name="keyPreviewLayout">@layout/keyboard_key_preview</item>
-        <item name="keyPreviewOffset">-12dip</item>
+        <item name="keyPreviewOffset">5dip</item>
         <item name="keyPreviewHeight">80dip</item>
         <item name="labelTextSize">14sp</item>
         <item name="popupLayout">@layout/keyboard_popup_keyboard</item>
diff --git a/java/res/xml/kbd_qwerty.xml b/java/res/xml/kbd_qwerty.xml
index 0a0691d..0aeaf6c 100755
--- a/java/res/xml/kbd_qwerty.xml
+++ b/java/res/xml/kbd_qwerty.xml
@@ -29,18 +29,26 @@
     >
 
     <Row>
-        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
-        <Key android:codes="119" android:keyLabel="w"/>
+        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_q"
+        />
+        <Key android:codes="119" android:keyLabel="w"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_w"
+        />
         <Key android:codes="101" android:keyLabel="e"
                 android:popupKeyboard="@xml/kbd_popup_template"
                 android:popupCharacters="@string/alternates_for_e"
         />
         <Key android:codes="114" android:keyLabel="r"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_r"/>
+                android:popupCharacters="@string/alternates_for_r"
+        />
         <Key android:codes="116" android:keyLabel="t"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_t"/>
+                android:popupCharacters="@string/alternates_for_t"
+        />
         <Key android:codes="121" android:keyLabel="y"
                 android:popupKeyboard="@xml/kbd_popup_template"
                 android:popupCharacters="@string/alternates_for_y"
@@ -57,7 +65,10 @@
                 android:popupKeyboard="@xml/kbd_popup_template"
                 android:popupCharacters="@string/alternates_for_o"
         />
-        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_p"
+        />
     </Row>
 
     <Row>
diff --git a/java/res/xml/kbd_qwerty_black.xml b/java/res/xml/kbd_qwerty_black.xml
index 2865472..8b68d75 100755
--- a/java/res/xml/kbd_qwerty_black.xml
+++ b/java/res/xml/kbd_qwerty_black.xml
@@ -26,18 +26,26 @@
     >
 
     <Row>
-        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
-        <Key android:codes="119" android:keyLabel="w"/>
+        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_q"
+        />
+        <Key android:codes="119" android:keyLabel="w"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_w"
+        />
         <Key android:codes="101" android:keyLabel="e"
                 android:popupKeyboard="@xml/kbd_popup_template"
                 android:popupCharacters="@string/alternates_for_e"
         />
         <Key android:codes="114" android:keyLabel="r"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_r"/>
+                android:popupCharacters="@string/alternates_for_r"
+        />
         <Key android:codes="116" android:keyLabel="t"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_t"/>
+                android:popupCharacters="@string/alternates_for_t"
+        />
         <Key android:codes="121" android:keyLabel="y"
                 android:popupKeyboard="@xml/kbd_popup_template"
                 android:popupCharacters="@string/alternates_for_y"
@@ -54,7 +62,10 @@
                 android:popupKeyboard="@xml/kbd_popup_template"
                 android:popupCharacters="@string/alternates_for_o"
         />
-        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_p"
+        />
     </Row>
 
     <Row>
diff --git a/java/src/com/android/inputmethod/latin/KeyDetector.java b/java/src/com/android/inputmethod/latin/KeyDetector.java
index 11d5f86..e443f27 100644
--- a/java/src/com/android/inputmethod/latin/KeyDetector.java
+++ b/java/src/com/android/inputmethod/latin/KeyDetector.java
@@ -19,18 +19,23 @@
 import android.inputmethodservice.Keyboard;
 import android.inputmethodservice.Keyboard.Key;
 
+import java.util.Arrays;
 import java.util.List;
 
 abstract class KeyDetector {
     protected Keyboard mKeyboard;
-    protected Key[] mKeys;
+    private Key[] mKeys;
 
+    protected int mCorrectionX;
+    protected int mCorrectionY;
     protected boolean mProximityCorrectOn;
     protected int mProximityThresholdSquare;
 
-    public Key[] setKeyboard(Keyboard keyboard) {
+    public Key[] setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
         if (keyboard == null)
             throw new NullPointerException();
+        mCorrectionX = (int)correctionX;
+        mCorrectionY = (int)correctionY;
         mKeyboard = keyboard;
         List<Key> keys = mKeyboard.getKeys();
         Key[] array = keys.toArray(new Key[keys.size()]);
@@ -38,6 +43,21 @@
         return array;
     }
 
+    protected int getTouchX(int x) {
+        return x + mCorrectionX;
+    }
+
+    protected int getTouchY(int y) {
+        return y + mCorrectionY;
+    }
+
+    protected Key[] getKeys() {
+        if (mKeys == null)
+            throw new IllegalStateException("keyboard isn't set");
+        // mKeyboard is guaranteed not null at setKeybaord() method
+        return mKeys;
+    }
+
     public void setProximityCorrectionEnabled(boolean enabled) {
         mProximityCorrectOn = enabled;
     }
@@ -50,7 +70,13 @@
         mProximityThresholdSquare = threshold * threshold;
     }
 
-    abstract public int[] newCodeArray();
+    public int[] newCodeArray() {
+        int[] codes = new int[getMaxNearbyKeys()];
+        Arrays.fill(codes, LatinKeyboardBaseView.NOT_A_KEY);
+        return codes;
+    }
+
+    abstract protected int getMaxNearbyKeys();
 
     abstract public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys);
 }
\ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
index 30042d8..ba901ed 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
@@ -62,8 +62,7 @@
  * @attr ref R.styleable#LatinKeyboardBaseView_verticalCorrection
  * @attr ref R.styleable#LatinKeyboardBaseView_popupLayout
  */
-public class LatinKeyboardBaseView extends View implements View.OnClickListener,
-        PointerTracker.UIProxy {
+public class LatinKeyboardBaseView extends View implements PointerTracker.UIProxy {
     private static final String TAG = "LatinKeyboardBaseView";
     private static final boolean DEBUG = false;
 
@@ -165,7 +164,7 @@
     private float mShadowRadius;
     private Drawable mKeyBackground;
     private float mBackgroundDimAmount;
-    private int mVerticalCorrection;
+    private float mVerticalCorrection;
     private int mPreviewOffset;
     private int mPreviewHeight;
     private int mPopupLayout;
@@ -185,17 +184,18 @@
     private boolean mShowTouchPoints = true;
     private int mPopupPreviewX;
     private int mPopupPreviewY;
+    private int mPopupPreviewOffsetX;
+    private int mPopupPreviewOffsetY;
     private int mWindowY;
 
     // Popup mini keyboard
-    private PopupWindow mPopupKeyboard;
-    private View mMiniKeyboardContainer;
+    private PopupWindow mMiniKeyboardPopup;
     private LatinKeyboardBaseView mMiniKeyboard;
-    private boolean mMiniKeyboardOnScreen;
-    private View mPopupParent;
-    private int mMiniKeyboardOffsetX;
-    private int mMiniKeyboardOffsetY;
+    private View mMiniKeyboardParent;
     private Map<Key,View> mMiniKeyboardCache;
+    private int mMiniKeyboardOriginX;
+    private int mMiniKeyboardOriginY;
+    private long mMiniKeyboardPopupTime;
     private int[] mWindowOffset;
 
     /** Listener for {@link OnKeyboardActionListener}. */
@@ -254,9 +254,11 @@
                     startKeyRepeatTimer(REPEAT_INTERVAL, msg.arg1, tracker);
                     break;
                 }
-                case MSG_LONGPRESS_KEY:
-                    openPopupIfRequired(msg.arg1);
+                case MSG_LONGPRESS_KEY: {
+                    final PointerTracker tracker = (PointerTracker)msg.obj;
+                    openPopupIfRequired(msg.arg1, tracker);
                     break;
+                }
             }
         }
 
@@ -299,9 +301,9 @@
             return mInKeyRepeat;
         }
 
-        public void startLongPressTimer(int keyIndex, long delay) {
+        public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {
             removeMessages(MSG_LONGPRESS_KEY);
-            sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, keyIndex, 0), delay);
+            sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, keyIndex, 0, tracker), delay);
         }
 
         public void cancelLongPressTimer() {
@@ -443,10 +445,10 @@
             mShowPreview = false;
         }
         mPreviewPopup.setTouchable(false);
-        mPopupParent = this;
+        mMiniKeyboardParent = this;
 
-        mPopupKeyboard = new PopupWindow(context);
-        mPopupKeyboard.setBackgroundDrawable(null);
+        mMiniKeyboardPopup = new PopupWindow(context);
+        mMiniKeyboardPopup.setBackgroundDrawable(null);
 
         mPaint = new Paint();
         mPaint.setAntiAlias(true);
@@ -539,7 +541,8 @@
         mHandler.cancelPopupPreview();
         mKeyboard = keyboard;
         LatinImeLogger.onSetKeyboard(keyboard);
-        mKeys = mKeyDetector.setKeyboard(keyboard);
+        mKeys = mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
+                -getPaddingTop() + mVerticalCorrection);
         for (PointerTracker tracker : mPointerTrackers) {
             tracker.setKeyboard(mKeys, mDebounceHysteresis);
         }
@@ -611,16 +614,13 @@
         return mSymbolColorScheme;
     }
 
-    public void setVerticalCorrection(int verticalOffset) {
-    }
-
     public void setPopupParent(View v) {
-        mPopupParent = v;
+        mMiniKeyboardParent = v;
     }
 
     public void setPopupOffset(int x, int y) {
-        mMiniKeyboardOffsetX = x;
-        mMiniKeyboardOffsetY = y;
+        mPopupPreviewOffsetX = x;
+        mPopupPreviewOffsetY = y;
         if (mPreviewPopup.isShowing()) {
             mPreviewPopup.dismiss();
         }
@@ -643,14 +643,6 @@
         return mKeyDetector.isProximityCorrectionEnabled();
     }
 
-    /**
-     * Popup keyboard close button clicked.
-     * @hide
-     */
-    public void onClick(View v) {
-        dismissPopupKeyboard();
-    }
-
     protected CharSequence adjustCase(CharSequence label) {
         if (mKeyboard.isShifted() && label != null && label.length() < 3
                 && Character.isLowerCase(label.charAt(0))) {
@@ -805,7 +797,7 @@
         }
         mInvalidatedKey = null;
         // Overlay a dark rectangle to dim the keyboard
-        if (mMiniKeyboardOnScreen) {
+        if (mMiniKeyboard != null) {
             paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
             canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
         }
@@ -896,8 +888,8 @@
         if (mOffsetInWindow == null) {
             mOffsetInWindow = new int[2];
             getLocationInWindow(mOffsetInWindow);
-            mOffsetInWindow[0] += mMiniKeyboardOffsetX; // Offset may be zero
-            mOffsetInWindow[1] += mMiniKeyboardOffsetY; // Offset may be zero
+            mOffsetInWindow[0] += mPopupPreviewOffsetX; // Offset may be zero
+            mOffsetInWindow[1] += mPopupPreviewOffsetY; // Offset may be zero
             int[] mWindowLocation = new int[2];
             getLocationOnScreen(mWindowLocation);
             mWindowY = mWindowLocation[1];
@@ -926,7 +918,7 @@
         } else {
             previewPopup.setWidth(popupWidth);
             previewPopup.setHeight(popupHeight);
-            previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY,
+            previewPopup.showAtLocation(mMiniKeyboardParent, Gravity.NO_GRAVITY,
                     mPopupPreviewX, mPopupPreviewY);
         }
         mPreviewText.setVisibility(VISIBLE);
@@ -962,18 +954,18 @@
                 key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop());
     }
 
-    private boolean openPopupIfRequired(int keyIndex) {
+    private boolean openPopupIfRequired(int keyIndex, PointerTracker tracker) {
         // Check if we have a popup layout specified first.
         if (mPopupLayout == 0) {
             return false;
         }
-        if (keyIndex < 0 || keyIndex >= mKeys.length) {
-            return false;
-        }
 
-        Key popupKey = mKeys[keyIndex];
+        Key popupKey = tracker.getKey(keyIndex);
+        if (popupKey == null)
+            return false;
         boolean result = onLongPress(popupKey);
         if (result) {
+            tracker.setAlreadyProcessed();
             dismissKeyPreview();
         }
         return result;
@@ -990,15 +982,13 @@
         int popupKeyboardId = popupKey.popupResId;
 
         if (popupKeyboardId != 0) {
-            mMiniKeyboardContainer = mMiniKeyboardCache.get(popupKey);
-            if (mMiniKeyboardContainer == null) {
+            View container = mMiniKeyboardCache.get(popupKey);
+            if (container == null) {
                 LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
                         Context.LAYOUT_INFLATER_SERVICE);
-                mMiniKeyboardContainer = inflater.inflate(mPopupLayout, null);
-                mMiniKeyboard = (LatinKeyboardBaseView) mMiniKeyboardContainer.findViewById(
+                container = inflater.inflate(mPopupLayout, null);
+                mMiniKeyboard = (LatinKeyboardBaseView) container.findViewById(
                        R.id.LatinKeyboardBaseView);
-                View closeButton = mMiniKeyboardContainer.findViewById(R.id.closeButton);
-                if (closeButton != null) closeButton.setOnClickListener(this);
                 mMiniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() {
                     public void onKey(int primaryCode, int[] keyCodes, int x, int y) {
                         mKeyboardActionListener.onKey(primaryCode, keyCodes, x, y);
@@ -1021,7 +1011,7 @@
                         mKeyboardActionListener.onRelease(primaryCode);
                     }
                 });
-                //mInputView.setSuggest(mSuggest);
+
                 Keyboard keyboard;
                 if (popupKey.popupCharacters != null) {
                     keyboard = new Keyboard(getContext(), popupKeyboardId,
@@ -1031,50 +1021,54 @@
                 }
                 mMiniKeyboard.setKeyboard(keyboard);
                 mMiniKeyboard.setPopupParent(this);
-                mMiniKeyboardContainer.measure(
+                container.measure(
                         MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
                         MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
 
-                mMiniKeyboardCache.put(popupKey, mMiniKeyboardContainer);
+                mMiniKeyboardCache.put(popupKey, container);
             } else {
-                mMiniKeyboard = (LatinKeyboardBaseView) mMiniKeyboardContainer.findViewById(
+                mMiniKeyboard = (LatinKeyboardBaseView) container.findViewById(
                         R.id.LatinKeyboardBaseView);
             }
             if (mWindowOffset == null) {
                 mWindowOffset = new int[2];
                 getLocationInWindow(mWindowOffset);
             }
-            int popupX = popupKey.x + getPaddingLeft();
+            int popupX = popupKey.x + popupKey.width + getPaddingLeft();
             int popupY = popupKey.y + getPaddingTop();
-            popupX = popupX + popupKey.width - mMiniKeyboardContainer.getMeasuredWidth();
-            popupY = popupY - mMiniKeyboardContainer.getMeasuredHeight();
-            final int x = popupX + mMiniKeyboardContainer.getPaddingRight() + mWindowOffset[0];
-            final int y = popupY + mMiniKeyboardContainer.getPaddingBottom() + mWindowOffset[1];
-            mMiniKeyboard.setPopupOffset(x < 0 ? 0 : x, y);
+            popupX -= container.getMeasuredWidth();
+            popupY -= container.getMeasuredHeight();
+            popupX += mWindowOffset[0];
+            popupY += mWindowOffset[1];
+            final int x = popupX + container.getPaddingRight();
+            final int y = popupY + container.getPaddingBottom();
+            mMiniKeyboardOriginX = (x < 0 ? 0 : x) + container.getPaddingLeft();
+            mMiniKeyboardOriginY = y + container.getPaddingTop();
+            mMiniKeyboard.setPopupOffset((x < 0) ? 0 : x, y);
             mMiniKeyboard.setShifted(isShifted());
-            mPopupKeyboard.setContentView(mMiniKeyboardContainer);
-            mPopupKeyboard.setWidth(mMiniKeyboardContainer.getMeasuredWidth());
-            mPopupKeyboard.setHeight(mMiniKeyboardContainer.getMeasuredHeight());
-            mPopupKeyboard.showAtLocation(this, Gravity.NO_GRAVITY, x, y);
-            mMiniKeyboardOnScreen = true;
-            //mMiniKeyboard.onTouchEvent(getTranslatedEvent(me));
+            mMiniKeyboard.setPreviewEnabled(isPreviewEnabled());
+            mMiniKeyboardPopup.setContentView(container);
+            mMiniKeyboardPopup.setWidth(container.getMeasuredWidth());
+            mMiniKeyboardPopup.setHeight(container.getMeasuredHeight());
+            mMiniKeyboardPopup.showAtLocation(this, Gravity.NO_GRAVITY, x, y);
+
+            // Inject down event on the key to mini keyboard.
+            long eventTime = System.currentTimeMillis();
+            mMiniKeyboardPopupTime = eventTime;
+            MotionEvent downEvent = generateMiniKeyboardMotionEvent(MotionEvent.ACTION_DOWN,
+                    popupKey.x + popupKey.width / 2, popupKey.y + popupKey.height / 2, eventTime);
+            mMiniKeyboard.onTouchEvent(downEvent);
+            downEvent.recycle();
+
             invalidateAllKeys();
             return true;
         }
         return false;
     }
 
-    // TODO: Should cleanup after refactoring mini-keyboard. 
-    public boolean isMiniKeyboardOnScreen() {
-        return mMiniKeyboardOnScreen;
-    }
-
-    private int getTouchX(float x) {
-        return (int)x - getPaddingLeft();
-    }
-
-    private int getTouchY(float y) {
-        return (int)y + mVerticalCorrection - getPaddingTop();
+    private MotionEvent generateMiniKeyboardMotionEvent(int action, int x, int y, long eventTime) {
+        return MotionEvent.obtain(mMiniKeyboardPopupTime, eventTime, action,
+                x - mMiniKeyboardOriginX, y - mMiniKeyboardOriginY, 0);
     }
 
     private PointerTracker getPointerTracker(final int id) {
@@ -1106,7 +1100,7 @@
         mSwipeTracker.addMovement(me);
 
         // We must disable gesture detector while mini-keyboard is on the screen.
-        if (!mMiniKeyboardOnScreen && mGestureDetector.onTouchEvent(me)) {
+        if (mMiniKeyboard == null && mGestureDetector.onTouchEvent(me)) {
             dismissKeyPreview();
             mHandler.cancelKeyTimers();
             return true;
@@ -1114,7 +1108,11 @@
 
         // Needs to be called after the gesture detector gets a turn, as it may have
         // displayed the mini keyboard
-        if (mMiniKeyboardOnScreen && action != MotionEvent.ACTION_CANCEL) {
+        if (mMiniKeyboard != null) {
+            MotionEvent translated = generateMiniKeyboardMotionEvent(action, (int)me.getX(),
+                    (int)me.getY(), eventTime);
+            mMiniKeyboard.onTouchEvent(translated);
+            translated.recycle();
             return true;
         }
 
@@ -1131,29 +1129,29 @@
 
         if (action == MotionEvent.ACTION_MOVE) {
             for (int index = 0; index < pointerCount; index++) {
-                int touchX = getTouchX(me.getX(index));
-                int touchY = getTouchY(me.getY(index));
+                int x = (int)me.getX(index);
+                int y = (int)me.getY(index);
                 int id = me.getPointerId(index);
                 PointerTracker tracker = getPointerTracker(id);
-                tracker.onMoveEvent(touchX, touchY, eventTime);
+                tracker.onMoveEvent(x, y, eventTime);
             }
         } else {
             int index = me.getActionIndex();
-            int touchX = getTouchX(me.getX(index));
-            int touchY = getTouchY(me.getY(index));
+            int x = (int)me.getX(index);
+            int y = (int)me.getY(index);
             int id = me.getPointerId(index);
             PointerTracker tracker = getPointerTracker(id);
             switch (action) {
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_POINTER_DOWN:
-                onDownEvent(tracker, touchX, touchY, eventTime);
+                onDownEvent(tracker, x, y, eventTime);
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_POINTER_UP:
-                onUpEvent(tracker, touchX, touchY, eventTime);
+                onUpEvent(tracker, x, y, eventTime);
                 break;
             case MotionEvent.ACTION_CANCEL:
-                onCancelEvent(tracker, touchX, touchY, eventTime);
+                onCancelEvent(tracker, x, y, eventTime);
                 break;
             }
         }
@@ -1161,12 +1159,12 @@
         return true;
     }
 
-    private void onDownEvent(PointerTracker tracker, int touchX, int touchY, long eventTime) {
-        tracker.onDownEvent(touchX, touchY, eventTime);
+    private void onDownEvent(PointerTracker tracker, int x, int y, long eventTime) {
+        tracker.onDownEvent(x, y, eventTime);
         mPointerQueue.add(tracker);
     }
 
-    private void onUpEvent(PointerTracker tracker, int touchX, int touchY, long eventTime) {
+    private void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) {
         int index = mPointerQueue.lastIndexOf(tracker);
         if (index >= 0) {
             mPointerQueue.releasePointersOlderThan(tracker, eventTime);
@@ -1174,12 +1172,12 @@
             Log.w(TAG, "onUpEvent: corresponding down event not found for pointer "
                     + tracker.mPointerId);
         }
-        tracker.onUpEvent(touchX, touchY, eventTime);
+        tracker.onUpEvent(x, y, eventTime);
         mPointerQueue.remove(tracker);
     }
 
-    private void onCancelEvent(PointerTracker tracker, int touchX, int touchY, long eventTime) {
-        tracker.onCancelEvent(touchX, touchY, eventTime);
+    private void onCancelEvent(PointerTracker tracker, int x, int y, long eventTime) {
+        tracker.onCancelEvent(x, y, eventTime);
         mPointerQueue.remove(tracker);
     }
 
@@ -1217,16 +1215,18 @@
         closing();
     }
 
-    public void dismissPopupKeyboard() {
-        if (mPopupKeyboard.isShowing()) {
-            mPopupKeyboard.dismiss();
-            mMiniKeyboardOnScreen = false;
+    private void dismissPopupKeyboard() {
+        if (mMiniKeyboardPopup.isShowing()) {
+            mMiniKeyboardPopup.dismiss();
+            mMiniKeyboard = null;
+            mMiniKeyboardOriginX = 0;
+            mMiniKeyboardOriginY = 0;
             invalidateAllKeys();
         }
     }
 
     public boolean handleBack() {
-        if (mPopupKeyboard.isShowing()) {
+        if (mMiniKeyboardPopup.isShowing()) {
             dismissPopupKeyboard();
             return true;
         }
diff --git a/java/src/com/android/inputmethod/latin/PointerTracker.java b/java/src/com/android/inputmethod/latin/PointerTracker.java
index 10f51bd..a4447d5 100644
--- a/java/src/com/android/inputmethod/latin/PointerTracker.java
+++ b/java/src/com/android/inputmethod/latin/PointerTracker.java
@@ -27,13 +27,11 @@
 public class PointerTracker {
     private static final String TAG = "PointerTracker";
     private static final boolean DEBUG = false;
+    private static final boolean DEBUG_MOVE = DEBUG && true;
 
     public interface UIProxy {
         public void invalidateKey(Key key);
         public void showPreview(int keyIndex, PointerTracker tracker);
-        // TODO: These methods might be temporary.
-        public void dismissPopupKeyboard();
-        public boolean isMiniKeyboardOnScreen();
     }
 
     public final int mPointerId;
@@ -60,6 +58,10 @@
     private int mCurrentKey = NOT_A_KEY;
     private int mStartX;
     private int mStartY;
+    private long mDownTime;
+
+    // true if event is already translated to a key action (long press or mini-keyboard)
+    private boolean mKeyAlreadyProcessed;
 
     // for move de-bouncing
     private int mLastCodeX;
@@ -138,12 +140,18 @@
         }
     }
 
-    public void onDownEvent(int touchX, int touchY, long eventTime) {
-        int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null);
+    public void setAlreadyProcessed() {
+        mKeyAlreadyProcessed = true;
+    }
+
+    public void onDownEvent(int x, int y, long eventTime) {
+        int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
         mCurrentKey = keyIndex;
-        mStartX = touchX;
-        mStartY = touchY;
-        startMoveDebouncing(touchX, touchY);
+        mStartX = x;
+        mStartY = y;
+        mDownTime = eventTime;
+        mKeyAlreadyProcessed = false;
+        startMoveDebouncing(x, y);
         startTimeDebouncing(eventTime);
         checkMultiTap(eventTime, keyIndex);
         if (mListener != null) {
@@ -155,32 +163,44 @@
                 repeatKey(keyIndex);
                 mHandler.startKeyRepeatTimer(REPEAT_START_DELAY, keyIndex, this);
             }
-            mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT);
+            mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this);
         }
         showKeyPreviewAndUpdateKey(keyIndex);
-        updateMoveDebouncing(touchX, touchY);
+        updateMoveDebouncing(x, y);
         if (DEBUG)
-            Log.d(TAG, "onDownEvent: [" + mPointerId + "] modifier=" + isModifier());
+            debugLog("onDownEvent:", x, y);
     }
 
-    public void onMoveEvent(int touchX, int touchY, long eventTime) {
-        int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null);
+    public void onMoveEvent(int x, int y, long eventTime) {
+        int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
         if (isValidKeyIndex(keyIndex)) {
             if (mCurrentKey == NOT_A_KEY) {
                 updateTimeDebouncing(eventTime);
                 mCurrentKey = keyIndex;
-                mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT);
-            } else if (isMinorMoveBounce(touchX, touchY, keyIndex, mCurrentKey)) {
+                mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this);
+            } else if (isMinorMoveBounce(x, y, keyIndex, mCurrentKey)) {
                 updateTimeDebouncing(eventTime);
             } else {
                 resetMultiTap();
                 resetTimeDebouncing(eventTime, mCurrentKey);
                 resetMoveDebouncing();
                 mCurrentKey = keyIndex;
-                mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT);
+                mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this);
             }
         } else {
-            mHandler.cancelLongPressTimer();
+            if (mCurrentKey != NOT_A_KEY) {
+                updateTimeDebouncing(eventTime);
+                mCurrentKey = keyIndex;
+                mHandler.cancelLongPressTimer();
+            } else if (isMinorMoveBounce(x, y, keyIndex, mCurrentKey)) {
+                updateTimeDebouncing(eventTime);
+            } else {
+                resetMultiTap();
+                resetTimeDebouncing(eventTime, mCurrentKey);
+                resetMoveDebouncing();
+                mCurrentKey = keyIndex;
+                mHandler.cancelLongPressTimer();
+            }
         }
         /*
          * While time debouncing is in effect, mCurrentKey holds the new key and this tracker
@@ -189,17 +209,19 @@
          * should not be showed as popup preview.
          */
         showKeyPreviewAndUpdateKey(isMinorTimeBounce() ? mLastKey : mCurrentKey);
-        updateMoveDebouncing(touchX, touchY);
+        updateMoveDebouncing(x, y);
+        if (DEBUG_MOVE)
+            debugLog("onMoveEvent:", x, y);
     }
 
-    public void onUpEvent(int touchX, int touchY, long eventTime) {
+    public void onUpEvent(int x, int y, long eventTime) {
         if (DEBUG)
-            Log.d(TAG, "onUpEvent: [" + mPointerId + "] modifier=" + isModifier());
-        int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null);
+            debugLog("onUpEvent  :", x, y);
+        int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
         boolean wasInKeyRepeat = mHandler.isInKeyRepeat();
         mHandler.cancelKeyTimers();
         mHandler.cancelPopupPreview();
-        if (isMinorMoveBounce(touchX, touchY, keyIndex, mCurrentKey)) {
+        if (isMinorMoveBounce(x, y, keyIndex, mCurrentKey)) {
             updateTimeDebouncing(eventTime);
         } else {
             resetMultiTap();
@@ -208,24 +230,23 @@
         }
         if (isMinorTimeBounce()) {
             mCurrentKey = mLastKey;
-            touchX = mLastCodeX;
-            touchY = mLastCodeY;
+            x = mLastCodeX;
+            y = mLastCodeY;
         }
         showKeyPreviewAndUpdateKey(NOT_A_KEY);
         // If we're not on a repeating key (which sends on a DOWN event)
-        if (!wasInKeyRepeat && !mProxy.isMiniKeyboardOnScreen()) {
-            detectAndSendKey(mCurrentKey, touchX, touchY, eventTime);
+        if (!wasInKeyRepeat && !mKeyAlreadyProcessed) {
+            detectAndSendKey(mCurrentKey, (int)x, (int)y, eventTime);
         }
         if (isValidKeyIndex(keyIndex))
             mProxy.invalidateKey(mKeys[keyIndex]);
     }
 
-    public void onCancelEvent(int touchX, int touchY, long eventTime) {
+    public void onCancelEvent(int x, int y, long eventTime) {
         if (DEBUG)
-            Log.d(TAG, "onCancelEvent: [" + mPointerId + "]");
+            debugLog("onCancelEvt:", x, y);
         mHandler.cancelKeyTimers();
         mHandler.cancelPopupPreview();
-        mProxy.dismissPopupKeyboard();
         showKeyPreviewAndUpdateKey(NOT_A_KEY);
         int keyIndex = mCurrentKey;
         if (isValidKeyIndex(keyIndex))
@@ -241,6 +262,18 @@
         }
     }
 
+    public int getLastX() {
+        return mLastX;
+    }
+
+    public int getLastY() {
+        return mLastY;
+    }
+
+    public long getDownTime() {
+        return mDownTime;
+    }
+
     // These package scope methods are only for debugging purpose.
     /* package */ int getStartX() {
         return mStartX;
@@ -250,14 +283,6 @@
         return mStartY;
     }
 
-    /* package */ int getLastX() {
-        return mLastX;
-    }
-
-    /* package */ int getLastY() {
-        return mLastY;
-    }
-
     private void startMoveDebouncing(int x, int y) {
         mLastCodeX = x;
         mLastCodeY = y;
@@ -412,4 +437,17 @@
             resetMultiTap();
         }
     }
+
+    private void debugLog(String title, int x, int y) {
+        Key key = getKey(mCurrentKey);
+        final String code;
+        if (key == null) {
+            code = "----";
+        } else {
+            int primaryCode = key.codes[0];
+            code = String.format((primaryCode < 0) ? "%4d" : "0x%02x", primaryCode);
+        }
+        Log.d(TAG, String.format("%s [%d] %3d,%3d %s %s", title, mPointerId, x, y, code,
+                isModifier() ? "modifier" : ""));
+    }
 }
\ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/ProximityKeyDetector.java b/java/src/com/android/inputmethod/latin/ProximityKeyDetector.java
index 6ee0055..d17bedb 100644
--- a/java/src/com/android/inputmethod/latin/ProximityKeyDetector.java
+++ b/java/src/com/android/inputmethod/latin/ProximityKeyDetector.java
@@ -27,35 +27,32 @@
     private int[] mDistances = new int[MAX_NEARBY_KEYS];
 
     @Override
-    public int[] newCodeArray() {
-        int[] codes = new int[MAX_NEARBY_KEYS];
-        Arrays.fill(codes, LatinKeyboardBaseView.NOT_A_KEY);
-        return codes;
+    protected int getMaxNearbyKeys() {
+        return MAX_NEARBY_KEYS;
     }
 
     @Override
     public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys) {
-        final Key[] keys = mKeys;
-        if (keys == null)
-            throw new IllegalStateException("keyboard isn't set");
-        // mKeyboard is guaranteed not null at setKeybaord() method
+        final Key[] keys = getKeys();
+        final int touchX = getTouchX(x);
+        final int touchY = getTouchY(y);
         int primaryIndex = LatinKeyboardBaseView.NOT_A_KEY;
         int closestKey = LatinKeyboardBaseView.NOT_A_KEY;
         int closestKeyDist = mProximityThresholdSquare + 1;
         int[] distances = mDistances;
         Arrays.fill(distances, Integer.MAX_VALUE);
-        int [] nearestKeyIndices = mKeyboard.getNearestKeys(x, y);
+        int [] nearestKeyIndices = mKeyboard.getNearestKeys(touchX, touchY);
         final int keyCount = nearestKeyIndices.length;
         for (int i = 0; i < keyCount; i++) {
             final Key key = keys[nearestKeyIndices[i]];
             int dist = 0;
-            boolean isInside = key.isInside(x,y);
+            boolean isInside = key.isInside(touchX, touchY);
             if (isInside) {
                 primaryIndex = nearestKeyIndices[i];
             }
 
             if (((mProximityCorrectOn
-                    && (dist = key.squaredDistanceFrom(x, y)) < mProximityThresholdSquare)
+                    && (dist = key.squaredDistanceFrom(touchX, touchY)) < mProximityThresholdSquare)
                     || isInside)
                     && key.codes[0] > 32) {
                 // Find insertion point