Merge "Add new assets for LatinIME" into gingerbread
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 7cdf69a..44ff0a2 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -47,6 +47,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/LatinIME*)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/LatinIME.apk)
 
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjni_latinime_intermediates)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_settings.png b/java/res/drawable-hdpi/sym_bkeyboard_settings.png
new file mode 100644
index 0000000..08ba18f
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_settings.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_settings.png b/java/res/drawable-hdpi/sym_keyboard_feedback_settings.png
new file mode 100644
index 0000000..08ba18f
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_settings.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_settings.png b/java/res/drawable-hdpi/sym_keyboard_settings.png
new file mode 100644
index 0000000..f3bcdbc
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_settings.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_settings.png b/java/res/drawable-mdpi/sym_bkeyboard_settings.png
new file mode 100644
index 0000000..08ba18f
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_settings.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_settings.png b/java/res/drawable-mdpi/sym_keyboard_feedback_settings.png
new file mode 100644
index 0000000..08ba18f
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_settings.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_settings.png b/java/res/drawable-mdpi/sym_keyboard_settings.png
new file mode 100644
index 0000000..f3bcdbc
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_settings.png
Binary files differ
diff --git a/java/res/drawable/sym_keyboard_feedback_settings.png b/java/res/drawable/sym_keyboard_feedback_settings.png
new file mode 100644
index 0000000..08ba18f
--- /dev/null
+++ b/java/res/drawable/sym_keyboard_feedback_settings.png
Binary files differ
diff --git a/java/res/drawable/sym_keyboard_settings.png b/java/res/drawable/sym_keyboard_settings.png
new file mode 100644
index 0000000..f3bcdbc
--- /dev/null
+++ b/java/res/drawable/sym_keyboard_settings.png
Binary files differ
diff --git a/java/res/values/keycodes.xml b/java/res/values/keycodes.xml
index 8156c0e..3f03dd6 100644
--- a/java/res/values/keycodes.xml
+++ b/java/res/values/keycodes.xml
@@ -20,5 +20,6 @@
 
 <resources>
     <!-- Keycode for F1 (function) key. This one switches between language switch & comma/.com -->
+    <integer name="key_settings">-100</integer>
     <integer name="key_f1">-103</integer>
 </resources>
diff --git a/java/res/xml/kbd_alpha.xml b/java/res/xml/kbd_alpha.xml
deleted file mode 100644
index 4e8af33..0000000
--- a/java/res/xml/kbd_alpha.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-<?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.
-*/
--->
-
-<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
-    android:keyWidth="10%p"
-    android:horizontalGap="0px"
-    android:verticalGap="0px"
-    android:keyHeight="@dimen/key_height"
-    >
-
-    <Row>
-        <Key android:keyLabel="a" 
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_a"
-                android:keyEdgeFlags="left" />
-        <Key android:keyLabel="b" />
-        <Key android:keyLabel="c" 
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_c" />
-        <Key android:keyLabel="d" />
-        <Key android:keyLabel="e" 
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_e" />
-        <Key android:keyLabel="f" />
-        <Key android:keyLabel="g" />
-        <Key android:keyLabel="h" />
-        <Key android:keyLabel="i"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_i" />
-        <Key android:keyLabel="j" android:keyEdgeFlags="right" />
-    </Row>
-    <Row>
-        <Key android:keyLabel="k" android:keyEdgeFlags="left" />
-        <Key android:keyLabel="l" />
-        <Key android:keyLabel="m" />
-        <Key android:keyLabel="n"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_n" />
-        <Key android:keyLabel="o"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_o" />
-        <Key android:keyLabel="p" />
-        <Key android:keyLabel="q" />
-        <Key android:keyLabel="r" />
-        <Key android:keyLabel="s"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_s" />
-        <Key android:keyLabel="t" android:keyEdgeFlags="right" />
-    </Row>
-
-    <Row>
-        <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift" 
-                android:keyWidth="15%p" android:isModifier="true"
-                android:iconPreview="@drawable/sym_keyboard_feedback_shift"
-                android:isSticky="true" android:keyEdgeFlags="left"/>
-        <Key android:keyLabel="u"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_u" />
-        <Key android:keyLabel="v"/>
-        <Key android:keyLabel="w"/>
-        <Key android:keyLabel="x"/>
-        <Key android:keyLabel="y"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_y"
-        />
-        <Key android:keyLabel="z"/>
-        <Key android:keyLabel=","/>
-        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" 
-                android:keyWidth="15%p" android:keyEdgeFlags="right" 
-                android:iconPreview="@drawable/sym_keyboard_feedback_delete"
-                android:isRepeatable="true"/>
-    </Row>
-
-    <Row android:rowEdgeFlags="bottom">
-        <Key android:codes="-3" android:keyIcon="@drawable/sym_keyboard_done" 
-                android:iconPreview="@drawable/sym_keyboard_feedback_done" 
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
-        <Key android:codes="-2" android:keyLabel="123" android:keyWidth="15%p"/>
-        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" 
-                android:iconPreview="@drawable/sym_keyboard_feedback_space"
-                android:keyWidth="30%p" android:isRepeatable="true"/>
-        <Key android:codes="46" android:keyLabel="." 
-                android:popupKeyboard="@xml/popup_punctuation" 
-                android:keyWidth="15%p"/>
-        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" 
-                android:iconPreview="@drawable/sym_keyboard_feedback_return"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
-    </Row>
-</Keyboard>
\ No newline at end of file
diff --git a/java/res/xml/kbd_alpha_black.xml b/java/res/xml/kbd_alpha_black.xml
deleted file mode 100644
index 108e466..0000000
--- a/java/res/xml/kbd_alpha_black.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
-    android:keyWidth="10%p"
-    android:horizontalGap="0px"
-    android:verticalGap="0px"
-    android:keyHeight="@dimen/key_height"
-    >
-
-    <Row>
-        <Key android:keyLabel="a"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_a"
-                android:keyEdgeFlags="left" />
-        <Key android:keyLabel="b" />
-        <Key android:keyLabel="c"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_c" />
-        <Key android:keyLabel="d" />
-        <Key android:keyLabel="e"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_e" />
-        <Key android:keyLabel="f" />
-        <Key android:keyLabel="g" />
-        <Key android:keyLabel="h" />
-        <Key android:keyLabel="i"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_i" />
-        <Key android:keyLabel="j" android:keyEdgeFlags="right" />
-    </Row>
-    <Row>
-        <Key android:keyLabel="k" android:keyEdgeFlags="left" />
-        <Key android:keyLabel="l" />
-        <Key android:keyLabel="m" />
-        <Key android:keyLabel="n"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_n" />
-        <Key android:keyLabel="o"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_o" />
-        <Key android:keyLabel="p" />
-        <Key android:keyLabel="q" />
-        <Key android:keyLabel="r" />
-        <Key android:keyLabel="s"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_s" />
-        <Key android:keyLabel="t" android:keyEdgeFlags="right" />
-    </Row>
-
-    <Row>
-        <Key android:codes="-1" android:keyIcon="@drawable/sym_bkeyboard_shift"
-                android:keyWidth="15%p" android:isModifier="true"
-                android:iconPreview="@drawable/sym_keyboard_feedback_shift"
-                android:isSticky="true" android:keyEdgeFlags="left"/>
-        <Key android:keyLabel="u"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_u" />
-        <Key android:keyLabel="v"/>
-        <Key android:keyLabel="w"/>
-        <Key android:keyLabel="x"/>
-        <Key android:keyLabel="y"
-                android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_y"
-        />
-        <Key android:keyLabel="z"/>
-        <Key android:keyLabel=","/>
-        <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
-                android:keyWidth="15%p" android:keyEdgeFlags="right"
-                android:iconPreview="@drawable/sym_keyboard_feedback_delete"
-                android:isRepeatable="true"/>
-    </Row>
-
-    <Row android:rowEdgeFlags="bottom">
-        <Key android:codes="-3" android:keyIcon="@drawable/sym_bkeyboard_done"
-                android:iconPreview="@drawable/sym_keyboard_feedback_done"
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
-        <Key android:codes="-2" android:keyLabel="123" android:keyWidth="15%p"/>
-        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
-                android:iconPreview="@drawable/sym_keyboard_feedback_space"
-                android:keyWidth="30%p" android:isRepeatable="true"/>
-        <Key android:codes="46" android:keyLabel="."
-                android:popupKeyboard="@xml/popup_punctuation"
-                android:keyWidth="15%p"/>
-        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
-                android:iconPreview="@drawable/sym_keyboard_feedback_return"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
-    </Row>
-</Keyboard>
diff --git a/java/res/xml/kbd_qwerty.xml b/java/res/xml/kbd_qwerty.xml
index b0450c6..59c61e4 100755
--- a/java/res/xml/kbd_qwerty.xml
+++ b/java/res/xml/kbd_qwerty.xml
@@ -111,74 +111,81 @@
 
     <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
         <Key android:codes="-2" android:keyLabel="@string/label_symbol_key" 
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+                android:keyWidth="15%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_keyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_mic"
                 android:iconPreview="@drawable/sym_keyboard_feedback_mic"
                 android:keyWidth="10%p"/>
         <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
                 android:iconPreview="@drawable/sym_keyboard_feedback_space"
-                android:keyWidth="40%p" android:isRepeatable="true"/>
+                android:keyWidth="30%p" android:isRepeatable="true"/>
         <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation" 
                 android:keyWidth="10%p"/>
         <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
                 android:iconPreview="@drawable/sym_keyboard_feedback_return"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+                android:keyWidth="25%p" android:keyEdgeFlags="right"/>
     </Row>
 
     <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
         <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+                android:keyWidth="15%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_keyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:keyLabel="/" android:keyWidth="10%p"/>
         <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
                 android:iconPreview="@drawable/sym_keyboard_feedback_space"
-                android:keyWidth="40%p" android:isRepeatable="true"/>
-        <!--Key android:keyLabel="@string/popular_domain_0"
-                android:keyOutputText="@string/popular_domain_0"
-                android:popupKeyboard="@xml/popup_domains"
-                android:keyWidth="20%p"/-->
+                android:keyWidth="30%p" android:isRepeatable="true"/>
         <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
                 android:keyWidth="10%p"/>
         <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
                 android:iconPreview="@drawable/sym_keyboard_feedback_return"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+                android:keyWidth="25%p" android:keyEdgeFlags="right"/>
     </Row>
 
     <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
         <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+                android:keyWidth="15%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_keyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:keyLabel="\@"/>
         <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
                 android:iconPreview="@drawable/sym_keyboard_feedback_space"
-                android:keyWidth="40%p" android:isRepeatable="true"/>
-        <!--Key android:keyLabel="@string/popular_domain_0"
-                android:keyOutputText="@string/popular_domain_0"
-                android:popupKeyboard="@xml/popup_domains"
-                android:keyWidth="20%p"/-->
+                android:keyWidth="30%p" android:isRepeatable="true"/>
         <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
         <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
                 android:iconPreview="@drawable/sym_keyboard_feedback_return"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+                android:keyWidth="25%p" android:keyEdgeFlags="right"/>
     </Row>
 
     <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
         <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+                android:keyWidth="15%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_keyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_mic"
                 android:iconPreview="@drawable/sym_keyboard_feedback_mic"
                 android:keyWidth="10%p"/>
         <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
                 android:iconPreview="@drawable/sym_keyboard_feedback_space"
-                android:keyWidth="40%p" android:isRepeatable="true"/>
+                android:keyWidth="30%p" android:isRepeatable="true"/>
         <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation" 
                 android:keyWidth="10%p"/>
         <Key android:keyLabel=":-)" android:keyOutputText=":-) "
                 android:popupKeyboard="@xml/popup_smileys"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+                android:keyWidth="25%p" android:keyEdgeFlags="right"/>
     </Row>
 
     <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
         <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+                android:keyWidth="10%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_keyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_mic"
                 android:iconPreview="@drawable/sym_keyboard_feedback_mic"
                 android:keyWidth="10%p"/>
@@ -187,12 +194,11 @@
                 android:keyWidth="20%p" android:isRepeatable="true"/>
         <Key android:codes="9" android:keyIcon="@drawable/sym_keyboard_tab"
                 android:iconPreview="@drawable/sym_keyboard_feedback_tab"
-                android:keyWidth="20%p"/>
-        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+                android:keyWidth="15%p"/>
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
         <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
                 android:iconPreview="@drawable/sym_keyboard_feedback_return"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+                android:keyWidth="15%p" android:keyEdgeFlags="right"/>
     </Row>
-
 </Keyboard>
-
diff --git a/java/res/xml/kbd_qwerty_black.xml b/java/res/xml/kbd_qwerty_black.xml
index afea2f3..076359c 100755
--- a/java/res/xml/kbd_qwerty_black.xml
+++ b/java/res/xml/kbd_qwerty_black.xml
@@ -110,75 +110,82 @@
     </Row>
 
     <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
-        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key" 
+                android:keyWidth="15%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_bkeyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
                 android:iconPreview="@drawable/sym_keyboard_feedback_mic"
                 android:keyWidth="10%p"/>
         <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
                 android:iconPreview="@drawable/sym_keyboard_feedback_space"
-                android:keyWidth="40%p" android:isRepeatable="true"/>
-        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="30%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation" 
                 android:keyWidth="10%p"/>
         <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
                 android:iconPreview="@drawable/sym_keyboard_feedback_return"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+                android:keyWidth="25%p" android:keyEdgeFlags="right"/>
     </Row>
 
     <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
         <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+                android:keyWidth="15%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_bkeyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:keyLabel="/" android:keyWidth="10%p"/>
         <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
                 android:iconPreview="@drawable/sym_keyboard_feedback_space"
-                android:keyWidth="40%p" android:isRepeatable="true"/>
-        <!--Key android:keyLabel="@string/popular_domain_0"
-                android:keyOutputText="@string/popular_domain_0"
-                android:popupKeyboard="@xml/popup_domains"
-                android:keyWidth="20%p"/-->
+                android:keyWidth="30%p" android:isRepeatable="true"/>
         <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
                 android:keyWidth="10%p"/>
         <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
                 android:iconPreview="@drawable/sym_keyboard_feedback_return"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+                android:keyWidth="25%p" android:keyEdgeFlags="right"/>
     </Row>
 
     <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
         <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+                android:keyWidth="15%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_bkeyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:keyLabel="\@"/>
         <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
                 android:iconPreview="@drawable/sym_keyboard_feedback_space"
-                android:keyWidth="40%p" android:isRepeatable="true"/>
-        <!--Key android:keyLabel="@string/popular_domain_0"
-                android:keyOutputText="@string/popular_domain_0"
-                android:popupKeyboard="@xml/popup_domains"
-                android:keyWidth="20%p"/-->
+                android:keyWidth="30%p" android:isRepeatable="true"/>
         <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
         <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
                 android:iconPreview="@drawable/sym_keyboard_feedback_return"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+                android:keyWidth="25%p" android:keyEdgeFlags="right"/>
     </Row>
 
     <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
         <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+                android:keyWidth="15%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_bkeyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
                 android:iconPreview="@drawable/sym_keyboard_feedback_mic"
                 android:keyWidth="10%p"/>
         <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
                 android:iconPreview="@drawable/sym_keyboard_feedback_space"
-                android:keyWidth="40%p" android:isRepeatable="true"/>
-        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="30%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation" 
                 android:keyWidth="10%p"/>
         <Key android:keyLabel=":-)" android:keyOutputText=":-) "
                 android:popupKeyboard="@xml/popup_smileys"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+                android:keyWidth="25%p" android:keyEdgeFlags="right"/>
     </Row>
 
     <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
         <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+                android:keyWidth="10%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_bkeyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
                 android:iconPreview="@drawable/sym_keyboard_feedback_mic"
                 android:keyWidth="10%p"/>
@@ -187,11 +194,12 @@
                 android:keyWidth="20%p" android:isRepeatable="true"/>
         <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
                 android:iconPreview="@drawable/sym_keyboard_feedback_tab"
-                android:keyWidth="20%p"/>
-        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+                android:keyWidth="15%p"/>
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
         <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
                 android:iconPreview="@drawable/sym_keyboard_feedback_return"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+                android:keyWidth="15%p" android:keyEdgeFlags="right"/>
     </Row>
 
 </Keyboard>
diff --git a/java/res/xml/kbd_symbols.xml b/java/res/xml/kbd_symbols.xml
index 77eb7d0..3f6b8ac 100755
--- a/java/res/xml/kbd_symbols.xml
+++ b/java/res/xml/kbd_symbols.xml
@@ -123,16 +123,19 @@
 
     <Row  android:rowEdgeFlags="bottom">
         <Key android:codes="-2" android:keyLabel="@string/label_alpha_key"
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+                android:keyWidth="15%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_keyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:codes="@integer/key_f1" android:keyWidth="10%p"/>
         <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
-                android:keyWidth="40%p"
+                android:keyWidth="30%p"
                 android:iconPreview="@drawable/sym_keyboard_feedback_space"
                 android:isRepeatable="true"/>
         <Key android:codes="46" android:keyLabel="."
                 android:popupKeyboard="@xml/popup_punctuation"
                 android:keyWidth="10%p"/>
-        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" android:keyWidth="20%p" android:keyEdgeFlags="right"
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" android:keyWidth="25%p" android:keyEdgeFlags="right"
                 android:iconPreview="@drawable/sym_keyboard_feedback_return"
                 />
     </Row>
diff --git a/java/res/xml/kbd_symbols_black.xml b/java/res/xml/kbd_symbols_black.xml
index 5f8dfbe..7eae554 100755
--- a/java/res/xml/kbd_symbols_black.xml
+++ b/java/res/xml/kbd_symbols_black.xml
@@ -123,16 +123,19 @@
 
     <Row  android:rowEdgeFlags="bottom">
         <Key android:codes="-2" android:keyLabel="@string/label_alpha_key"
-                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+                android:keyWidth="15%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_bkeyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:codes="@integer/key_f1" android:keyWidth="10%p"/>
         <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
-                android:keyWidth="40%p"
+                android:keyWidth="30%p"
                 android:iconPreview="@drawable/sym_keyboard_feedback_space"
                 android:isRepeatable="true"/>
         <Key android:codes="46" android:keyLabel="."
                 android:popupKeyboard="@xml/popup_punctuation"
                 android:keyWidth="10%p"/>
-        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return" android:keyWidth="20%p" android:keyEdgeFlags="right"
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return" android:keyWidth="25%p" android:keyEdgeFlags="right"
                 android:iconPreview="@drawable/sym_keyboard_feedback_return"
                 />
     </Row>
diff --git a/java/res/xml/kbd_symbols_shift.xml b/java/res/xml/kbd_symbols_shift.xml
index 467ca52..0ec4c71 100755
--- a/java/res/xml/kbd_symbols_shift.xml
+++ b/java/res/xml/kbd_symbols_shift.xml
@@ -89,16 +89,19 @@
     </Row>
     
     <Row android:rowEdgeFlags="bottom">
-        <Key android:codes="-2" android:keyLabel="@string/label_alpha_key" android:keyWidth="20%p"
+        <Key android:codes="-2" android:keyLabel="@string/label_alpha_key" android:keyWidth="15%p"
                 android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_keyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:keyLabel="„" android:keyWidth="10%p" />
         <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
-                android:keyWidth="40%p"
+                android:keyWidth="30%p"
                 android:iconPreview="@drawable/sym_keyboard_feedback_space"
                 android:isRepeatable="true"/>
         <Key android:keyLabel="…" android:keyWidth="10%p" />
         <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"
+                android:keyWidth="25%p" android:keyEdgeFlags="right"
                 android:iconPreview="@drawable/sym_keyboard_feedback_return"
                 />
     </Row>
diff --git a/java/res/xml/kbd_symbols_shift_black.xml b/java/res/xml/kbd_symbols_shift_black.xml
index 511ad49..250e085 100755
--- a/java/res/xml/kbd_symbols_shift_black.xml
+++ b/java/res/xml/kbd_symbols_shift_black.xml
@@ -89,16 +89,19 @@
     </Row>
 
     <Row android:rowEdgeFlags="bottom">
-        <Key android:codes="-2" android:keyLabel="@string/label_alpha_key" android:keyWidth="20%p"
+        <Key android:codes="-2" android:keyLabel="@string/label_alpha_key" android:keyWidth="15%p"
                 android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_settings" android:keyIcon="@drawable/sym_bkeyboard_settings"
+                android:iconPreview="@drawable/sym_keyboard_feedback_settings"
+                android:keyWidth="10%p"/>
         <Key android:keyLabel="„" android:keyWidth="10%p" />
         <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
-                android:keyWidth="40%p"
+                android:keyWidth="30%p"
                 android:iconPreview="@drawable/sym_keyboard_feedback_space"
                 android:isRepeatable="true"/>
         <Key android:keyLabel="…" android:keyWidth="10%p" />
         <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
-                android:keyWidth="20%p" android:keyEdgeFlags="right"
+                android:keyWidth="25%p" android:keyEdgeFlags="right"
                 android:iconPreview="@drawable/sym_keyboard_feedback_return"
                 />
     </Row>
diff --git a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
index 45a54b1..a50c5aa 100644
--- a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
@@ -37,11 +37,7 @@
     public static final int MODE_EMAIL = 5;
     public static final int MODE_IM = 6;
     public static final int MODE_WEB = 7;
-    
-    public static final int MODE_TEXT_QWERTY = 0;
-    public static final int MODE_TEXT_ALPHA = 1;
-    public static final int MODE_TEXT_COUNT = 2;
-    
+
     public static final int KEYBOARDMODE_NORMAL = R.id.mode_normal;
     public static final int KEYBOARDMODE_URL = R.id.mode_url;
     public static final int KEYBOARDMODE_EMAIL = R.id.mode_email;
@@ -59,7 +55,6 @@
     private static final int CHAR_THEME_COLOR_BLACK = 1;
 
     // Tables which contains resource ids for each character theme color
-    private static final int[] KBD_ALPHA = new int[] {R.xml.kbd_alpha, R.xml.kbd_alpha_black};
     private static final int[] KBD_PHONE = new int[] {R.xml.kbd_phone, R.xml.kbd_phone_black};
     private static final int[] KBD_PHONE_SYMBOLS = new int[] {
         R.xml.kbd_phone_symbols, R.xml.kbd_phone_symbols_black};
@@ -92,7 +87,6 @@
 
     private int mMode = MODE_NONE; /** One of the MODE_XXX values */
     private int mImeOptions;
-    private int mTextMode = MODE_TEXT_QWERTY;
     private boolean mIsSymbols;
     private boolean mHasVoice;
     private boolean mVoiceOnPrimary;
@@ -291,11 +285,6 @@
                         "getKeyboardId:" + mode + "," + imeOptions + "," + isSymbols);
                 /* fall through */
             case MODE_TEXT:
-                if (mTextMode == MODE_TEXT_ALPHA) {
-                    return new KeyboardId(
-                            KBD_ALPHA[charColorId], KEYBOARDMODE_NORMAL, true, hasVoice);
-                }
-                // Normally mTextMode should be MODE_TEXT_QWERTY.
                 return new KeyboardId(keyboardRowsResId, KEYBOARDMODE_NORMAL, true, hasVoice);
             case MODE_SYMBOLS:
                 return new KeyboardId(KBD_SYMBOLS[charColorId], hasVoice);
@@ -320,10 +309,6 @@
     boolean isTextMode() {
         return mMode == MODE_TEXT;
     }
-    
-    int getTextModeCount() {
-        return MODE_TEXT_COUNT;
-    }
 
     boolean isAlphabetMode() {
         if (mCurrentId == null) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index f26cbc0..74ed90f 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -647,16 +647,14 @@
                         (attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) == 0) {
                     mInputTypeNoAutoCorrect = true;
                 }
-                if ((attribute.inputType&EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
+                if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
                     mPredictionOn = false;
-                    mCompletionOn = true && isFullscreenMode();
+                    mCompletionOn = isFullscreenMode();
                 }
-                updateShiftKeyState(attribute);
                 break;
             default:
                 mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT,
                         attribute.imeOptions, enableVoiceButton);
-                updateShiftKeyState(attribute);
         }
         inputView.closing();
         mComposing.setLength(0);
@@ -666,8 +664,9 @@
         loadSettings();
         updateShiftKeyState(attribute);
 
-        setCandidatesViewShown(false);
-        setSuggestions(null, false, false, false);
+        setCandidatesViewShownInternal(isCandidateStripVisible() || mCompletionOn,
+                false /* needsInputViewShown */ );
+        updateSuggestions();
 
         // If the dictionary is not big enough, don't auto correct
         mHasDictionary = mSuggest.hasMainDictionary();
@@ -831,18 +830,21 @@
             // When in fullscreen mode, show completions generated by the application
             setSuggestions(stringList, true, true, true);
             mBestWord = null;
-            setCandidatesViewShown(isCandidateStripVisible() || mCompletionOn);
+            setCandidatesViewShown(true);
+        }
+    }
+
+    private void setCandidatesViewShownInternal(boolean shown, boolean needsInputViewShown) {
+        // TODO: Remove this if we support candidates with hard keyboard
+        if (onEvaluateInputViewShown()) {
+            super.setCandidatesViewShown(shown && mKeyboardSwitcher.getInputView() != null
+                    && (needsInputViewShown ? mKeyboardSwitcher.getInputView().isShown() : true));
         }
     }
 
     @Override
     public void setCandidatesViewShown(boolean shown) {
-        // TODO: Remove this if we support candidates with hard keyboard
-        if (onEvaluateInputViewShown()) {
-            // Show the candidates view only if input view is showing
-            super.setCandidatesViewShown(shown && mKeyboardSwitcher.getInputView() != null
-                    && mKeyboardSwitcher.getInputView().isShown());
-        }
+        setCandidatesViewShownInternal(shown, true /* needsInputViewShown */ );
     }
 
     @Override
@@ -1430,8 +1432,7 @@
     }
 
     private boolean isPredictionOn() {
-        boolean predictionOn = mPredictionOn;
-        return predictionOn;
+        return mPredictionOn;
     }
 
     private boolean isCandidateStripVisible() {
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
index d9fa9f2..0833a40 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
@@ -61,7 +61,8 @@
  * @attr ref R.styleable#LatinKeyboardBaseView_verticalCorrection
  * @attr ref R.styleable#LatinKeyboardBaseView_popupLayout
  */
-public class LatinKeyboardBaseView extends View implements View.OnClickListener {
+public class LatinKeyboardBaseView extends View implements View.OnClickListener,
+        PointerTracker.UIProxy {
     private static final boolean DEBUG = false;
 
     public static final int NOT_A_TOUCH_COORDINATE = -1;
@@ -146,15 +147,10 @@
     // Timing constants
     private static final int DELAY_BEFORE_PREVIEW = 0;
     private static final int DELAY_AFTER_PREVIEW = 70;
-    private static final int REPEAT_INTERVAL = 50; // ~20 keys per second
-    private static final int REPEAT_START_DELAY = 400;
-    private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
-    private static final int MULTITAP_INTERVAL = 800; // milliseconds
-    private static final int KEY_DEBOUNCE_TIME = 70;
+    private static final int REPEAT_INTERVAL = PointerTracker.REPEAT_INTERVAL;
 
     // Miscellaneous constants
-    static final int NOT_A_KEY = -1;
-    private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE };
+    /* package */ static final int NOT_A_KEY = -1;
     private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
 
     // XML attribute
@@ -188,7 +184,6 @@
     private int mPopupPreviewX;
     private int mPopupPreviewY;
     private int mWindowY;
-    private final StringBuilder mPreviewLabel = new StringBuilder(1);
 
     // Popup mini keyboard
     private PopupWindow mPopupKeyboard;
@@ -204,20 +199,11 @@
     /** Listener for {@link OnKeyboardActionListener}. */
     private OnKeyboardActionListener mKeyboardActionListener;
 
-    private final KeyDebouncer mDebouncer = new KeyDebouncer();
+    private final PointerTracker mPointerTracker;
     private final float mDebounceHysteresis;
-    private int mCurrentKey = NOT_A_KEY;
-    private int mStartX;
-    private int mStartY;
 
     private final ProximityKeyDetector mProximityKeyDetector = new ProximityKeyDetector();
 
-    // For multi-tap
-    private int mLastSentIndex;
-    private int mTapCount;
-    private long mLastTapTime;
-    private boolean mInMultiTap;
-
     // Variables for dealing with multiple pointers
     private int mOldPointerCount = 1;
     private int mOldPointerX;
@@ -251,7 +237,7 @@
         private static final int MSG_POPUP_PREVIEW = 1;
         private static final int MSG_DISMISS_PREVIEW = 2;
         private static final int MSG_REPEAT_KEY = 3;
-        private static final int MSG_LOGPRESS_KEY = 4;
+        private static final int MSG_LONGPRESS_KEY = 4;
 
         private boolean mInKeyRepeat;
 
@@ -259,24 +245,32 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_POPUP_PREVIEW:
-                    showKey(msg.arg1);
+                    showKey(msg.arg1, (PointerTracker)msg.obj);
                     break;
                 case MSG_DISMISS_PREVIEW:
                     mPreviewText.setVisibility(INVISIBLE);
                     break;
-                case MSG_REPEAT_KEY:
-                    repeatKey(msg.arg1);
-                    startKeyRepeatTimer(REPEAT_INTERVAL, msg.arg1);
+                case MSG_REPEAT_KEY: {
+                    final PointerTracker tracker = (PointerTracker)msg.obj;
+                    tracker.repeatKey(msg.arg1);
+                    startKeyRepeatTimer(REPEAT_INTERVAL, msg.arg1, tracker);
                     break;
-                case MSG_LOGPRESS_KEY:
+                }
+                case MSG_LONGPRESS_KEY:
                     openPopupIfRequired(msg.arg1);
                     break;
             }
         }
 
-        public void popupPreview(int keyIndex, long delay) {
+        public void popupPreview(long delay, int keyIndex, PointerTracker tracker) {
             removeMessages(MSG_POPUP_PREVIEW);
-            sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0), delay);
+            if (mPreviewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) {
+                // Show right away, if it's already visible and finger is moving around
+                showKey(keyIndex, tracker);
+            } else {
+                sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0, tracker),
+                        delay);
+            }
         }
 
         public void cancelPopupPreview() {
@@ -284,16 +278,18 @@
         }
 
         public void dismissPreview(long delay) {
-            sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay);
+            if (mPreviewPopup.isShowing()) {
+                sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay);
+            }
         }
 
         public void cancelDismissPreview() {
             removeMessages(MSG_DISMISS_PREVIEW);
         }
 
-        public void startKeyRepeatTimer(long delay, int keyIndex) {
+        public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {
             mInKeyRepeat = true;
-            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, keyIndex, 0), delay);
+            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, keyIndex, 0, tracker), delay);
         }
 
         public void cancelKeyRepeatTimer() {
@@ -306,12 +302,12 @@
         }
 
         public void startLongPressTimer(int keyIndex, long delay) {
-            removeMessages(MSG_LOGPRESS_KEY);
-            sendMessageDelayed(obtainMessage(MSG_LOGPRESS_KEY, keyIndex, 0), delay);
+            removeMessages(MSG_LONGPRESS_KEY);
+            sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, keyIndex, 0), delay);
         }
 
         public void cancelLongPressTimer() {
-            removeMessages(MSG_LOGPRESS_KEY);
+            removeMessages(MSG_LONGPRESS_KEY);
         }
 
         public void cancelKeyTimers() {
@@ -326,114 +322,6 @@
         }
     };
 
-    static class KeyDebouncer {
-        private Key[] mKeys;
-        private int mKeyDebounceThresholdSquared = -1;
-
-        // for move de-bouncing
-        private int mLastCodeX;
-        private int mLastCodeY;
-        private int mLastX;
-        private int mLastY;
-
-        // for time de-bouncing
-        private int mLastKey;
-        private long mLastKeyTime;
-        private long mLastMoveTime;
-        private long mCurrentKeyTime;
-
-        public void setKeyboard(Key[] keys, float hysteresisPixel) {
-            if (keys == null || hysteresisPixel < 1.0f)
-                throw new IllegalArgumentException();
-            mKeys = keys;
-            mKeyDebounceThresholdSquared = (int)(hysteresisPixel * hysteresisPixel);
-        }
-
-        public int getLastCodeX() {
-            return mLastCodeX;
-        }
-
-        public int getLastCodeY() {
-            return mLastCodeY;
-        }
-
-        public int getLastX() {
-            return mLastX;
-        }
-
-        public int getLastY() {
-            return mLastY;
-        }
-
-        public int getLastKey() {
-            return mLastKey;
-        }
-
-        public void startMoveDebouncing(int x, int y) {
-            mLastCodeX = x;
-            mLastCodeY = y;
-        }
-
-        public void updateMoveDebouncing(int x, int y) {
-            mLastX = x;
-            mLastY = y;
-        }
-
-        public void resetMoveDebouncing() {
-            mLastCodeX = mLastX;
-            mLastCodeY = mLastY;
-        }
-
-        public boolean isMinorMoveBounce(int x, int y, int newKey, int curKey) {
-            if (mKeys == null || mKeyDebounceThresholdSquared < 0)
-                throw new IllegalStateException("keyboard and/or hysteresis not set");
-            if (newKey == curKey) {
-                return true;
-            } else if (curKey >= 0 && curKey < mKeys.length) {
-                return getSquareDistanceToKeyEdge(x, y, mKeys[curKey])
-                        < mKeyDebounceThresholdSquared;
-            } else {
-                return false;
-            }
-        }
-
-        private static int getSquareDistanceToKeyEdge(int x, int y, Key key) {
-            final int left = key.x;
-            final int right = key.x + key.width;
-            final int top = key.y;
-            final int bottom = key.y + key.height;
-            final int edgeX = x < left ? left : (x > right ? right : x);
-            final int edgeY = y < top ? top : (y > bottom ? bottom : y);
-            final int dx = x - edgeX;
-            final int dy = y - edgeY;
-            return dx * dx + dy * dy;
-        }
-
-        public void startTimeDebouncing(long eventTime) {
-            mLastKey = NOT_A_KEY;
-            mLastKeyTime = 0;
-            mCurrentKeyTime = 0;
-            mLastMoveTime = eventTime;
-        }
-
-        public void updateTimeDebouncing(long eventTime) {
-            mCurrentKeyTime += eventTime - mLastMoveTime;
-            mLastMoveTime = eventTime;
-        }
-
-        public void resetTimeDebouncing(long eventTime, int currentKey) {
-            mLastKey = currentKey;
-            mLastKeyTime = mCurrentKeyTime + eventTime - mLastMoveTime;
-            mCurrentKeyTime = 0;
-            mLastMoveTime = eventTime;
-        }
-
-        public boolean isMinorTimeBounce() {
-            return mCurrentKeyTime < mLastKeyTime && mCurrentKeyTime < KEY_DEBOUNCE_TIME
-                && mLastKey != NOT_A_KEY;
-        }
-    }
-
     public LatinKeyboardBaseView(Context context, AttributeSet attrs) {
         this(context, attrs, R.attr.keyboardViewStyle);
     }
@@ -542,7 +430,6 @@
         // TODO: Refer frameworks/base/core/res/res/values/config.xml
         mDisambiguateSwipe = res.getBoolean(R.bool.config_swipeDisambiguation);
         mDebounceHysteresis = res.getDimension(R.dimen.key_debounce_hysteresis_distance);
-        resetMultiTap();
 
         GestureDetector.SimpleOnGestureListener listener =
                 new GestureDetector.SimpleOnGestureListener() {
@@ -586,10 +473,13 @@
         final boolean ignoreMultitouch = true;
         mGestureDetector = new GestureDetector(getContext(), listener, null, ignoreMultitouch);
         mGestureDetector.setIsLongpressEnabled(false);
+
+        mPointerTracker = new PointerTracker(mHandler, mProximityKeyDetector, this);
     }
 
     public void setOnKeyboardActionListener(OnKeyboardActionListener listener) {
         mKeyboardActionListener = listener;
+        mPointerTracker.setOnKeyboardActionListener(listener);
     }
 
     /**
@@ -609,7 +499,7 @@
      */
     public void setKeyboard(Keyboard keyboard) {
         if (mKeyboard != null) {
-            showPreview(NOT_A_KEY);
+            dismissKeyPreview();
         }
         // Remove any pending messages, except dismissing preview
         mHandler.cancelKeyTimers();
@@ -619,7 +509,7 @@
         List<Key> keys = mKeyboard.getKeys();
         mKeys = keys.toArray(new Key[keys.size()]);
         mProximityKeyDetector.setKeyboard(keyboard, mKeys);
-        mDebouncer.setKeyboard(mKeys, mDebounceHysteresis);
+        mPointerTracker.setKeyboard(mKeys, mDebounceHysteresis);
         requestLayout();
         // Hint to reallocate the buffer if the size changed
         mKeyboardChanged = true;
@@ -889,16 +779,19 @@
 
         if (DEBUG) {
             if (mShowTouchPoints) {
-                int lastX = mDebouncer.getLastX();
-                int lastY = mDebouncer.getLastY();
+                PointerTracker tracker = mPointerTracker;
+                int startX = tracker.getStartX();
+                int startY = tracker.getStartY();
+                int lastX = tracker.getLastX();
+                int lastY = tracker.getLastY();
                 paint.setAlpha(128);
                 paint.setColor(0xFFFF0000);
-                canvas.drawCircle(mStartX, mStartY, 3, paint);
-                canvas.drawLine(mStartX, mStartY, lastX, lastY, paint);
+                canvas.drawCircle(startX, startY, 3, paint);
+                canvas.drawLine(startX, startY, lastX, lastY, paint);
                 paint.setColor(0xFF0000FF);
                 canvas.drawCircle(lastX, lastY, 3, paint);
                 paint.setColor(0xFF00FF00);
-                canvas.drawCircle((mStartX + lastX) / 2, (mStartY + lastY) / 2, 2, paint);
+                canvas.drawCircle((startX + lastX) / 2, (startY + lastY) / 2, 2, paint);
             }
         }
 
@@ -906,107 +799,38 @@
         mDirtyRect.setEmpty();
     }
 
-
-    private void detectAndSendKey(int index, int x, int y, long eventTime) {
-        if (index != NOT_A_KEY && index < mKeys.length) {
-            final Key key = mKeys[index];
-            if (key.text != null) {
-                mKeyboardActionListener.onText(key.text);
-                mKeyboardActionListener.onRelease(NOT_A_KEY);
-            } else {
-                int code = key.codes[0];
-                //TextEntryState.keyPressedAt(key, x, y);
-                int[] codes = mProximityKeyDetector.newCodeArray();
-                mProximityKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes);
-                // Multi-tap
-                if (mInMultiTap) {
-                    if (mTapCount != -1) {
-                        mKeyboardActionListener.onKey(Keyboard.KEYCODE_DELETE, KEY_DELETE, x, y);
-                    } else {
-                        mTapCount = 0;
-                    }
-                    code = key.codes[mTapCount];
-                }
-                /*
-                 * Swap the first and second values in the codes array if the primary code is not
-                 * the first value but the second value in the array. This happens when key
-                 * debouncing is in effect.
-                 */
-                if (codes.length >= 2 && codes[0] != code && codes[1] == code) {
-                    codes[1] = codes[0];
-                    codes[0] = code;
-                }
-                mKeyboardActionListener.onKey(code, codes, x, y);
-                mKeyboardActionListener.onRelease(code);
-            }
-            mLastSentIndex = index;
-            mLastTapTime = eventTime;
-        }
+    // TODO: clean up this method.
+    private void dismissKeyPreview() {
+        mPointerTracker.updateKey(NOT_A_KEY);
+        showPreview(NOT_A_KEY, mPointerTracker);
     }
 
-    /**
-     * Handle multi-tap keys by producing the key label for the current multi-tap state.
-     */
-    private CharSequence getPreviewText(Key key) {
-        if (mInMultiTap) {
-            // Multi-tap
-            mPreviewLabel.setLength(0);
-            mPreviewLabel.append((char) key.codes[mTapCount < 0 ? 0 : mTapCount]);
-            return adjustCase(mPreviewLabel);
-        } else {
-            return adjustCase(key.label);
-        }
-    }
-
-    private void showPreview(int keyIndex) {
+    public void showPreview(int keyIndex, PointerTracker tracker) {
         int oldKeyIndex = mOldPreviewKeyIndex;
         mOldPreviewKeyIndex = keyIndex;
-
-        // Release the old key and press the new key
-        final Key[] keys = mKeys;
-        if (oldKeyIndex != keyIndex) {
-            if (oldKeyIndex != NOT_A_KEY && keys.length > oldKeyIndex) {
-                // if new key index is not a key, old key was just released inside of the key.
-                final boolean inside = (keyIndex == NOT_A_KEY);
-                keys[oldKeyIndex].onReleased(inside);
-                invalidateKey(oldKeyIndex);
-            }
-            if (keyIndex != NOT_A_KEY && keys.length > keyIndex) {
-                keys[keyIndex].onPressed();
-                invalidateKey(keyIndex);
-            }
-        }
         // If key changed and preview is on ...
         if (oldKeyIndex != keyIndex && mShowPreview) {
-            final PopupWindow previewPopup = mPreviewPopup;
             if (keyIndex == NOT_A_KEY) {
                 mHandler.cancelPopupPreview();
-                if (previewPopup.isShowing()) {
-                    mHandler.dismissPreview(DELAY_AFTER_PREVIEW);
-                }
+                mHandler.dismissPreview(DELAY_AFTER_PREVIEW);
             } else {
-                if (previewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) {
-                    // Show right away, if it's already visible and finger is moving around
-                    showKey(keyIndex);
-                } else {
-                    mHandler.popupPreview(keyIndex, DELAY_BEFORE_PREVIEW);
-                }
+                mHandler.popupPreview(DELAY_BEFORE_PREVIEW, keyIndex, tracker);
             }
         }
     }
 
-    private void showKey(final int keyIndex) {
+    private void showKey(final int keyIndex, PointerTracker tracker) {
+        Key key = tracker.getKey(keyIndex);
+        if (key == null)
+            return;
         final PopupWindow previewPopup = mPreviewPopup;
-        final Key[] keys = mKeys;
-        if (keyIndex < 0 || keyIndex >= mKeys.length) return;
-        Key key = keys[keyIndex];
         if (key.icon != null) {
             mPreviewText.setCompoundDrawables(null, null, null,
                     key.iconPreview != null ? key.iconPreview : key.icon);
             mPreviewText.setText(null);
         } else {
             mPreviewText.setCompoundDrawables(null, null, null, null);
-            mPreviewText.setText(getPreviewText(key));
+            mPreviewText.setText(adjustCase(tracker.getPreviewText(key)));
             if (key.label.length() > 1 && key.codes.length < 2) {
                 mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyTextSize);
                 mPreviewText.setTypeface(Typeface.DEFAULT_BOLD);
@@ -1077,7 +901,7 @@
      * 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
      * draws the cached buffer.
-     * @see #invalidateKey(int)
+     * @see #invalidateKey(Key)
      */
     public void invalidateAllKeys() {
         mDirtyRect.union(0, 0, getWidth(), getHeight());
@@ -1089,15 +913,12 @@
      * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only
      * one key is changing it's content. Any changes that affect the position or size of the key
      * may not be honored.
-     * @param keyIndex the index of the key in the attached {@link Keyboard}.
+     * @param key key in the attached {@link Keyboard}.
      * @see #invalidateAllKeys
      */
-    public void invalidateKey(int keyIndex) {
-        if (mKeys == null) return;
-        if (keyIndex < 0 || keyIndex >= mKeys.length) {
+    public void invalidateKey(Key key) {
+        if (key == null)
             return;
-        }
-        final Key key = mKeys[keyIndex];
         mInvalidatedKey = key;
         mDirtyRect.union(key.x + getPaddingLeft(), key.y + getPaddingTop(),
                 key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop());
@@ -1118,7 +939,7 @@
         Key popupKey = mKeys[keyIndex];
         boolean result = onLongPress(popupKey);
         if (result) {
-            showPreview(NOT_A_KEY);
+            dismissKeyPreview();
         }
         return result;
     }
@@ -1208,6 +1029,11 @@
         return false;
     }
 
+    // TODO: Should cleanup after refactoring mini-keyboard. 
+    public boolean isMiniKeyboardOnScreen() {
+        return mMiniKeyboardOnScreen;
+    }
+
     private int getTouchX(float x) {
         return (int)x - getPaddingLeft();
     }
@@ -1234,7 +1060,7 @@
 
         // We must disable gesture detector while mini-keyboard is on the screen.
         if (!mMiniKeyboardOnScreen && mGestureDetector.onTouchEvent(me)) {
-            showPreview(NOT_A_KEY);
+            dismissKeyPreview();
             mHandler.cancelKeyTimers();
             return true;
         }
@@ -1261,20 +1087,20 @@
         if (pointerCount != mOldPointerCount) {
             if (pointerCount == 1) {
                 // Send a down event for the latest pointer
-                onDownEvent(touchX, touchY, eventTime);
+                mPointerTracker.onDownEvent(touchX, touchY, eventTime);
                 // If it's an up action, then deliver the up as well.
                 if (action == MotionEvent.ACTION_UP) {
-                    onUpEvent(touchX, touchY, eventTime);
+                    mPointerTracker.onUpEvent(touchX, touchY, eventTime);
                 }
             } else {
                 // Send an up event for the last pointer
-                onUpEvent(mOldPointerX, mOldPointerY, eventTime);
+                mPointerTracker.onUpEvent(mOldPointerX, mOldPointerY, eventTime);
             }
             mOldPointerCount = pointerCount;
             return true;
         } else {
             if (pointerCount == 1) {
-                onModifiedTouchEvent(action, touchX, touchY, eventTime);
+                mPointerTracker.onModifiedTouchEvent(action, touchX, touchY, eventTime);
                 mOldPointerX = touchX;
                 mOldPointerY = touchY;
                 return true;
@@ -1284,112 +1110,6 @@
         return false;
     }
 
-    private void onModifiedTouchEvent(int action, int touchX, int touchY, long eventTime) {
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                onDownEvent(touchX, touchY, eventTime);
-                break;
-            case MotionEvent.ACTION_MOVE:
-                onMoveEvent(touchX, touchY, eventTime);
-                break;
-            case MotionEvent.ACTION_UP:
-                onUpEvent(touchX, touchY, eventTime);
-                break;
-            case MotionEvent.ACTION_CANCEL:
-                onCancelEvent(touchX, touchY, eventTime);
-                break;
-        }
-    }
-
-    private void onDownEvent(int touchX, int touchY, long eventTime) {
-        int keyIndex = mProximityKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null);
-        mCurrentKey = keyIndex;
-        mStartX = touchX;
-        mStartY = touchY;
-        mDebouncer.startMoveDebouncing(touchX, touchY);
-        mDebouncer.startTimeDebouncing(eventTime);
-        checkMultiTap(eventTime, keyIndex);
-        mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ? mKeys[keyIndex].codes[0] : 0);
-        if (keyIndex >= 0 && mKeys[keyIndex].repeatable) {
-            repeatKey(keyIndex);
-            mHandler.startKeyRepeatTimer(REPEAT_START_DELAY, keyIndex);
-        }
-        if (keyIndex != NOT_A_KEY) {
-            mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT);
-        }
-        showPreview(keyIndex);
-        mDebouncer.updateMoveDebouncing(touchX, touchY);
-    }
-
-    private void onMoveEvent(int touchX, int touchY, long eventTime) {
-        int keyIndex = mProximityKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null);
-        if (keyIndex != NOT_A_KEY) {
-            if (mCurrentKey == NOT_A_KEY) {
-                mDebouncer.updateTimeDebouncing(eventTime);
-                mCurrentKey = keyIndex;
-                mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT);
-            } else if (mDebouncer.isMinorMoveBounce(touchX, touchY, keyIndex, mCurrentKey)) {
-                mDebouncer.updateTimeDebouncing(eventTime);
-            } else {
-                resetMultiTap();
-                mDebouncer.resetTimeDebouncing(eventTime, mCurrentKey);
-                mDebouncer.resetMoveDebouncing();
-                mCurrentKey = keyIndex;
-                mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT);
-            }
-        } else {
-            mHandler.cancelLongPressTimer();
-        }
-        /*
-         * While time debouncing is in effect, mCurrentKey holds the new key and mDebouncer
-         * holds the last key.  At ACTION_UP event if time debouncing will be in effect
-         * eventually, the last key should be sent as the result.  In such case mCurrentKey
-         * should not be showed as popup preview.
-         */
-        showPreview(mDebouncer.isMinorTimeBounce() ? mDebouncer.getLastKey() : mCurrentKey);
-        mDebouncer.updateMoveDebouncing(touchX, touchY);
-    }
-
-    private void onUpEvent(int touchX, int touchY, long eventTime) {
-        int keyIndex = mProximityKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null);
-        boolean wasInKeyRepeat = mHandler.isInKeyRepeat();
-        mHandler.cancelKeyTimers();
-        mHandler.cancelPopupPreview();
-        if (mDebouncer.isMinorMoveBounce(touchX, touchY, keyIndex, mCurrentKey)) {
-            mDebouncer.updateTimeDebouncing(eventTime);
-        } else {
-            resetMultiTap();
-            mDebouncer.resetTimeDebouncing(eventTime, mCurrentKey);
-            mCurrentKey = keyIndex;
-        }
-        if (mDebouncer.isMinorTimeBounce()) {
-            mCurrentKey = mDebouncer.getLastKey();
-            touchX = mDebouncer.getLastCodeX();
-            touchY = mDebouncer.getLastCodeY();
-        }
-        showPreview(NOT_A_KEY);
-        // If we're not on a repeating key (which sends on a DOWN event)
-        if (!wasInKeyRepeat && !mMiniKeyboardOnScreen) {
-            detectAndSendKey(mCurrentKey, touchX, touchY, eventTime);
-        }
-        invalidateKey(keyIndex);
-    }
-
-    private void onCancelEvent(int touchX, int touchY, long eventTime) {
-        mHandler.cancelKeyTimers();
-        mHandler.cancelPopupPreview();
-        dismissPopupKeyboard();
-        showPreview(NOT_A_KEY);
-        invalidateKey(mCurrentKey);
-    }
-
-    private void repeatKey(int keyIndex) {
-        Key key = mKeys[keyIndex];
-        // While key is repeating, because there is no need to handle multi-tap key, we can pass
-        // -1 as eventTime argument.
-        detectAndSendKey(keyIndex, key.x, key.y, -1);
-    }
-
     protected void swipeRight() {
         mKeyboardActionListener.swipeRight();
     }
@@ -1424,7 +1144,7 @@
         closing();
     }
 
-    private void dismissPopupKeyboard() {
+    public void dismissPopupKeyboard() {
         if (mPopupKeyboard.isShowing()) {
             mPopupKeyboard.dismiss();
             mMiniKeyboardOnScreen = false;
@@ -1439,30 +1159,4 @@
         }
         return false;
     }
-
-    private void resetMultiTap() {
-        mLastSentIndex = NOT_A_KEY;
-        mTapCount = 0;
-        mLastTapTime = -1;
-        mInMultiTap = false;
-    }
-
-    private void checkMultiTap(long eventTime, int keyIndex) {
-        if (keyIndex == NOT_A_KEY) return;
-        Key key = mKeys[keyIndex];
-        if (key.codes.length > 1) {
-            mInMultiTap = true;
-            if (eventTime < mLastTapTime + MULTITAP_INTERVAL
-                    && keyIndex == mLastSentIndex) {
-                mTapCount = (mTapCount + 1) % key.codes.length;
-                return;
-            } else {
-                mTapCount = -1;
-                return;
-            }
-        }
-        if (eventTime > mLastTapTime + MULTITAP_INTERVAL || keyIndex != mLastSentIndex) {
-            resetMultiTap();
-        }
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/PointerTracker.java b/java/src/com/android/inputmethod/latin/PointerTracker.java
new file mode 100644
index 0000000..0c35ea9
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/PointerTracker.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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.
+ */
+
+package com.android.inputmethod.latin;
+
+import com.android.inputmethod.latin.LatinKeyboardBaseView.OnKeyboardActionListener;
+import com.android.inputmethod.latin.LatinKeyboardBaseView.UIHandler;
+
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.Keyboard.Key;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+public class PointerTracker {
+    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();
+    }
+
+    // Timing constants
+    private static final int REPEAT_START_DELAY = 400;
+    /* package */  static final int REPEAT_INTERVAL = 50; // ~20 keys per second
+    private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
+    private static final int MULTITAP_INTERVAL = 800; // milliseconds
+    private static final int KEY_DEBOUNCE_TIME = 70;
+
+    // Miscellaneous constants
+    private static final int NOT_A_KEY = LatinKeyboardBaseView.NOT_A_KEY;
+    private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE };
+
+    private final UIProxy mProxy;
+    private final UIHandler mHandler;
+    private final ProximityKeyDetector mKeyDetector;
+    private OnKeyboardActionListener mListener;
+
+    private Key[] mKeys;
+    private int mKeyDebounceThresholdSquared = -1;
+
+    private int mCurrentKey = NOT_A_KEY;
+    private int mStartX;
+    private int mStartY;
+
+    // for move de-bouncing
+    private int mLastCodeX;
+    private int mLastCodeY;
+    private int mLastX;
+    private int mLastY;
+
+    // for time de-bouncing
+    private int mLastKey;
+    private long mLastKeyTime;
+    private long mLastMoveTime;
+    private long mCurrentKeyTime;
+
+    // For multi-tap
+    private int mLastSentIndex;
+    private int mTapCount;
+    private long mLastTapTime;
+    private boolean mInMultiTap;
+    private final StringBuilder mPreviewLabel = new StringBuilder(1);
+
+    // pressed key
+    private int mPreviousKey;
+
+    public PointerTracker(UIHandler handler, ProximityKeyDetector keyDetector, UIProxy proxy) {
+        if (proxy == null || handler == null || keyDetector == null)
+            throw new NullPointerException();
+        mProxy = proxy;
+        mHandler = handler;
+        mKeyDetector = keyDetector;
+        resetMultiTap();
+    }
+
+    public void setOnKeyboardActionListener(OnKeyboardActionListener listener) {
+        mListener = listener;
+    }
+
+    public void setKeyboard(Key[] keys, float hysteresisPixel) {
+        if (keys == null || hysteresisPixel < 1.0f)
+            throw new IllegalArgumentException();
+        mKeys = keys;
+        mKeyDebounceThresholdSquared = (int)(hysteresisPixel * hysteresisPixel);
+    }
+
+    public Key getKey(int keyIndex) {
+        return (keyIndex >= 0 && keyIndex < mKeys.length) ? mKeys[keyIndex] : null;
+    }
+
+    public void updateKey(int keyIndex) {
+        int oldKeyIndex = mPreviousKey;
+        mPreviousKey = keyIndex;
+        if (keyIndex != oldKeyIndex) {
+            if (oldKeyIndex != NOT_A_KEY && oldKeyIndex < mKeys.length) {
+                // if new key index is not a key, old key was just released inside of the key.
+                final boolean inside = (keyIndex == NOT_A_KEY);
+                mKeys[oldKeyIndex].onReleased(inside);
+                mProxy.invalidateKey(mKeys[oldKeyIndex]);
+            }
+            if (keyIndex != NOT_A_KEY && keyIndex < mKeys.length) {
+                mKeys[keyIndex].onPressed();
+                mProxy.invalidateKey(mKeys[keyIndex]);
+            }
+        }
+    }
+
+    public void onModifiedTouchEvent(int action, int touchX, int touchY, long eventTime) {
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                onDownEvent(touchX, touchY, eventTime);
+                break;
+            case MotionEvent.ACTION_MOVE:
+                onMoveEvent(touchX, touchY, eventTime);
+                break;
+            case MotionEvent.ACTION_UP:
+                onUpEvent(touchX, touchY, eventTime);
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                onCancelEvent(touchX, touchY, eventTime);
+                break;
+        }
+    }
+
+    public void onDownEvent(int touchX, int touchY, long eventTime) {
+        int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null);
+        mCurrentKey = keyIndex;
+        mStartX = touchX;
+        mStartY = touchY;
+        startMoveDebouncing(touchX, touchY);
+        startTimeDebouncing(eventTime);
+        checkMultiTap(eventTime, keyIndex);
+        if (mListener != null) {
+            int primaryCode = (keyIndex != NOT_A_KEY) ? mKeys[keyIndex].codes[0] : 0;
+            mListener.onPress(primaryCode);
+        }
+        if (keyIndex >= 0 && mKeys[keyIndex].repeatable) {
+            repeatKey(keyIndex);
+            mHandler.startKeyRepeatTimer(REPEAT_START_DELAY, keyIndex, this);
+        }
+        if (keyIndex != NOT_A_KEY) {
+            mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT);
+        }
+        showKeyPreviewAndUpdateKey(keyIndex);
+        updateMoveDebouncing(touchX, touchY);
+    }
+
+    public void onMoveEvent(int touchX, int touchY, long eventTime) {
+        int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null);
+        if (keyIndex != NOT_A_KEY) {
+            if (mCurrentKey == NOT_A_KEY) {
+                updateTimeDebouncing(eventTime);
+                mCurrentKey = keyIndex;
+                mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT);
+            } else if (isMinorMoveBounce(touchX, touchY, keyIndex, mCurrentKey)) {
+                updateTimeDebouncing(eventTime);
+            } else {
+                resetMultiTap();
+                resetTimeDebouncing(eventTime, mCurrentKey);
+                resetMoveDebouncing();
+                mCurrentKey = keyIndex;
+                mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT);
+            }
+        } else {
+            mHandler.cancelLongPressTimer();
+        }
+        /*
+         * While time debouncing is in effect, mCurrentKey holds the new key and this tracker
+         * holds the last key.  At ACTION_UP event if time debouncing will be in effect
+         * eventually, the last key should be sent as the result.  In such case mCurrentKey
+         * should not be showed as popup preview.
+         */
+        showKeyPreviewAndUpdateKey(isMinorTimeBounce() ? mLastKey : mCurrentKey);
+        updateMoveDebouncing(touchX, touchY);
+    }
+
+    public void onUpEvent(int touchX, int touchY, long eventTime) {
+        int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null);
+        boolean wasInKeyRepeat = mHandler.isInKeyRepeat();
+        mHandler.cancelKeyTimers();
+        mHandler.cancelPopupPreview();
+        if (isMinorMoveBounce(touchX, touchY, keyIndex, mCurrentKey)) {
+            updateTimeDebouncing(eventTime);
+        } else {
+            resetMultiTap();
+            resetTimeDebouncing(eventTime, mCurrentKey);
+            mCurrentKey = keyIndex;
+        }
+        if (isMinorTimeBounce()) {
+            mCurrentKey = mLastKey;
+            touchX = mLastCodeX;
+            touchY = 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 (keyIndex != NOT_A_KEY && keyIndex < mKeys.length)
+            mProxy.invalidateKey(mKeys[keyIndex]);
+    }
+
+    public void onCancelEvent(int touchX, int touchY, long eventTime) {
+        mHandler.cancelKeyTimers();
+        mHandler.cancelPopupPreview();
+        mProxy.dismissPopupKeyboard();
+        showKeyPreviewAndUpdateKey(NOT_A_KEY);
+        int keyIndex = mCurrentKey;
+        if (keyIndex != NOT_A_KEY && keyIndex < mKeys.length)
+           mProxy.invalidateKey(mKeys[keyIndex]);
+    }
+
+    public void repeatKey(int keyIndex) {
+        Key key = mKeys[keyIndex];
+        // While key is repeating, because there is no need to handle multi-tap key, we can pass
+        // -1 as eventTime argument.
+        detectAndSendKey(keyIndex, key.x, key.y, -1);
+    }
+
+    // These package scope methods are only for debugging purpose.
+    /* package */ int getStartX() {
+        return mStartX;
+    }
+
+    /* package */ int getStartY() {
+        return mStartY;
+    }
+
+    /* package */ int getLastX() {
+        return mLastX;
+    }
+
+    /* package */ int getLastY() {
+        return mLastY;
+    }
+
+    private void startMoveDebouncing(int x, int y) {
+        mLastCodeX = x;
+        mLastCodeY = y;
+    }
+
+    private void updateMoveDebouncing(int x, int y) {
+        mLastX = x;
+        mLastY = y;
+    }
+
+    private void resetMoveDebouncing() {
+        mLastCodeX = mLastX;
+        mLastCodeY = mLastY;
+    }
+
+    private boolean isMinorMoveBounce(int x, int y, int newKey, int curKey) {
+        if (mKeys == null || mKeyDebounceThresholdSquared < 0)
+            throw new IllegalStateException("keyboard and/or hysteresis not set");
+        if (newKey == curKey) {
+            return true;
+        } else if (curKey >= 0 && curKey < mKeys.length) {
+            return getSquareDistanceToKeyEdge(x, y, mKeys[curKey])
+                    < mKeyDebounceThresholdSquared;
+        } else {
+            return false;
+        }
+    }
+
+    private static int getSquareDistanceToKeyEdge(int x, int y, Key key) {
+        final int left = key.x;
+        final int right = key.x + key.width;
+        final int top = key.y;
+        final int bottom = key.y + key.height;
+        final int edgeX = x < left ? left : (x > right ? right : x);
+        final int edgeY = y < top ? top : (y > bottom ? bottom : y);
+        final int dx = x - edgeX;
+        final int dy = y - edgeY;
+        return dx * dx + dy * dy;
+    }
+
+    private void startTimeDebouncing(long eventTime) {
+        mLastKey = NOT_A_KEY;
+        mLastKeyTime = 0;
+        mCurrentKeyTime = 0;
+        mLastMoveTime = eventTime;
+    }
+
+    private void updateTimeDebouncing(long eventTime) {
+        mCurrentKeyTime += eventTime - mLastMoveTime;
+        mLastMoveTime = eventTime;
+    }
+
+    private void resetTimeDebouncing(long eventTime, int currentKey) {
+        mLastKey = currentKey;
+        mLastKeyTime = mCurrentKeyTime + eventTime - mLastMoveTime;
+        mCurrentKeyTime = 0;
+        mLastMoveTime = eventTime;
+    }
+
+    private boolean isMinorTimeBounce() {
+        return mCurrentKeyTime < mLastKeyTime && mCurrentKeyTime < KEY_DEBOUNCE_TIME
+                && mLastKey != NOT_A_KEY;
+    }
+
+    private void showKeyPreviewAndUpdateKey(int keyIndex) {
+        updateKey(keyIndex);
+        mProxy.showPreview(keyIndex, this);
+    }
+
+    private void detectAndSendKey(int index, int x, int y, long eventTime) {
+        if (index != NOT_A_KEY && index < mKeys.length) {
+            final Key key = mKeys[index];
+            OnKeyboardActionListener listener = mListener;
+            if (key.text != null) {
+                if (listener != null) {
+                    listener.onText(key.text);
+                    listener.onRelease(NOT_A_KEY);
+                }
+            } else {
+                int code = key.codes[0];
+                //TextEntryState.keyPressedAt(key, x, y);
+                int[] codes = mKeyDetector.newCodeArray();
+                mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes);
+                // Multi-tap
+                if (mInMultiTap) {
+                    if (mTapCount != -1) {
+                        mListener.onKey(Keyboard.KEYCODE_DELETE, KEY_DELETE, x, y);
+                    } else {
+                        mTapCount = 0;
+                    }
+                    code = key.codes[mTapCount];
+                }
+                /*
+                 * Swap the first and second values in the codes array if the primary code is not
+                 * the first value but the second value in the array. This happens when key
+                 * debouncing is in effect.
+                 */
+                if (codes.length >= 2 && codes[0] != code && codes[1] == code) {
+                    codes[1] = codes[0];
+                    codes[0] = code;
+                }
+                if (listener != null) {
+                    listener.onKey(code, codes, x, y);
+                    listener.onRelease(code);
+                }
+            }
+            mLastSentIndex = index;
+            mLastTapTime = eventTime;
+        }
+    }
+
+    /**
+     * Handle multi-tap keys by producing the key label for the current multi-tap state.
+     */
+    public CharSequence getPreviewText(Key key) {
+        if (mInMultiTap) {
+            // Multi-tap
+            mPreviewLabel.setLength(0);
+            mPreviewLabel.append((char) key.codes[mTapCount < 0 ? 0 : mTapCount]);
+            return mPreviewLabel;
+        } else {
+            return key.label;
+        }
+    }
+
+    private void resetMultiTap() {
+        mLastSentIndex = NOT_A_KEY;
+        mTapCount = 0;
+        mLastTapTime = -1;
+        mInMultiTap = false;
+    }
+
+    private void checkMultiTap(long eventTime, int keyIndex) {
+        if (keyIndex == NOT_A_KEY) return;
+        Key key = mKeys[keyIndex];
+        if (key.codes.length > 1) {
+            mInMultiTap = true;
+            if (eventTime < mLastTapTime + MULTITAP_INTERVAL && keyIndex == mLastSentIndex) {
+                mTapCount = (mTapCount + 1) % key.codes.length;
+                return;
+            } else {
+                mTapCount = -1;
+                return;
+            }
+        }
+        if (eventTime > mLastTapTime + MULTITAP_INTERVAL || keyIndex != mLastSentIndex) {
+            resetMultiTap();
+        }
+    }
+}
\ No newline at end of file
diff --git a/native/Android.mk b/native/Android.mk
index b294469..e9ceceb 100644
--- a/native/Android.mk
+++ b/native/Android.mk
@@ -8,8 +8,8 @@
 	src/dictionary.cpp \
 	src/char_utils.cpp
 
-LOCAL_NDK_VERSION := 4
-LOCAL_SDK_VERSION := 8
+#LOCAL_NDK_VERSION := 4
+#LOCAL_SDK_VERSION := 8
 
 LOCAL_MODULE := libjni_latinime