am 4c5452c1: (-s ours) Import revised translations.  DO NOT MERGE.

* commit '4c5452c15ea301ce9b82b384a437a9868b1c9de1':
  Import revised translations.  DO NOT MERGE.
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 9b498f4..505e41b 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -49,6 +49,7 @@
 
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjni_latinime_intermediates)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjni_latinime_intermediates)
+$(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/more_suggestions_hint.png b/java/res/drawable-hdpi/more_suggestions_hint.png
deleted file mode 100644
index 9360475..0000000
--- a/java/res/drawable-hdpi/more_suggestions_hint.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/more_suggestions_hint_holo.png b/java/res/drawable-hdpi/more_suggestions_hint_holo.png
deleted file mode 100644
index 9360475..0000000
--- a/java/res/drawable-hdpi/more_suggestions_hint_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/more_suggestions_hint.png b/java/res/drawable-mdpi/more_suggestions_hint.png
deleted file mode 100644
index 7352810..0000000
--- a/java/res/drawable-mdpi/more_suggestions_hint.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/more_suggestions_hint_holo.png b/java/res/drawable-mdpi/more_suggestions_hint_holo.png
deleted file mode 100644
index 7352810..0000000
--- a/java/res/drawable-mdpi/more_suggestions_hint_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/more_suggestions_hint.png b/java/res/drawable-xhdpi/more_suggestions_hint.png
deleted file mode 100644
index 35fb420..0000000
--- a/java/res/drawable-xhdpi/more_suggestions_hint.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/more_suggestions_hint_holo.png b/java/res/drawable-xhdpi/more_suggestions_hint_holo.png
deleted file mode 100644
index 35fb420..0000000
--- a/java/res/drawable-xhdpi/more_suggestions_hint_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable/btn_keyboard_key_gingerbread.xml b/java/res/drawable/btn_keyboard_key_gingerbread.xml
index 4a113a8..5b4399e 100644
--- a/java/res/drawable/btn_keyboard_key_gingerbread.xml
+++ b/java/res/drawable/btn_keyboard_key_gingerbread.xml
@@ -23,6 +23,13 @@
     <item android:state_single="true"
           android:drawable="@drawable/btn_keyboard_key_dark_normal" />
 
+    <!-- Action keys. -->
+
+    <item android:state_active="true" android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_dark_pressed" />
+    <item android:state_active="true"
+          android:drawable="@drawable/btn_keyboard_key_dark_normal" />
+
     <!-- Toggle keys. Use checkable/checked state. -->
 
     <item android:state_checkable="true" android:state_checked="true" android:state_pressed="true"
@@ -34,7 +41,7 @@
     <item android:state_checkable="true"
           android:drawable="@drawable/btn_keyboard_key_dark_normal_off" />
 
-    <!-- Normal keys -->
+    <!-- Normal keys. -->
 
     <item android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_light_pressed" />
diff --git a/java/res/drawable/btn_keyboard_key_stone.xml b/java/res/drawable/btn_keyboard_key_stone.xml
index 27932e8..9bc3f18 100644
--- a/java/res/drawable/btn_keyboard_key_stone.xml
+++ b/java/res/drawable/btn_keyboard_key_stone.xml
@@ -23,6 +23,13 @@
     <item android:state_single="true"
           android:drawable="@drawable/btn_keyboard_key_normal_stone" />
 
+    <!-- Action keys. -->
+
+    <item android:state_active="true" android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+    <item android:state_active="true"
+          android:drawable="@drawable/btn_keyboard_key_normal_stone" />
+
     <!-- Toggle keys. Use checkable/checked state. -->
 
     <item android:state_checkable="true" android:state_checked="true"
@@ -35,7 +42,7 @@
     <item android:state_checkable="true"
           android:drawable="@drawable/btn_keyboard_key_normal_off_stone" />
 
-    <!-- Normal keys -->
+    <!-- Normal keys. -->
 
     <item android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml
index 13560e0..2e0cddc 100644
--- a/java/res/layout/input_view.xml
+++ b/java/res/layout/input_view.xml
@@ -18,7 +18,7 @@
 */
 -->
 
-<LinearLayout
+<com.android.inputmethod.latin.InputView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
     android:orientation="vertical"
@@ -61,4 +61,4 @@
         android:layout_alignParentBottom="true"
         android:layout_width="match_parent"
         android:layout_height="wrap_content" />
-</LinearLayout>
+</com.android.inputmethod.latin.InputView>
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index 2e14525..73ef7f4 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -44,7 +44,7 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Stel voorstelle vir herkorrigerings"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Outohoofletters"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Voeg woordeboeke by"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Hoofwoordeboek"</string>
+    <string name="main_dictionary" msgid="4798763781818361168">"Hoof woordeboek"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Wys voorstelle vir korrigering"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Wys voorgestelde woorde terwyl jy tik"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Wys altyd"</string>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index 5eeb75b..b39ee3d 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"ድጋሚ ለማስተካከል ጥቆማዎችን አዘጋጅ"</string>
     <string name="auto_cap" msgid="1719746674854628252">"ራስ-ሰር አቢይ ማድረግ"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"መዝገበ ቃላቶች ጨምር"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"ዋና መዝገበ ቃላት"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"የማስተካከያ ጥቆማዎች አሳይ"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"እየተየብክ ተመራጭ ቃላትን አሳይ"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"ሁልጊዜ አሳይ"</string>
@@ -52,7 +53,7 @@
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"ሁልጊዜ ደብቅ"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"የቅንብሮች ቁልፍ አሳይ"</string>
     <string name="auto_correction" msgid="4979925752001319458">"በራስ ማስተካከል"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"የቦታ ቁልፍ እና ሥርዓተ ነጥብ በስህተት የተተየቡ ቃላትን  በራስሰር ያስተካክላሉ።"</string>
+    <string name="auto_correction_summary" msgid="5625751551134658006">"የቦታቁልፍ እና ሥርዓተ ነጥብ በስህተት የተተየቡ ቃላትን  በራስሰር ያስተካክላሉ።"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"ውጪ"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"መጠነኛ"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"ኃይለኛ"</string>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index 87395d8..9f1209a 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"تعيين اقتراحات لعمليات إعادة التصحيح"</string>
     <string name="auto_cap" msgid="1719746674854628252">"أحرف كبيرة تلقائيًا"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"القواميس الإضافية"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"القاموس الرئيسي"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"عرض اقتراحات التصحيح"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"عرض الكلمات المقترحة أثناء الكتابة"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"عرض دومًا"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 9c05900..47e81e7 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Задаване на предложения за повторни поправки"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Автоматично поставяне на главни букви"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Добавени речници"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Основен речник"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Показване на предложения за поправка"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Показване на предложения, докато пишете"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Винаги да се показва"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index cab232f..a5a3e74 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nastavit návrhy pro opětovné opravy"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Velká písmena automaticky"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Doplňkové slovníky"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Hlavní slovník"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Zobrazit návrhy oprav"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Zobrazovat navržená slova během psaní"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vždy zobrazovat"</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index b5ab6c4..56ade85 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Angiv forslag til fornyet rettelse"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Skriv aut. med stort"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Tillægsordbøger"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Hovedordbog"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Vis rettelsesforslag"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Vis ordforslag under indtastning"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vis altid"</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index f7fb78c..fac7fac 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Vorschläge für Korrekturen festlegen"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschr."</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Add-on-Wörterbücher"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Allgemeines Wörterbuch"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Änderungsvorschläge anzeigen"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Vorgeschlagene Wörter während des Tippens anzeigen"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Immer anzeigen"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index ce91243..2b9ebf5 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Ορισμός προτάσεων για επαναλήψεις διορθώσεων"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Αυτόματη χρήση κεφαλαίων"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Πρόσθετα λεξικά"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Κύριο λεξικό"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Εμφάνιση προτάσεων διόρθωσης"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Προβολή προτεινόμενων λέξεων κατά την πληκτρολόγηση"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Να εμφανίζεται πάντα"</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index f958821..86163ac 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Establecer sugerencias para realizar correcciones"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionarios complementarios"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Diccionario principal"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostrar sugerencias de correcciones"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Mostrar palabras sugeridas al escribir"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar siempre"</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 1c19c62..701c1e0 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Establecer sugerencias para nuevas correcciones"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionarios complementarios"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Diccionario principal"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostrar sugerencias de correcciones"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Muestra las palabras sugeridas mientras se escribe."</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar siempre"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index c25b66e..1d00851 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"تنظیم پیشنهادات برای تصحیح مجدد"</string>
     <string name="auto_cap" msgid="1719746674854628252">"نوشتن با حروف بزرگ خودکار"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"فرهنگ‌های لغت افزودنی"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"فرهنگ لغت اصلی"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"نمایش پیشنهادات تصحیح"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"نمایش واژه های پیشنهادی در حین تایپ"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"همیشه نمایش داده شود"</string>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index 9f02e4b..4f943cf 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Aseta korjausehdotuksia"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automaattiset isot kirjaimet"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Lisäsanakirjat"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Pääsanakirja"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Näytä korjausehdotukset"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Näytä sanaehdotukset kirjoitettaessa"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Näytä aina"</string>
@@ -63,7 +64,7 @@
     <string name="bigram_prediction_summary" msgid="1747261921174300098">"Käytä edellistä sanaa myös ennakointiin"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Tallennettu"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Siirry"</string>
-    <string name="label_next_key" msgid="362972844525672568">"Seur."</string>
+    <string name="label_next_key" msgid="362972844525672568">"Seuraava"</string>
     <string name="label_done_key" msgid="2441578748772529288">"Valmis"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Lähetä"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 04d72d1..33c3771 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Définir des suggestions de recorrection"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Majuscules auto"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dictionnaires complémentaires"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Dictionnaire principal"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Afficher les suggestions de correction"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Afficher les suggestions de terme lors de la saisie"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Toujours afficher"</string>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index fbb6491..3e5470f 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Postavite prijedloge za ponovne ispravke"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automatsko pisanje velikih slova"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Rječnici-dodaci"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Glavni rječnik"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Pokaži prijedloge ispravka"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Prikazivanje predloženih riječi prilikom upisivanja"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Uvijek prikaži"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 7f9833d..47801a5 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Javaslatok beállítása az újbóli javításokhoz"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automatikusan nagy kezdőbetű"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Bővítmények: szótárak"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Fő szótár"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Javítási ajánlások megjelenítése"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"A javasolt szavak megjelenítése gépelés közben"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mindig látszik"</string>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index ced70b3..7150de3 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Setel saran untuk koreksi ulang"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Kapitalisasi otomatis"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Kamus pengaya"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Kamus utama"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Tampilkan saran koreksi"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Tampilkan kata yang disarankan ketika mengetik"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Selalu tampilkan"</string>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index fe4a1e0..26aee30 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"הגדר הצעות עבור תיקונים חוזרים"</string>
     <string name="auto_cap" msgid="1719746674854628252">"הפיכת אותיות לרישיות באופן אוטומטי"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"הוספת מילונים"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"מילון ראשי"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"הצג הצעות לתיקונים"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"הצג הצעות למילים בעת הקלדה"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"הצג תמיד"</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index de26cf8..f45817f 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -23,7 +23,7 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Androidキーボード"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Androidキーボードの設定"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"入力オプション"</string>
-    <string name="spell_checker_service_name" msgid="2003013122022285508">"Androidスペルチェッカー"</string>
+    <string name="spell_checker_service_name" msgid="2003013122022285508">"Android校正"</string>
     <string name="android_spell_checker_settings" msgid="5822324635435443689">"スペルチェックの設定"</string>
     <string name="use_proximity_option_title" msgid="7469233942295924620">"近接データを使用"</string>
     <string name="use_proximity_option_summary" msgid="2857708859847261945">"スペルチェックでキーボードと同じような近接アルゴリズムを使用する"</string>
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"再修正の候補を挿入する"</string>
     <string name="auto_cap" msgid="1719746674854628252">"自動大文字変換"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"辞書を追加"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"メイン辞書"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"修正候補を表示する"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"入力中に入力候補を表示する"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"常に表示"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 03974fe..2b650a6 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"다시 수정하기 위한 추천사항 설정"</string>
     <string name="auto_cap" msgid="1719746674854628252">"자동 대문자화"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"사전 추가"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"기본 디렉토리"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"수정 제안 표시"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"글자를 입력하는 동안 추천 단어 표시"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"항상 표시"</string>
diff --git a/java/res/values-land/dimens.xml b/java/res/values-land/dimens.xml
index 0f9bde8..4a5c5a4 100644
--- a/java/res/values-land/dimens.xml
+++ b/java/res/values-land/dimens.xml
@@ -25,7 +25,6 @@
     <!-- key_height + key_bottom_gap = popup_key_height -->
 <!--    <dimen name="key_height">0.260in</dimen>-->
     <dimen name="popup_key_height">0.280in</dimen>
-    <dimen name="keyboard_horizontal_edges_padding">0.0in</dimen>
 
     <fraction name="keyboard_top_padding">1.818%p</fraction>
     <fraction name="keyboard_bottom_padding">0.0%p</fraction>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index 9aa9cc5..622b059 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nustatyti pakartotinio pataisymo pasiūlymų"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automatinis didžiųjų raidžių rašymas"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Papildomi žodynai"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Pagrindinis žodynas"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Rodyti taisymo pasiūlymus"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Įvedant tekstą pateikti siūlomų žodžių"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Visada rodyti"</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index f347dbc..090ae1e 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Iestatīt atkārtotu labojumu ieteikumus"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automātiska lielo burtu lietošana"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Papildinājumu vārdnīcas"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Galvenā vārdnīca"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Rādīt labojumu ieteikumus"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Ievades laikā attēlot ieteiktos vārdus"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vienmēr rādīt"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 96935e5..597b60f 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Angi forslag for korrigeringer"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Stor forbokstav"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Tilleggsordbøker"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Hovedordliste"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Vis rettingsforslag"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Vis ordforslag under skriving"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vis alltid"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index ab731bf..9ffc6e7 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Suggesties instellen voor verbeteringen"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Auto-hoofdlettergebruik"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Woordenboeken toevoegen"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Algemeen woordenboek"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Correctievoorstellen weergeven"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Voorgestelde woorden weergeven tijdens typen"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Altijd weergeven"</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index d793bc9..31c9d4e 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Ustaw sugestie poprawek"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Wstawiaj wielkie litery"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dodatkowe słowniki"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Główny słownik"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Pokazuj propozycje poprawek"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Wyświetl proponowane słowa podczas wpisywania"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Zawsze pokazuj"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index b60aea3..41e10b5 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Definir sugestões para correções"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Letras maiúsculas automáticas"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicionários extras"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Dicionário principal"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostrar sugestões de correcção"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Apresentar sugestões de palavras ao escrever"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar sempre"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 045baa9..73eb932 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Definir sugestões para recorreções"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Capitaliz. automática"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicionários complementares"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Dicionário principal"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Exibir sugestões de correção"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Exibir sugestões de palavras durante a digitação"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar sempre"</string>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index 17a3437..97a254b 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Setaţi sugestii pentru rectificări"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalizare"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicţionare suplimentare"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Dicţionar principal"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Afişaţi sugestii de corectare"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Afişaţi sugestii de cuvinte în timpul introducerii textului"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Afişaţi întotdeauna"</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index 77a4894..cfc5022 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Показывать варианты исправления"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Заглавные автоматически"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Дополнительные словари"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Основной словарь"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Показать варианты исправлений"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Предлагать варианты слов во время ввода"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Всегда показывать"</string>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index 6312c59..ac10b85 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nastaviť návrhy pre opätovné opravy"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Veľké písmená automaticky"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Doplnkové slovníky"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Hlavný slovník"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Zobraziť návrhy opráv"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Zobrazovať navrhované slová počas písania"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vždy zobrazovať"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index a96108b..a742e1d 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nastavitev predlogov za vnovične popravke"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Samodejne velike začetnice"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Nastavitev slovarjev"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Glavni slovar"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Pokaži predloge popravkov"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Pokaži predlagane besede med tipkanjem"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vedno pokaži"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 1ef55b5..32792c2 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Подешавање предлога за поновне исправке"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Аутоматски унос великих слова"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Помоћни речници"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Главни речник"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Прикажи предлоге за исправку"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Приказивање предложених речи током уноса текста"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Увек прикажи"</string>
@@ -69,7 +70,7 @@
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
     <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
     <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"Пауза"</string>
+    <string name="label_pause_key" msgid="181098308428035340"></string>
     <string name="label_wait_key" msgid="6402152600878093134">"Чекај"</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Тренутни текст је %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Текст није унет"</string>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 27417bf..4e2cdd4 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Ställ in förslag för omkorrigeringar"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automatiska versaler"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Tilläggsordlistor"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Huvudordlistan"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Visa rättningsförslag"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Visar ordförslag när du skriver"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Visa alltid"</string>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index bd8d956..cf4111e 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Weka mapendekezo kwa ajili ya kusahihisha upya"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Uwekaji wa herufi kubwa kiotomatiki"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Nyongeza za kamusi"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Kamusi kuu"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Onyesha mapendekezo ya marekebisho"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Onyesha maneno yaliyopendekezwa wakati unachapa"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Onyesha kila wakati"</string>
diff --git a/java/res/values-sw600dp-land/dimens.xml b/java/res/values-sw600dp-land/dimens.xml
index c1cef1d..1b8c8a6 100644
--- a/java/res/values-sw600dp-land/dimens.xml
+++ b/java/res/values-sw600dp-land/dimens.xml
@@ -23,7 +23,6 @@
     <dimen name="keyboardHeight">45.0mm</dimen>
     <fraction name="minKeyboardHeight">45%p</fraction>
 
-    <dimen name="keyboard_horizontal_edges_padding">0dp</dimen>
     <fraction name="keyboard_top_padding">2.444%p</fraction>
     <fraction name="keyboard_bottom_padding">0.0%p</fraction>
     <fraction name="key_bottom_gap">4.911%p</fraction>
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml
index d02b4ea..3183023 100644
--- a/java/res/values-sw600dp/dimens.xml
+++ b/java/res/values-sw600dp/dimens.xml
@@ -26,7 +26,6 @@
 
     <dimen name="popup_key_height">10.0mm</dimen>
 
-    <dimen name="keyboard_horizontal_edges_padding">0.0mm</dimen>
     <fraction name="keyboard_top_padding">2.291%p</fraction>
     <fraction name="keyboard_bottom_padding">0.0%p</fraction>
     <fraction name="key_bottom_gap">3.750%p</fraction>
@@ -76,4 +75,5 @@
     <dimen name="suggestion_min_width">0.3in</dimen>
     <dimen name="suggestion_padding">12dip</dimen>
     <dimen name="suggestion_text_size">22dip</dimen>
+    <dimen name="more_suggestions_hint_text_size">33dip</dimen>
 </resources>
diff --git a/java/res/values-sw768dp-land/dimens.xml b/java/res/values-sw768dp-land/dimens.xml
index 8f9b006..664e8c1 100644
--- a/java/res/values-sw768dp-land/dimens.xml
+++ b/java/res/values-sw768dp-land/dimens.xml
@@ -23,7 +23,6 @@
     <dimen name="keyboardHeight">58.0mm</dimen>
     <fraction name="minKeyboardHeight">45%p</fraction>
 
-    <dimen name="keyboard_horizontal_edges_padding">0.0mm</dimen>
     <fraction name="keyboard_top_padding">1.896%p</fraction>
     <fraction name="keyboard_bottom_padding">0.0%p</fraction>
 
diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml
index bfc2593..bb4937d 100644
--- a/java/res/values-sw768dp/dimens.xml
+++ b/java/res/values-sw768dp/dimens.xml
@@ -24,7 +24,6 @@
     <fraction name="maxKeyboardHeight">50%p</fraction>
     <fraction name="minKeyboardHeight">-35.0%p</fraction>
 
-    <dimen name="keyboard_horizontal_edges_padding">0.0mm</dimen>
     <fraction name="keyboard_top_padding">2.291%p</fraction>
     <fraction name="keyboard_bottom_padding">0.0%p</fraction>
 
@@ -79,4 +78,5 @@
     <dimen name="suggestion_min_width">46dip</dimen>
     <dimen name="suggestion_padding">8dip</dimen>
     <dimen name="suggestion_text_size">22dip</dimen>
+    <dimen name="more_suggestions_hint_text_size">33dip</dimen>
 </resources>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index d442cdb..819c417 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"ตั้งค่าคำแนะนำสำหรับการแก้ไขซ้ำ"</string>
     <string name="auto_cap" msgid="1719746674854628252">"ปรับเป็นตัวพิมพ์ใหญ่อัตโนมัติ"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"พจนานุกรม Add-On"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"พจนานุกรมหลัก"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"แสดงคำแนะนำการแก้ไข"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"แสดงคำที่แนะนำขณะพิมพ์"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"แสดงทุกครั้ง"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index d61cda9..f5f0dfb 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Magtakda ng mga suhestyon para sa mga muling pagtatama"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalization"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Mga diksyunaryo na add-on"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Pangunahing diksyunaryo"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Magpakita ng mga suhestiyon ng pagwawasto"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Ipakita ang mga iminumungkahing salita habang nagta-type"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Palaging ipakita"</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index f233fc5..23e79ca 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Yeniden düzeltmeler için önerileri ayarla"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Otomatik olarak büyük harf yap"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Ekli sözlükler"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Ana sözlük"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Düzeltme önerilerini göster"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Yazarken, önerilen kelimeleri görüntüle"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Her zaman göster"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index d8a59d6..2112420 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Установити пропозиції для повторних виправлень"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Авто викор. вел. літер"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Додані словники"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Основний словник"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Показувати пропозиції виправлень"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Відображати пропоновані слова під час вводу"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Завжди показувати"</string>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 3a837b4..0aaed0c 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Đặt đề xuất cho các sửa đổi lại"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Tự động viết hoa"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Thêm từ điển"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Từ điển chính"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Hiển thị gợi ý sửa"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Hiển thị từ được đề xuất khi nhập"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Luôn hiển thị"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index b26b649..d1ef9cd 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"设置更正建议"</string>
     <string name="auto_cap" msgid="1719746674854628252">"自动大写"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"附加词典"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"主词典"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"显示更正建议"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"键入时显示建议的字词"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"始终显示"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index ea157ea..387ad1c 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"設定建議供重新更正"</string>
     <string name="auto_cap" msgid="1719746674854628252">"自動大寫"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"外掛字典"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"主要字典"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"顯示修正建議"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"輸入時顯示建議字詞"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"一律顯示"</string>
@@ -67,7 +68,7 @@
     <string name="label_done_key" msgid="2441578748772529288">"完成"</string>
     <string name="label_send_key" msgid="2815056534433717444">"傳送"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
-    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
     <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string>
     <string name="label_pause_key" msgid="181098308428035340">"暫停"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"等候"</string>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index e70ad6b..074dcbb 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -44,7 +44,8 @@
     <string name="enable_span_insert_summary" msgid="2947317657871394467">"Setha iziphakamiso zokulungisa kabusha"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Ukwenza ofeleba okuzenzakalelayo"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Faka izichazamazwi"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Isichazimazwi sakho ngqangi"</string>
+    <!-- no translation found for main_dictionary (4798763781818361168) -->
+    <skip />
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Bonisa ukusikesela kokulungisa"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Bonisa amagama aphakamisiwe ngenkathi uthayipha"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Bonisa njalo"</string>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 6e9461a..2a76321 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <declare-styleable name="KeyboardTheme">
+        <attr name="themeId" format="integer" />
         <!-- Keyboard style -->
         <attr name="keyboardStyle" format="reference" />
         <!-- LatinKeyboard style -->
@@ -118,10 +119,10 @@
 
     <declare-styleable name="SuggestionsView">
         <attr name="suggestionStripOption" format="integer">
+            <!-- This should be aligned with SuggestionsViewParams.AUTO_CORRECT_* and etc. -->
             <flag name="autoCorrectBold" value="0x01" />
             <flag name="autoCorrectUnderline" value="0x02" />
-            <flag name="autoCorrectInvert" value="0x04" />
-            <flag name="validTypedWordBold" value="0x08" />
+            <flag name="validTypedWordBold" value="0x04" />
         </attr>
         <attr name="colorTypedWord" format="color" />
         <attr name="colorAutoCorrect" format="color" />
@@ -132,7 +133,6 @@
         <attr name="alphaObsoleted" format="integer" />
         <attr name="suggestionsCountInStrip" format="integer" />
         <attr name="centerSuggestionPercentile" format="integer" />
-        <attr name="moreSuggestionsHint" format="reference" />
         <attr name="maxMoreSuggestionsRow" format="integer" />
         <attr name="minMoreSuggestionsWidth" format="float" />
     </declare-styleable>
@@ -145,19 +145,10 @@
         <!-- Minimum keyboard height represented in pixels, percentage of display height if fraction
              is positive, or percentage of display width if fraction is negative. -->
         <attr name="minKeyboardHeight" format="dimension|fraction" />
-        <!-- Keyboard top and bottom paddings. -->
+        <!-- Keyboard top, bottom, both horizontal edges paddings. -->
         <attr name="keyboardTopPadding" format="dimension|fraction" />
         <attr name="keyboardBottomPadding" format="dimension|fraction" />
-        <!-- Default width of a key, in pixels or percentage of display width.
-             If the value is zero, the actual key width will be determined to fill out the area up
-             to the right edge of the keyboard.
-             If the value is negative, the actual key width will be determined to fill out the
-             area between the nearest key on the left hand side and the right edge of the keyboard.
-             -->
-        <attr name="keyWidth" format="dimension|fraction|enum">
-            <enum name="fillRight" value="-1" />
-            <enum name="fillBoth" value="-2" />
-        </attr>
+        <attr name="keyboardHorizontalEdgesPadding" format="dimension|fraction" />
         <!-- Default height of a row (key height + vertical gap), in pixels or percentage of
              keyboard height. -->
         <attr name="rowHeight" format="dimension|fraction" />
@@ -259,6 +250,17 @@
         <!-- Visual insets -->
         <attr name="visualInsetsLeft" format="dimension|fraction" />
         <attr name="visualInsetsRight" format="dimension|fraction" />
+        <!-- Width of the key, in pixels or percentage of display width.
+             If the value is fillRight, the actual key width will be determined to fill out the area
+             up to the right edge of the keyboard.
+             If the value is fillBoth, the actual key width will be determined to fill out the
+             area between the nearest key on the left hand side and the right edge of the keyboard.
+             -->
+        <!-- This should be aligned with KeyboardBuilder.Row.KEYWIDTH_* -->
+        <attr name="keyWidth" format="dimension|fraction|enum">
+            <enum name="fillRight" value="-1" />
+            <enum name="fillBoth" value="-2" />
+        </attr>
         <!-- The X-coordinate of upper right corner of this key including horizontal gap.
              If the value is negative, the origin is the right edge of the keyboard. -->
         <attr name="keyXPos" format="dimension|fraction" />
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 8cfa680..9992478 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -39,6 +39,7 @@
     <bool name="config_default_bigram_prediction">false</bool>
     <bool name="config_default_compat_recorrection_enabled">true</bool>
     <bool name="config_default_sound_enabled">false</bool>
+    <bool name="config_default_vibration_enabled">true</bool>
     <bool name="config_auto_correction_spacebar_led_enabled">true</bool>
     <!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false -->
     <bool name="config_show_mini_keyboard_at_touched_point">false</bool>
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 8488c77..8b4cd6d 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -29,9 +29,9 @@
     <dimen name="mini_keyboard_horizontal_edges_padding">16dip</dimen>
     <dimen name="mini_keyboard_key_horizontal_padding">8dip</dimen>
 
-    <dimen name="keyboard_horizontal_edges_padding">0dp</dimen>
     <fraction name="keyboard_top_padding">1.556%p</fraction>
     <fraction name="keyboard_bottom_padding">4.669%p</fraction>
+    <fraction name="keyboard_horizontal_edges_padding">0%p</fraction>
     <fraction name="key_bottom_gap">6.250%p</fraction>
     <fraction name="key_horizontal_gap">1.352%p</fraction>
 
@@ -87,11 +87,12 @@
     <fraction name="min_more_suggestions_width">90%</fraction>
     <fraction name="more_suggestions_info_ratio">18%</fraction>
     <!-- key_preview_backing_height = more_suggestions_row_height * max_more_suggestions_row -->
-    <dimen name="key_preview_backing_height">160dip</dimen>
+    <dimen name="key_preview_backing_height">240dip</dimen>
     <dimen name="suggestions_strip_padding">0dip</dimen>
     <dimen name="suggestion_min_width">44dip</dimen>
     <dimen name="suggestion_padding">6dip</dimen>
     <dimen name="suggestion_text_size">18dip</dimen>
+    <dimen name="more_suggestions_hint_text_size">27dip</dimen>
     <integer name="suggestions_count_in_strip">3</integer>
     <integer name="center_suggestion_percentile">36</integer>
 
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index 4072ea4..75e22dd 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -31,6 +31,21 @@
     <!-- Word separator list is the union of all symbols except those that are not separators:
     magic_space_swapping_symbols | magic_space_stripping_symbols |
             magic_space_neutral_symbols \ symbols_excluded_from_word_separators -->
+    <!-- Symbol characters list that should switch back to the main layout -->
+    <!--  \u0022: Quotation mark (double quotation mark)
+          \u0027: Apostrophe (single quotation mark)
+          \u2018: Left single quotation mark
+          \u2019: Right single quotation mark
+          \u201a: Single low-9 quotation mark
+          \u201b: Single high-reversed-9 quotation mark
+          \u201c: Left double quotation mark
+          \u201d: Right double quotation mark
+          \u201e: Double low-9 quotation mark
+          \u201f: Double high-reversed-9 quotation mark
+          \u00ab: Left-pointing double angle quotation mark
+          \u00bb: Right-pointing double angle quotation mark  -->
+    <!-- string name="layout_switch_back_symbols">\u0022\u0027\u2018\u2019\u201a\u201b\u201c\u201d\u201e\u201f\u00ab\u00bb</string> -->
+    <string name="layout_switch_back_symbols"></string>
 
     <!-- Label for "switch to more symbol" modifier key.  Must be short to fit on key! -->
     <string name="label_to_more_symbol_key">= \\ &lt;</string>
diff --git a/java/res/values/keypress-vibration-durations.xml b/java/res/values/keypress-vibration-durations.xml
new file mode 100644
index 0000000..2569f23
--- /dev/null
+++ b/java/res/values/keypress-vibration-durations.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <string-array name="keypress_vibration_durations" translatable="false">
+        <!-- Build.HARDWARE,duration_in_milliseconds -->
+        <item>herring,5</item>
+        <item>tuna,5</item>
+    </string-array>
+</resources>
diff --git a/java/res/values/keypress-volumes.xml b/java/res/values/keypress-volumes.xml
new file mode 100644
index 0000000..4d0e7a0
--- /dev/null
+++ b/java/res/values/keypress-volumes.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <string-array name="keypress_volumes" translatable="false">
+        <!-- Build.HARDWARE,volume -->
+        <item>herring,0.05</item>
+        <item>tuna,0.05</item>
+        <item>stingray,0.04</item>
+    </string-array>
+</resources>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 6aa9674..c6ea2a5 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -24,6 +24,7 @@
         <item name="moreKeysTemplate">@xml/kbd_mini_keyboard_template</item>
         <item name="keyboardTopPadding">@fraction/keyboard_top_padding</item>
         <item name="keyboardBottomPadding">@fraction/keyboard_bottom_padding</item>
+        <item name="keyboardHorizontalEdgesPadding">@fraction/keyboard_horizontal_edges_padding</item>
         <item name="horizontalGap">@fraction/key_horizontal_gap</item>
         <item name="verticalGap">@fraction/key_bottom_gap</item>
         <item name="maxMoreKeysColumn">@integer/config_max_more_keys_column</item>
@@ -102,7 +103,6 @@
         <item name="alphaObsoleted">50</item>
         <item name="suggestionsCountInStrip">@integer/suggestions_count_in_strip</item>
         <item name="centerSuggestionPercentile">@integer/center_suggestion_percentile</item>
-        <item name="moreSuggestionsHint">@drawable/more_suggestions_hint</item>
         <item name="maxMoreSuggestionsRow">@integer/max_more_suggestions_row</item>
         <item name="minMoreSuggestionsWidth">@fraction/min_more_suggestions_width</item>
     </style>
@@ -284,7 +284,6 @@
         <item name="alphaObsoleted">70</item>
         <item name="suggestionsCountInStrip">@integer/suggestions_count_in_strip</item>
         <item name="centerSuggestionPercentile">@integer/center_suggestion_percentile</item>
-        <item name="moreSuggestionsHint">@drawable/more_suggestions_hint_holo</item>
         <item name="maxMoreSuggestionsRow">@integer/max_more_suggestions_row</item>
         <item name="minMoreSuggestionsWidth">@fraction/min_more_suggestions_width</item>
     </style>
diff --git a/java/res/values/sudden-jumping-touch-event-device-list.xml b/java/res/values/sudden-jumping-touch-event-device-list.xml
index af1eefc..ba828a7 100644
--- a/java/res/values/sudden-jumping-touch-event-device-list.xml
+++ b/java/res/values/sudden-jumping-touch-event-device-list.xml
@@ -20,7 +20,7 @@
 <resources>
     <string-array name="sudden_jumping_touch_event_device_list" translatable="false">
         <!-- Nexus One -->
-        <item>passion</item>
+        <item>mahimahi</item>
         <!-- Droid -->
         <item>sholes</item>
     </string-array>
diff --git a/java/res/values/themes-basic-highcontrast.xml b/java/res/values/themes-basic-highcontrast.xml
index ce05980..9127235 100644
--- a/java/res/values/themes-basic-highcontrast.xml
+++ b/java/res/values/themes-basic-highcontrast.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <style name="KeyboardTheme.HighContrast" parent="KeyboardIcons">
+        <item name="themeId">1</item>
         <item name="keyboardStyle">@style/Keyboard</item>
         <item name="latinKeyboardStyle">@style/LatinKeyboard</item>
         <item name="keyboardViewStyle">@style/KeyboardView.HighContrast</item>
diff --git a/java/res/values/themes-basic.xml b/java/res/values/themes-basic.xml
index ff9fed5..6c0e16e 100644
--- a/java/res/values/themes-basic.xml
+++ b/java/res/values/themes-basic.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <style name="KeyboardTheme" parent="KeyboardIcons">
+        <item name="themeId">0</item>
         <item name="keyboardStyle">@style/Keyboard</item>
         <item name="latinKeyboardStyle">@style/LatinKeyboard</item>
         <item name="keyboardViewStyle">@style/KeyboardView</item>
diff --git a/java/res/values/themes-gingerbread.xml b/java/res/values/themes-gingerbread.xml
index be853eb..43bff50 100644
--- a/java/res/values/themes-gingerbread.xml
+++ b/java/res/values/themes-gingerbread.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <style name="KeyboardTheme.Gingerbread" parent="KeyboardIcons">
+        <item name="themeId">8</item>
         <item name="keyboardStyle">@style/Keyboard.Gingerbread</item>
         <item name="latinKeyboardStyle">@style/LatinKeyboard</item>
         <item name="keyboardViewStyle">@style/KeyboardView.Gingerbread</item>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index 618aaed..1235d4e 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <style name="KeyboardTheme.IceCreamSandwich" parent="KeyboardIcons.IceCreamSandwich">
+        <item name="themeId">5</item>
         <item name="keyboardStyle">@style/Keyboard.IceCreamSandwich</item>
         <item name="latinKeyboardStyle">@style/LatinKeyboard.IceCreamSandwich</item>
         <item name="keyboardViewStyle">@style/KeyboardView.IceCreamSandwich</item>
diff --git a/java/res/values/themes-stone-bold.xml b/java/res/values/themes-stone-bold.xml
index fdf9c51..6e25f41 100644
--- a/java/res/values/themes-stone-bold.xml
+++ b/java/res/values/themes-stone-bold.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <style name="KeyboardTheme.Stone.Bold" parent="KeyboardIcons.Black">
+        <item name="themeId">7</item>
         <item name="keyboardStyle">@style/Keyboard.Stone</item>
         <item name="latinKeyboardStyle">@style/LatinKeyboard.Stone</item>
         <item name="keyboardViewStyle">@style/KeyboardView.Stone.Bold</item>
diff --git a/java/res/values/themes-stone.xml b/java/res/values/themes-stone.xml
index cb3edc5..3cbda81 100644
--- a/java/res/values/themes-stone.xml
+++ b/java/res/values/themes-stone.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <style name="KeyboardTheme.Stone" parent="KeyboardIcons.Black">
+        <item name="themeId">6</item>
         <item name="keyboardStyle">@style/Keyboard.Stone</item>
         <item name="latinKeyboardStyle">@style/LatinKeyboard.Stone</item>
         <item name="keyboardViewStyle">@style/KeyboardView.Stone</item>
diff --git a/java/res/xml-land/kbd_number.xml b/java/res/xml-land/kbd_number.xml
new file mode 100644
index 0000000..f5930ef
--- /dev/null
+++ b/java/res/xml-land/kbd_number.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyboardHorizontalEdgesPadding="10%p"
+    latin:keyWidth="26.67%p"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_number" />
+</Keyboard>
diff --git a/java/res/xml-land/kbd_phone.xml b/java/res/xml-land/kbd_phone.xml
new file mode 100644
index 0000000..3b1fb36
--- /dev/null
+++ b/java/res/xml-land/kbd_phone.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyboardHorizontalEdgesPadding="10%p"
+    latin:keyWidth="26.67%p"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_phone" />
+</Keyboard>
diff --git a/java/res/xml-land/kbd_phone_shift.xml b/java/res/xml-land/kbd_phone_shift.xml
new file mode 100644
index 0000000..e596647
--- /dev/null
+++ b/java/res/xml-land/kbd_phone_shift.xml
@@ -0,0 +1,28 @@
+<?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:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyboardHorizontalEdgesPadding="10%p"
+    latin:keyWidth="26.67%p"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_phone_shift" />
+</Keyboard>
diff --git a/java/res/xml-sw600dp-land/kbd_mini_keyboard_template.xml b/java/res/xml-sw600dp-land/kbd_mini_keyboard_template.xml
index 3c19c29..8272e02 100644
--- a/java/res/xml-sw600dp-land/kbd_mini_keyboard_template.xml
+++ b/java/res/xml-sw600dp-land/kbd_mini_keyboard_template.xml
@@ -21,5 +21,6 @@
 <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
     latin:keyWidth="5%p"
     latin:rowHeight="@dimen/popup_key_height"
+    style="?attr/miniKeyboardStyle"
     >
 </Keyboard>
diff --git a/java/res/xml-sw600dp/kbd_mini_keyboard_template.xml b/java/res/xml-sw600dp/kbd_mini_keyboard_template.xml
index 9955fe8..0d5795f 100644
--- a/java/res/xml-sw600dp/kbd_mini_keyboard_template.xml
+++ b/java/res/xml-sw600dp/kbd_mini_keyboard_template.xml
@@ -21,5 +21,6 @@
 <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
     latin:keyWidth="8%p"
     latin:rowHeight="@dimen/popup_key_height"
+    style="?attr/miniKeyboardStyle"
     >
 </Keyboard>
diff --git a/java/res/xml-sw768dp-land/kbd_mini_keyboard_template.xml b/java/res/xml-sw768dp-land/kbd_mini_keyboard_template.xml
index d5f80e7..85e864a 100644
--- a/java/res/xml-sw768dp-land/kbd_mini_keyboard_template.xml
+++ b/java/res/xml-sw768dp-land/kbd_mini_keyboard_template.xml
@@ -21,5 +21,6 @@
 <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
     latin:keyWidth="3.5%p"
     latin:rowHeight="@dimen/popup_key_height"
+    style="?attr/miniKeyboardStyle"
     >
 </Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_mini_keyboard_template.xml b/java/res/xml-sw768dp/kbd_mini_keyboard_template.xml
index 1c15a5e..409c605 100644
--- a/java/res/xml-sw768dp/kbd_mini_keyboard_template.xml
+++ b/java/res/xml-sw768dp/kbd_mini_keyboard_template.xml
@@ -21,5 +21,6 @@
 <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
     latin:keyWidth="5.0%p"
     latin:rowHeight="@dimen/popup_key_height"
+    style="?attr/miniKeyboardStyle"
     >
 </Keyboard>
diff --git a/java/res/xml/kbd_number.xml b/java/res/xml/kbd_number.xml
index aabf0eb..38dd6bf 100644
--- a/java/res/xml/kbd_number.xml
+++ b/java/res/xml/kbd_number.xml
@@ -23,109 +23,5 @@
     latin:keyWidth="26.67%p"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_key_styles" />
-    <include
-        latin:keyboardLayout="@xml/kbd_numkey_styles" />
-    <switch>
-        <case
-            latin:passwordInput="true"
-        >
-            <Row>
-                <Key
-                    latin:keyStyle="num1KeyStyle" />
-                <Key
-                    latin:keyStyle="num2KeyStyle" />
-                <Key
-                    latin:keyStyle="num3KeyStyle" />
-            </Row>
-            <Row>
-                <Key
-                    latin:keyStyle="num4KeyStyle" />
-                <Key
-                    latin:keyStyle="num5KeyStyle" />
-                <Key
-                    latin:keyStyle="num6KeyStyle" />
-            </Row>
-            <Row>
-                <Key
-                    latin:keyStyle="num7KeyStyle" />
-                <Key
-                    latin:keyStyle="num8KeyStyle" />
-                <Key
-                    latin:keyStyle="num9KeyStyle" />
-                <Key
-                    latin:keyStyle="deleteKeyStyle"
-                    latin:keyWidth="fillRight" />
-            </Row>
-            <Row>
-                <Spacer />
-                <Key
-                    latin:keyStyle="num0KeyStyle" />
-                <Spacer />
-                <Key
-                    latin:keyStyle="returnKeyStyle"
-                    latin:keyWidth="fillRight" />
-            </Row>
-        </case>
-        <!-- latin:passwordInput="false" -->
-        <default>
-            <Row>
-                <Key
-                    latin:keyLabel="1"
-                    latin:keyStyle="numKeyStyle" />
-                <Key
-                    latin:keyLabel="2"
-                    latin:keyStyle="numKeyStyle" />
-                <Key
-                    latin:keyLabel="3"
-                    latin:keyStyle="numKeyStyle" />
-                <Key
-                    latin:keyLabel="-"
-                    latin:keyStyle="numFunctionalKeyStyle"
-                    latin:keyWidth="fillRight" />
-            </Row>
-            <Row>
-                <Key
-                    latin:keyLabel="4"
-                    latin:keyStyle="numKeyStyle" />
-                <Key
-                    latin:keyLabel="5"
-                    latin:keyStyle="numKeyStyle" />
-                <Key
-                    latin:keyLabel="6"
-                    latin:keyStyle="numKeyStyle" />
-                <Key
-                    latin:keyLabel=","
-                    latin:keyStyle="numFunctionalKeyStyle"
-                    latin:keyWidth="fillRight" />
-            </Row>
-            <Row>
-                <Key
-                    latin:keyLabel="7"
-                    latin:keyStyle="numKeyStyle" />
-                <Key
-                    latin:keyLabel="8"
-                    latin:keyStyle="numKeyStyle"/>
-                <Key
-                    latin:keyLabel="9"
-                    latin:keyStyle="numKeyStyle" />
-                <Key
-                    latin:keyStyle="deleteKeyStyle"
-                    latin:keyWidth="fillRight" />
-            </Row>
-            <Row>
-                <Key
-                    latin:keyStyle="numSpaceKeyStyle" />
-                <Key
-                    latin:keyLabel="0"
-                    latin:keyStyle="numKeyStyle" />
-                <Key
-                    latin:keyLabel="."
-                    latin:keyStyle="numKeyStyle" />
-                <Key
-                    latin:keyStyle="returnKeyStyle"
-                    latin:keyWidth="fillRight" />
-            </Row>
-        </default>
-    </switch>
+        latin:keyboardLayout="@xml/kbd_rows_number" />
 </Keyboard>
diff --git a/java/res/xml/kbd_phone.xml b/java/res/xml/kbd_phone.xml
index 4588ab2..b550f17 100644
--- a/java/res/xml/kbd_phone.xml
+++ b/java/res/xml/kbd_phone.xml
@@ -1,19 +1,19 @@
 <?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 
+** 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 
+**     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 
+** 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.
 */
 -->
@@ -23,53 +23,5 @@
     latin:keyWidth="26.67%p"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_key_styles" />
-    <include
-        latin:keyboardLayout="@xml/kbd_numkey_styles" />
-    <Row>
-        <Key
-            latin:keyStyle="num1KeyStyle" />
-        <Key
-            latin:keyStyle="num2KeyStyle" />
-        <Key
-            latin:keyStyle="num3KeyStyle" />
-        <Key
-            latin:keyLabel="-"
-            latin:keyStyle="numFunctionalKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyStyle="num4KeyStyle" />
-        <Key
-            latin:keyStyle="num5KeyStyle" />
-        <Key
-            latin:keyStyle="num6KeyStyle" />
-        <Key
-            latin:keyLabel="."
-            latin:keyStyle="numFunctionalKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyStyle="num7KeyStyle" />
-        <Key
-            latin:keyStyle="num8KeyStyle" />
-        <Key
-            latin:keyStyle="num9KeyStyle" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyStyle="numSwitchToAltKeyStyle" />
-        <Key
-            latin:keyStyle="num0KeyStyle" />
-        <Key
-            latin:keyStyle="numSpaceKeyStyle" />
-        <Key
-            latin:keyStyle="returnKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
+        latin:keyboardLayout="@xml/kbd_rows_phone" />
 </Keyboard>
diff --git a/java/res/xml/kbd_phone_shift.xml b/java/res/xml/kbd_phone_shift.xml
index 5be9bf9..eea823f 100644
--- a/java/res/xml/kbd_phone_shift.xml
+++ b/java/res/xml/kbd_phone_shift.xml
@@ -1,19 +1,19 @@
 <?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 
+** 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 
+**     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 
+** 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.
 */
 -->
@@ -23,66 +23,5 @@
     latin:keyWidth="26.67%p"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_key_styles" />
-    <include
-        latin:keyboardLayout="@xml/kbd_numkey_styles" />
-    <Row>
-        <Key
-            latin:keyLabel="("
-            latin:keyStyle="numKeyStyle" />
-        <Key
-            latin:keyLabel="/"
-            latin:keyStyle="numKeyStyle" />
-        <Key
-            latin:keyLabel=")"
-            latin:keyStyle="numKeyStyle" />
-        <Key
-            latin:keyLabel="-"
-            latin:keyStyle="numFunctionalKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyLabel="N" />
-        <!-- Pause is a comma. Check PhoneNumberUtils.java to see if this 
-            has changed. -->
-        <Key
-            latin:code="44"
-            latin:keyLabel="@string/label_pause_key"
-            latin:keyLabelOption="followKeyHintLabelRatio|autoXScale" />
-        <Key
-            latin:keyLabel=","
-            latin:keyStyle="numKeyStyle" />
-        <Key
-            latin:keyLabel="."
-            latin:keyStyle="numFunctionalKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyStyle="numStarKeyStyle" />
-        <!-- Wait is a semicolon. -->
-        <Key
-            latin:code="59"
-            latin:keyLabel="@string/label_wait_key"
-            latin:keyLabelOption="followKeyHintLabelRatio|autoXScale" />
-        <Key
-            latin:keyLabel="#"
-            latin:keyStyle="numKeyStyle" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyStyle="numSwitchToNumericKeyStyle" />
-        <Key
-            latin:keyLabel="+"
-            latin:keyStyle="numKeyStyle" />
-        <Key
-            latin:keyStyle="numSpaceKeyStyle" />
-        <Key
-            latin:keyStyle="returnKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
+        latin:keyboardLayout="@xml/kbd_rows_phone_shift" />
 </Keyboard>
diff --git a/java/res/xml/kbd_rows_number.xml b/java/res/xml/kbd_rows_number.xml
new file mode 100644
index 0000000..21d0656
--- /dev/null
+++ b/java/res/xml/kbd_rows_number.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_key_styles" />
+    <include
+        latin:keyboardLayout="@xml/kbd_numkey_styles" />
+    <switch>
+        <case
+            latin:passwordInput="true"
+        >
+            <Row>
+                <Key
+                    latin:keyStyle="num1KeyStyle" />
+                <Key
+                    latin:keyStyle="num2KeyStyle" />
+                <Key
+                    latin:keyStyle="num3KeyStyle" />
+            </Row>
+            <Row>
+                <Key
+                    latin:keyStyle="num4KeyStyle" />
+                <Key
+                    latin:keyStyle="num5KeyStyle" />
+                <Key
+                    latin:keyStyle="num6KeyStyle" />
+            </Row>
+            <Row>
+                <Key
+                    latin:keyStyle="num7KeyStyle" />
+                <Key
+                    latin:keyStyle="num8KeyStyle" />
+                <Key
+                    latin:keyStyle="num9KeyStyle" />
+                <Key
+                    latin:keyStyle="deleteKeyStyle"
+                    latin:keyWidth="fillRight" />
+            </Row>
+            <Row>
+                <Spacer />
+                <Key
+                    latin:keyStyle="num0KeyStyle" />
+                <Spacer />
+                <Key
+                    latin:keyStyle="returnKeyStyle"
+                    latin:keyWidth="fillRight" />
+            </Row>
+        </case>
+        <!-- latin:passwordInput="false" -->
+        <default>
+            <Row>
+                <Key
+                    latin:keyLabel="1"
+                    latin:keyStyle="numKeyStyle" />
+                <Key
+                    latin:keyLabel="2"
+                    latin:keyStyle="numKeyStyle" />
+                <Key
+                    latin:keyLabel="3"
+                    latin:keyStyle="numKeyStyle" />
+                <Key
+                    latin:keyLabel="-"
+                    latin:keyStyle="numFunctionalKeyStyle"
+                    latin:keyWidth="fillRight" />
+            </Row>
+            <Row>
+                <Key
+                    latin:keyLabel="4"
+                    latin:keyStyle="numKeyStyle" />
+                <Key
+                    latin:keyLabel="5"
+                    latin:keyStyle="numKeyStyle" />
+                <Key
+                    latin:keyLabel="6"
+                    latin:keyStyle="numKeyStyle" />
+                <Key
+                    latin:keyLabel=","
+                    latin:keyStyle="numFunctionalKeyStyle"
+                    latin:keyWidth="fillRight" />
+            </Row>
+            <Row>
+                <Key
+                    latin:keyLabel="7"
+                    latin:keyStyle="numKeyStyle" />
+                <Key
+                    latin:keyLabel="8"
+                    latin:keyStyle="numKeyStyle"/>
+                <Key
+                    latin:keyLabel="9"
+                    latin:keyStyle="numKeyStyle" />
+                <Key
+                    latin:keyStyle="deleteKeyStyle"
+                    latin:keyWidth="fillRight" />
+            </Row>
+            <Row>
+                <Key
+                    latin:keyStyle="numSpaceKeyStyle" />
+                <Key
+                    latin:keyLabel="0"
+                    latin:keyStyle="numKeyStyle" />
+                <Key
+                    latin:keyLabel="."
+                    latin:keyStyle="numKeyStyle" />
+                <Key
+                    latin:keyStyle="returnKeyStyle"
+                    latin:keyWidth="fillRight" />
+            </Row>
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/kbd_rows_phone.xml b/java/res/xml/kbd_rows_phone.xml
new file mode 100644
index 0000000..5500a60
--- /dev/null
+++ b/java/res/xml/kbd_rows_phone.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_key_styles" />
+    <include
+        latin:keyboardLayout="@xml/kbd_numkey_styles" />
+    <Row>
+        <Key
+            latin:keyStyle="num1KeyStyle" />
+        <Key
+            latin:keyStyle="num2KeyStyle" />
+        <Key
+            latin:keyStyle="num3KeyStyle" />
+        <Key
+            latin:keyLabel="-"
+            latin:keyStyle="numFunctionalKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row>
+        <Key
+            latin:keyStyle="num4KeyStyle" />
+        <Key
+            latin:keyStyle="num5KeyStyle" />
+        <Key
+            latin:keyStyle="num6KeyStyle" />
+        <Key
+            latin:keyLabel="."
+            latin:keyStyle="numFunctionalKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row>
+        <Key
+            latin:keyStyle="num7KeyStyle" />
+        <Key
+            latin:keyStyle="num8KeyStyle" />
+        <Key
+            latin:keyStyle="num9KeyStyle" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row>
+        <Key
+            latin:keyStyle="numSwitchToAltKeyStyle" />
+        <Key
+            latin:keyStyle="num0KeyStyle" />
+        <Key
+            latin:keyStyle="numSpaceKeyStyle" />
+        <Key
+            latin:keyStyle="returnKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+</merge>
diff --git a/java/res/xml/kbd_rows_phone_shift.xml b/java/res/xml/kbd_rows_phone_shift.xml
new file mode 100644
index 0000000..3c283d3
--- /dev/null
+++ b/java/res/xml/kbd_rows_phone_shift.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_key_styles" />
+    <include
+        latin:keyboardLayout="@xml/kbd_numkey_styles" />
+    <Row>
+        <Key
+            latin:keyLabel="("
+            latin:keyStyle="numKeyStyle" />
+        <Key
+            latin:keyLabel="/"
+            latin:keyStyle="numKeyStyle" />
+        <Key
+            latin:keyLabel=")"
+            latin:keyStyle="numKeyStyle" />
+        <Key
+            latin:keyLabel="-"
+            latin:keyStyle="numFunctionalKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row>
+        <Key
+            latin:keyLabel="N" />
+        <!-- Pause is a comma. Check PhoneNumberUtils.java to see if this
+            has changed. -->
+        <Key
+            latin:code="44"
+            latin:keyLabel="@string/label_pause_key"
+            latin:keyLabelOption="followKeyHintLabelRatio|autoXScale" />
+        <Key
+            latin:keyLabel=","
+            latin:keyStyle="numKeyStyle" />
+        <Key
+            latin:keyLabel="."
+            latin:keyStyle="numFunctionalKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row>
+        <Key
+            latin:keyStyle="numStarKeyStyle" />
+        <!-- Wait is a semicolon. -->
+        <Key
+            latin:code="59"
+            latin:keyLabel="@string/label_wait_key"
+            latin:keyLabelOption="followKeyHintLabelRatio|autoXScale" />
+        <Key
+            latin:keyLabel="#"
+            latin:keyStyle="numKeyStyle" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row>
+        <Key
+            latin:keyStyle="numSwitchToNumericKeyStyle" />
+        <Key
+            latin:keyLabel="+"
+            latin:keyStyle="numKeyStyle" />
+        <Key
+            latin:keyStyle="numSpaceKeyStyle" />
+        <Key
+            latin:keyStyle="returnKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+</merge>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 43bbd65..24de95f 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -32,6 +32,7 @@
         <CheckBoxPreference
             android:key="vibrate_on"
             android:title="@string/vibrate_on_keypress"
+            android:defaultValue="@bool/config_default_vibration_enabled"
             android:persistent="true" />
         <CheckBoxPreference
             android:key="sound_on"
diff --git a/java/res/xml/prefs_for_debug.xml b/java/res/xml/prefs_for_debug.xml
index 2dad171..80613a5 100644
--- a/java/res/xml/prefs_for_debug.xml
+++ b/java/res/xml/prefs_for_debug.xml
@@ -27,7 +27,7 @@
             />
 
     <ListPreference
-            android:key="pref_keyboard_layout_20100902"
+            android:key="pref_keyboard_layout_20110916"
             android:title="@string/keyboard_layout"
             android:persistent="true"
             android:entryValues="@array/keyboard_layout_modes_values"
diff --git a/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
index a6304d8..2fb8b87 100644
--- a/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
@@ -44,4 +44,8 @@
             return false;
         return (Boolean) CompatUtils.invoke(mVibrator, true, METHOD_hasVibrator);
     }
+
+    public void vibrate(long milliseconds) {
+        mVibrator.vibrate(milliseconds);
+    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 7ae3467..1e2ea55 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -19,6 +19,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
@@ -86,6 +87,9 @@
     public final int mX;
     /** Y coordinate of the key in the keyboard layout */
     public final int mY;
+    /** Hit bounding box of the key */
+    public final Rect mHitBox = new Rect();
+
     /** Text to output when pressed. This can be multiple characters, like ".com" */
     public final CharSequence mOutputText;
     /** More keys */
@@ -93,14 +97,6 @@
     /** More keys maximum column number */
     public final int mMaxMoreKeysColumn;
 
-    /**
-     * Flags that specify the anchoring to edges of the keyboard for detecting touch events
-     * that are just out of the boundary of the key. This is a bit mask of
-     * {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT},
-     * {@link Keyboard#EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM}.
-     */
-    private int mEdgeFlags;
-
     /** Background type that represents different key background visual than normal one. */
     public final int mBackgroundType;
     public static final int BACKGROUND_TYPE_NORMAL = 0;
@@ -120,11 +116,6 @@
     /** Whether this key needs to show the "..." popup hint for special purposes */
     private boolean mNeedsSpecialPopupHint;
 
-    // keyWidth enum constants
-    private static final int KEYWIDTH_NOT_ENUM = 0;
-    private static final int KEYWIDTH_FILL_RIGHT = -1;
-    private static final int KEYWIDTH_FILL_BOTH = -2;
-
     // RTL parenthesis character swapping map.
     private static final Map<Integer, Integer> sRtlParenthesisMap = new HashMap<Integer, Integer>();
 
@@ -172,23 +163,22 @@
      * This constructor is being used only for key in more keys keyboard.
      */
     public Key(Resources res, KeyboardParams params, String moreKeySpec,
-            int x, int y, int width, int height, int edgeFlags) {
+            int x, int y, int width, int height) {
         this(params, MoreKeySpecParser.getLabel(moreKeySpec), null, getIcon(params, moreKeySpec),
                 getCode(res, params, moreKeySpec), MoreKeySpecParser.getOutputText(moreKeySpec),
-                x, y, width, height, edgeFlags);
+                x, y, width, height);
     }
 
     /**
      * This constructor is being used only for key in popup suggestions pane.
      */
     public Key(KeyboardParams params, CharSequence label, CharSequence hintLabel, Drawable icon,
-            int code, CharSequence outputText, int x, int y, int width, int height, int edgeFlags) {
+            int code, CharSequence outputText, int x, int y, int width, int height) {
         mHeight = height - params.mVerticalGap;
         mHorizontalGap = params.mHorizontalGap;
         mVerticalGap = params.mVerticalGap;
         mVisualInsetsLeft = mVisualInsetsRight = 0;
         mWidth = width - mHorizontalGap;
-        mEdgeFlags = edgeFlags;
         mHintLabel = hintLabel;
         mLabelOption = 0;
         mBackgroundType = BACKGROUND_TYPE_NORMAL;
@@ -202,6 +192,7 @@
         // Horizontal gap is divided equally to both sides of the key.
         mX = x + mHorizontalGap / 2;
         mY = y;
+        mHitBox.set(x, y, x + width + 1, y + height);
     }
 
     /**
@@ -216,20 +207,10 @@
      */
     public Key(Resources res, KeyboardParams params, KeyboardBuilder.Row row,
             XmlResourceParser parser, KeyStyles keyStyles) {
-        final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.Keyboard);
-        mHeight = (int)KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
-                R.styleable.Keyboard_rowHeight, params.mHeight, row.mRowHeight)
-                - params.mVerticalGap;
-        final float horizontalGap = isSpacer() ? 0
-                : KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
-                        R.styleable.Keyboard_horizontalGap, params.mWidth, params.mHorizontalGap);
+        final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap;
+        final int keyHeight = row.mRowHeight;
         mVerticalGap = params.mVerticalGap;
-        final int widthType = KeyboardBuilder.getEnumValue(keyboardAttr,
-                R.styleable.Keyboard_keyWidth, KEYWIDTH_NOT_ENUM);
-        float keyWidth = KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
-                R.styleable.Keyboard_keyWidth, params.mWidth, row.mDefaultKeyWidth);
-        keyboardAttr.recycle();
+        mHeight = keyHeight - mVerticalGap;
 
         final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard_Key);
@@ -244,38 +225,18 @@
             style = keyStyles.getEmptyKeyStyle();
         }
 
-        final int keyboardWidth = params.mOccupiedWidth;
-        final float x = row.mCurrentX;
-        float keyXPos = KeyboardBuilder.getDimensionOrFraction(keyAttr,
-                R.styleable.Keyboard_Key_keyXPos, keyboardWidth, x);
-        if (keyXPos < 0) {
-            // If keyXPos is negative, the actual x-coordinate will be keyboardWidth + keyXPos.
-            keyXPos += keyboardWidth;
-            if (keyXPos < x) {
-                // keyXPos shouldn't be less than x because drawable area for this key starts
-                // at x. Or, this key will overlaps the adjacent key on its left hand side.
-                keyXPos = x;
-            }
-        }
-        if (widthType == KEYWIDTH_FILL_RIGHT) {
-            // If keyWidth is zero, the actual key width will be determined to fill out the
-            // area up to the right edge of the keyboard.
-            keyWidth = keyboardWidth - keyXPos;
-        } else if (widthType == KEYWIDTH_FILL_BOTH) {
-            // If keyWidth is negative, the actual key width will be determined to fill out the
-            // area between the nearest key on the left hand side and the right edge of the
-            // keyboard.
-            keyXPos = x;
-            keyWidth = keyboardWidth - keyXPos;
-        }
+        final float keyXPos = row.getKeyX(keyAttr);
+        final float keyWidth = row.getKeyWidth(keyAttr, keyXPos);
+        final int keyYPos = row.getKeyY();
 
         // Horizontal gap is divided equally to both sides of the key.
         mX = (int) (keyXPos + horizontalGap / 2);
-        mY = row.mCurrentY;
+        mY = keyYPos;
         mWidth = (int) (keyWidth - horizontalGap);
         mHorizontalGap = (int) horizontalGap;
+        mHitBox.set((int)keyXPos, keyYPos, (int)(keyXPos + keyWidth) + 1, keyYPos + keyHeight);
         // Update row to have current x coordinate.
-        row.mCurrentX = keyXPos + keyWidth;
+        row.setXPos(keyXPos + keyWidth);
 
         final CharSequence[] moreKeys = style.getTextArray(keyAttr,
                 R.styleable.Keyboard_Key_moreKeys);
@@ -287,20 +248,19 @@
         } else {
             mMoreKeys = moreKeys;
         }
-        mMaxMoreKeysColumn = style.getInt(keyboardAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn,
-                params.mMaxMiniKeyboardColumn);
+        mMaxMoreKeysColumn = style.getInt(keyAttr,
+                R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMiniKeyboardColumn);
 
-        mBackgroundType = style.getInt(
-                keyAttr, R.styleable.Keyboard_Key_backgroundType, BACKGROUND_TYPE_NORMAL);
+        mBackgroundType = style.getInt(keyAttr,
+                R.styleable.Keyboard_Key_backgroundType, BACKGROUND_TYPE_NORMAL);
         mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false);
         mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true);
-        mEdgeFlags = 0;
 
         final KeyboardIconsSet iconsSet = params.mIconsSet;
         mVisualInsetsLeft = (int) KeyboardBuilder.getDimensionOrFraction(keyAttr,
-                R.styleable.Keyboard_Key_visualInsetsLeft, keyboardWidth, 0);
+                R.styleable.Keyboard_Key_visualInsetsLeft, params.mBaseWidth, 0);
         mVisualInsetsRight = (int) KeyboardBuilder.getDimensionOrFraction(keyAttr,
-                R.styleable.Keyboard_Key_visualInsetsRight, keyboardWidth, 0);
+                R.styleable.Keyboard_Key_visualInsetsRight, params.mBaseWidth, 0);
         mPreviewIcon = iconsSet.getIcon(style.getInt(keyAttr,
                 R.styleable.Keyboard_Key_keyIconPreview, KeyboardIconsSet.ICON_UNDEFINED));
         mIcon = iconsSet.getIcon(style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIcon,
@@ -332,8 +292,20 @@
         keyAttr.recycle();
     }
 
-    public void addEdgeFlags(int flags) {
-        mEdgeFlags |= flags;
+    public void markAsLeftEdge(KeyboardParams params) {
+        mHitBox.left = params.mHorizontalEdgesPadding;
+    }
+
+    public void markAsRightEdge(KeyboardParams params) {
+        mHitBox.right = params.mOccupiedWidth - params.mHorizontalEdgesPadding;
+    }
+
+    public void markAsTopEdge(KeyboardParams params) {
+        mHitBox.top = params.mTopPadding;
+    }
+
+    public void markAsBottomEdge(KeyboardParams params) {
+        mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding;
     }
 
     public boolean isSticky() {
@@ -465,23 +437,10 @@
      * @param y the y-coordinate of the point
      * @return whether or not the point falls on the key. If the key is attached to an edge, it will
      * assume that all points between the key and the edge are considered to be on the key.
+     * @see {@link #markAsLeftEdge(KeyboardParams)} etc.
      */
     public boolean isOnKey(int x, int y) {
-        final int left = mX - mHorizontalGap / 2;
-        final int right = left + mWidth + mHorizontalGap;
-        final int top = mY;
-        final int bottom = top + mHeight + mVerticalGap;
-        final int flags = mEdgeFlags;
-        if (flags == 0) {
-            return x >= left && x <= right && y >= top && y <= bottom;
-        }
-        final boolean leftEdge = (flags & Keyboard.EDGE_LEFT) != 0;
-        final boolean rightEdge = (flags & Keyboard.EDGE_RIGHT) != 0;
-        final boolean topEdge = (flags & Keyboard.EDGE_TOP) != 0;
-        final boolean bottomEdge = (flags & Keyboard.EDGE_BOTTOM) != 0;
-        // In order to mitigate rounding errors, we use (left <= x <= right) here.
-        return (x >= left || leftEdge) && (x <= right || rightEdge)
-                && (y >= top || topEdge) && (y <= bottom || bottomEdge);
+        return mHitBox.contains(x, y);
     }
 
     /**
@@ -585,7 +544,7 @@
          * This constructor is being used only for divider in more keys keyboard.
          */
         public Spacer(KeyboardParams params, Drawable icon, int x, int y, int width, int height) {
-            super(params, null, null, icon, Keyboard.CODE_DUMMY, null, x, y, width, height, 0);
+            super(params, null, null, icon, Keyboard.CODE_DUMMY, null, x, y, width, height);
         }
 
         @Override
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 3a8a1d4..045f0d7 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -47,11 +47,6 @@
  * </pre>
  */
 public class Keyboard {
-    public static final int EDGE_LEFT = 0x01;
-    public static final int EDGE_RIGHT = 0x02;
-    public static final int EDGE_TOP = 0x04;
-    public static final int EDGE_BOTTOM = 0x08;
-
     /** Some common keys code.  These should be aligned with values/keycodes.xml */
     public static final int CODE_ENTER = '\n';
     public static final int CODE_TAB = '\t';
@@ -84,21 +79,19 @@
     public static final int CODE_UNSPECIFIED = -99;
 
     public final KeyboardId mId;
+    public final int mThemeId;
 
     /** Total height of the keyboard, including the padding and keys */
     public final int mOccupiedHeight;
     /** Total width of the keyboard, including the padding and keys */
     public final int mOccupiedWidth;
 
-    public final int mHeight;
-    public final int mWidth;
-
-    /** Default row height */
-    public final int mDefaultRowHeight;
-
+    /** The padding above the keyboard */
+    public final int mTopPadding;
     /** Default gap between rows */
     public final int mVerticalGap;
 
+    public final int mMostCommonKeyHeight;
     public final int mMostCommonKeyWidth;
 
     /** More keys keyboard template */
@@ -124,16 +117,16 @@
 
     public Keyboard(KeyboardParams params) {
         mId = params.mId;
+        mThemeId = params.mThemeId;
         mOccupiedHeight = params.mOccupiedHeight;
         mOccupiedWidth = params.mOccupiedWidth;
-        mHeight = params.mHeight;
-        mWidth = params.mWidth;
+        mMostCommonKeyHeight = params.mMostCommonKeyHeight;
         mMostCommonKeyWidth = params.mMostCommonKeyWidth;
         mIsRtlKeyboard = params.mIsRtlKeyboard;
         mMoreKeysTemplate = params.mMoreKeysTemplate;
         mMaxMiniKeyboardColumn = params.mMaxMiniKeyboardColumn;
 
-        mDefaultRowHeight = params.mDefaultRowHeight;
+        mTopPadding = params.mTopPadding;
         mVerticalGap = params.mVerticalGap;
 
         mKeys = Collections.unmodifiableList(params.mKeys);
@@ -242,4 +235,17 @@
     public int[] getNearestKeys(int x, int y) {
         return mProximityInfo.getNearestKeys(x, y);
     }
+
+    public static String themeName(int themeId) {
+        // This should be aligned with theme-*.xml resource files' themeId attribute.
+        switch (themeId) {
+        case 0: return "Basic";
+        case 1: return "BasicHighContrast";
+        case 5: return "IceCreamSandwich";
+        case 6: return "Stone";
+        case 7: return "StoneBold";
+        case 8: return "GingerBread";
+        default: return null;
+        }
+    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index e43ae55..13e8ba1 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -20,6 +20,7 @@
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
@@ -31,6 +32,7 @@
 import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
 import com.android.inputmethod.keyboard.internal.ModifierKeyState;
 import com.android.inputmethod.keyboard.internal.ShiftKeyState;
+import com.android.inputmethod.latin.InputView;
 import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.LocaleUtils;
@@ -48,7 +50,7 @@
     private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG;
     public static final boolean DEBUG_STATE = false;
 
-    public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902";
+    public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916";
     private static final int[] KEYBOARD_THEMES = {
         R.style.KeyboardTheme,
         R.style.KeyboardTheme_HighContrast,
@@ -61,7 +63,7 @@
     private SubtypeSwitcher mSubtypeSwitcher;
     private SharedPreferences mPrefs;
 
-    private View mCurrentInputView;
+    private InputView mCurrentInputView;
     private LatinKeyboardView mKeyboardView;
     private LatinIME mInputMethodService;
     private String mPackageName;
@@ -97,6 +99,8 @@
     private static final int SWITCH_STATE_CHORDING_SYMBOL = 6;
     private int mSwitchState = SWITCH_STATE_ALPHA;
 
+    private static String mLayoutSwitchBackSymbols;
+
     private int mThemeIndex = -1;
     private Context mThemeContext;
 
@@ -204,6 +208,7 @@
             mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues);
             mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues);
             mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues);
+            mLayoutSwitchBackSymbols = mResources.getString(R.string.layout_switch_back_symbols);
             setKeyboard(getKeyboard(mSavedKeyboardState.getKeyboardId()));
         } catch (RuntimeException e) {
             Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e);
@@ -226,6 +231,7 @@
     private void setKeyboard(final Keyboard keyboard) {
         final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
         mKeyboardView.setKeyboard(keyboard);
+        mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding);
         mCurrentId = keyboard.mId;
         mSwitchState = getSwitchState(mCurrentId);
         updateShiftLockState(keyboard);
@@ -269,10 +275,12 @@
 
             if (DEBUG_CACHE) {
                 Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
-                        + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
+                        + ((ref == null) ? "LOAD" : "GCed") + " id=" + id
+                        + " theme=" + Keyboard.themeName(keyboard.mThemeId));
             }
         } else if (DEBUG_CACHE) {
-            Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id);
+            Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id
+                    + " theme=" + Keyboard.themeName(keyboard.mThemeId));
         }
 
         keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive);
@@ -661,24 +669,9 @@
         return c == Keyboard.CODE_SPACE || c == Keyboard.CODE_ENTER;
     }
 
-    private static boolean isQuoteCharacter(int c) {
-        // Apostrophe, quotation mark.
-        if (c == Keyboard.CODE_SINGLE_QUOTE || c == Keyboard.CODE_DOUBLE_QUOTE)
-            return true;
-        // \u2018: Left single quotation mark
-        // \u2019: Right single quotation mark
-        // \u201a: Single low-9 quotation mark
-        // \u201b: Single high-reversed-9 quotation mark
-        // \u201c: Left double quotation mark
-        // \u201d: Right double quotation mark
-        // \u201e: Double low-9 quotation mark
-        // \u201f: Double high-reversed-9 quotation mark
-        if (c >= '\u2018' && c <= '\u201f')
-            return true;
-        // \u00ab: Left-pointing double angle quotation mark
-        // \u00bb: Right-pointing double angle quotation mark
-        if (c == '\u00ab' || c == '\u00bb')
-            return true;
+    private static boolean isLayoutSwitchBackCharacter(int c) {
+        if (TextUtils.isEmpty(mLayoutSwitchBackSymbols)) return false;
+        if (mLayoutSwitchBackSymbols.indexOf(c) >= 0) return true;
         return false;
     }
 
@@ -736,7 +729,7 @@
                 mSwitchState = SWITCH_STATE_SYMBOL;
             }
             // Snap back to alpha keyboard mode immediately if user types a quote character.
-            if (isQuoteCharacter(code)) {
+            if (isLayoutSwitchBackCharacter(code)) {
                 changeKeyboardMode();
             }
             break;
@@ -744,7 +737,7 @@
         case SWITCH_STATE_CHORDING_SYMBOL:
             // Snap back to alpha keyboard mode if user types one or more non-space/enter
             // characters followed by a space/enter or a quote character.
-            if (isSpaceCharacter(code) || isQuoteCharacter(code)) {
+            if (isSpaceCharacter(code) || isLayoutSwitchBackCharacter(code)) {
                 changeKeyboardMode();
             }
             break;
@@ -773,7 +766,7 @@
         for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
             try {
                 setContextThemeWrapper(mInputMethodService, newThemeIndex);
-                mCurrentInputView = LayoutInflater.from(mThemeContext).inflate(
+                mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
                         R.layout.input_view, null);
                 tryGC = false;
             } catch (OutOfMemoryError e) {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 5a44460..6af4123 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -391,7 +391,7 @@
         mDirtyRect.set(0, 0, getWidth(), getHeight());
         mBufferNeedsUpdate = true;
         invalidateAllKeys();
-        final int keyHeight = keyboard.mDefaultRowHeight - keyboard.mVerticalGap;
+        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
         mKeyDrawParams.updateKeyHeight(keyHeight);
         mKeyPreviewDrawParams.updateKeyHeight(keyHeight);
     }
@@ -968,7 +968,7 @@
     }
 
     @Override
-    public void onDetachedFromWindow() {
+    protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         closing();
         if (mPreviewPlacer != null) {
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
index ad8056c..d4b35a5 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
@@ -122,9 +122,9 @@
                     mTopRowAdjustment = -1;
                 }
 
-                mWidth = mOccupiedWidth = mNumColumns * mDefaultKeyWidth;
+                mBaseWidth = mOccupiedWidth = mNumColumns * mDefaultKeyWidth;
                 // Need to subtract the bottom row's gutter only.
-                mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight - mVerticalGap
+                mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight - mVerticalGap
                         + mTopPadding + mBottomPadding;
             }
 
@@ -193,13 +193,11 @@
                 return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
             }
 
-            public int getRowFlags(int row) {
-                int rowFlags = 0;
+            public void markAsEdgeKey(Key key, int row) {
                 if (row == 0)
-                    rowFlags |= Keyboard.EDGE_TOP;
+                    key.markAsTopEdge(this);
                 if (isTopRow(row))
-                    rowFlags |= Keyboard.EDGE_BOTTOM;
-                return rowFlags;
+                    key.markAsBottomEdge(this);
             }
 
             private boolean isTopRow(int rowCount) {
@@ -219,7 +217,7 @@
 
             final int keyWidth = getMaxKeyWidth(view, mMoreKeys, mParams.mDefaultKeyWidth);
             mParams.setParameters(mMoreKeys.length, parentKey.mMaxMoreKeysColumn,
-                    keyWidth, parentKeyboard.mDefaultRowHeight, parentKey.mX
+                    keyWidth, parentKeyboard.mMostCommonKeyHeight, parentKey.mX
                             + (mParams.mDefaultKeyWidth - keyWidth) / 2, view.getMeasuredWidth());
         }
 
@@ -254,8 +252,8 @@
                 final String moreKeySpec = mMoreKeys[n].toString();
                 final int row = n / params.mNumColumns;
                 final Key key = new Key(mResources, params, moreKeySpec, params.getX(n, row),
-                        params.getY(row), params.mDefaultKeyWidth, params.mDefaultRowHeight,
-                        params.getRowFlags(row));
+                        params.getY(row), params.mDefaultKeyWidth, params.mDefaultRowHeight);
+                params.markAsEdgeKey(key, row);
                 params.onAddKey(key);
             }
             return new MiniKeyboard(params);
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index a82bcbe..71b46d6 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -27,6 +27,7 @@
     public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
     /** Number of key widths from current touch point to search for nearest keys. */
     private static float SEARCH_DISTANCE = 1.2f;
+    private static final int UNKNOWN_THEME = -1;
     private static final int[] EMPTY_INT_ARRAY = new int[0];
 
     private final int mGridWidth;
@@ -65,7 +66,8 @@
         spellCheckerProximityInfo.mNativeProximityInfo =
                 spellCheckerProximityInfo.setProximityInfoNative(
                         SpellCheckerProximityInfo.ROW_SIZE,
-                        480, 300, 10, 3, SpellCheckerProximityInfo.PROXIMITY);
+                        480, 300, 10, 3, SpellCheckerProximityInfo.PROXIMITY,
+                        0, null, null, null, null, null, UNKNOWN_THEME);
         return spellCheckerProximityInfo;
     }
 
@@ -74,7 +76,9 @@
         Utils.loadNativeLibrary();
     }
     private native int setProximityInfoNative(int maxProximityCharsSize, int displayWidth,
-            int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray);
+            int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray,
+            int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
+            int[] keyWidths, int[] keyHeights, int[] keyCharCodes, int themeId);
     private native void releaseProximityInfoNative(int nativeProximityInfo);
 
     private final void setProximityInfo(int[][] gridNeighborKeyIndexes, int keyboardWidth,
@@ -88,8 +92,25 @@
                         keys.get(gridNeighborKeyIndexes[i][j]).mCode;
             }
         }
+        final int keyCount = keys.size();
+        int[] keyXCoordinates = new int[keyCount];
+        int[] keyYCoordinates = new int[keyCount];
+        int[] keyWidths = new int[keyCount];
+        int[] keyHeights = new int[keyCount];
+        int[] keyCharCodes = new int[keyCount];
+        final int themeId = 5;    // TODO: Use real theme id.
+        for (int i = 0; i < keyCount; ++i) {
+            final Key key = keys.get(i);
+            keyXCoordinates[i] = key.mX;
+            keyYCoordinates[i] = key.mY;
+            keyWidths[i] = key.mWidth;
+            keyHeights[i] = key.mHeight;
+            keyCharCodes[i] = key.mCode;
+        }
         mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE,
-                keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, proximityCharsArray);
+                keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, proximityCharsArray,
+                keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
+                themeId);
     }
 
     public int getNativeProximityInfo() {
diff --git a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
index c4251cc..62a9259 100644
--- a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
@@ -51,7 +51,7 @@
         mView = view;
         final String[] deviceList = context.getResources().getStringArray(
                 R.array.sudden_jumping_touch_event_device_list);
-        mNeedsSuddenJumpingHack = needsSuddenJumpingHack(Build.DEVICE, deviceList);
+        mNeedsSuddenJumpingHack = needsSuddenJumpingHack(Build.HARDWARE, deviceList);
     }
 
     private static boolean needsSuddenJumpingHack(String deviceName, String[] deviceList) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index e39548e..187a1ad 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -123,6 +123,9 @@
     private static final String TAG_DEFAULT = "default";
     public static final String TAG_KEY_STYLE = "key-style";
 
+    private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
+    private static final int DEFAULT_KEYBOARD_ROWS = 4;
+
     protected final KP mParams;
     protected final Context mContext;
     protected final Resources mResources;
@@ -141,29 +144,96 @@
      * defines.
      */
     public static class Row {
+        // keyWidth enum constants
+        private static final int KEYWIDTH_NOT_ENUM = 0;
+        private static final int KEYWIDTH_FILL_RIGHT = -1;
+        private static final int KEYWIDTH_FILL_BOTH = -2;
+
+        private final KeyboardParams mParams;
         /** Default width of a key in this row. */
         public final float mDefaultKeyWidth;
         /** Default height of a key in this row. */
         public final int mRowHeight;
 
-        public final int mCurrentY;
+        private final int mCurrentY;
         // Will be updated by {@link Key}'s constructor.
-        public float mCurrentX;
+        private float mCurrentX;
 
         public Row(Resources res, KeyboardParams params, XmlResourceParser parser, int y) {
-            final int keyboardWidth = params.mWidth;
-            final int keyboardHeight = params.mHeight;
-            TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
+            mParams = params;
+            TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
                     R.styleable.Keyboard);
-            mDefaultKeyWidth = KeyboardBuilder.getDimensionOrFraction(a,
-                    R.styleable.Keyboard_keyWidth, keyboardWidth, params.mDefaultKeyWidth);
-            mRowHeight = (int)KeyboardBuilder.getDimensionOrFraction(a,
-                    R.styleable.Keyboard_rowHeight, keyboardHeight, params.mDefaultRowHeight);
-            a.recycle();
+            mRowHeight = (int)KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_rowHeight, params.mBaseHeight, params.mDefaultRowHeight);
+            keyboardAttr.recycle();
+            TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
+                    R.styleable.Keyboard_Key);
+            mDefaultKeyWidth = KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth, params.mDefaultKeyWidth);
+            keyAttr.recycle();
 
             mCurrentY = y;
             mCurrentX = 0.0f;
         }
+
+        public void setXPos(float keyXPos) {
+            mCurrentX = keyXPos;
+        }
+
+        public void advanceXPos(float width) {
+            mCurrentX += width;
+        }
+
+        public int getKeyY() {
+            return mCurrentY;
+        }
+
+        public float getKeyX(TypedArray keyAttr) {
+            final int widthType = KeyboardBuilder.getEnumValue(keyAttr,
+                    R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
+            if (widthType == KEYWIDTH_FILL_BOTH) {
+                // If keyWidth is fillBoth, the key width should start right after the nearest key
+                // on the left hand side.
+                return mCurrentX;
+            }
+
+            final int keyboardRightEdge = mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding;
+            if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
+                final float keyXPos = KeyboardBuilder.getDimensionOrFraction(keyAttr,
+                        R.styleable.Keyboard_Key_keyXPos, mParams.mBaseWidth, 0);
+                if (keyXPos < 0) {
+                    // If keyXPos is negative, the actual x-coordinate will be
+                    // keyboardWidth + keyXPos.
+                    // keyXPos shouldn't be less than mCurrentX because drawable area for this key
+                    // starts at mCurrentX. Or, this key will overlaps the adjacent key on its left
+                    // hand side.
+                    return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
+                } else {
+                    return keyXPos + mParams.mHorizontalEdgesPadding;
+                }
+            }
+            return mCurrentX;
+        }
+
+        public float getKeyWidth(TypedArray keyAttr, float keyXPos) {
+            final int widthType = KeyboardBuilder.getEnumValue(keyAttr,
+                    R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
+            switch (widthType) {
+            case KEYWIDTH_FILL_RIGHT:
+            case KEYWIDTH_FILL_BOTH:
+                final int keyboardRightEdge =
+                        mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding;
+                // If keyWidth is fillRight, the actual key width will be determined to fill out the
+                // area up to the right edge of the keyboard.
+                // If keyWidth is fillBoth, the actual key width will be determined to fill out the
+                // area between the nearest key on the left hand side and the right edge of the
+                // keyboard.
+                return keyboardRightEdge - keyXPos;
+            default: // KEYWIDTH_NOT_ENUM
+                return KeyboardBuilder.getDimensionOrFraction(keyAttr,
+                        R.styleable.Keyboard_Key_keyWidth, mParams.mBaseWidth, mDefaultKeyWidth);
+            }
+        }
     }
 
     public KeyboardBuilder(Context context, KP params) {
@@ -173,8 +243,10 @@
         mDisplayMetrics = res.getDisplayMetrics();
 
         mParams = params;
-        mParams.mHorizontalEdgesPadding = (int)res.getDimension(
-                R.dimen.keyboard_horizontal_edges_padding);
+
+        final TypedArray a = context.obtainStyledAttributes(R.styleable.KeyboardTheme);
+        mParams.mThemeId = a.getInt(R.styleable.KeyboardTheme_themeId, 0);
+        a.recycle();
 
         mParams.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
         mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
@@ -259,38 +331,42 @@
                 minKeyboardHeight = -(int)getDimensionOrFraction(keyboardAttr,
                         R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2);
             }
+            final KeyboardParams params = mParams;
             // Keyboard height will not exceed maxKeyboardHeight and will not be less than
             // minKeyboardHeight.
-            mParams.mOccupiedHeight = Math.max(
+            params.mOccupiedHeight = Math.max(
                     Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
-            mParams.mOccupiedWidth = mParams.mId.mWidth;
-            mParams.mTopPadding = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_keyboardTopPadding, mParams.mOccupiedHeight, 0);
-            mParams.mBottomPadding = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_keyboardBottomPadding, mParams.mOccupiedHeight, 0);
+            params.mOccupiedWidth = params.mId.mWidth;
+            params.mTopPadding = (int)getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_keyboardTopPadding, params.mOccupiedHeight, 0);
+            params.mBottomPadding = (int)getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_keyboardBottomPadding, params.mOccupiedHeight, 0);
+            params.mHorizontalEdgesPadding = (int)getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_keyboardHorizontalEdgesPadding, mParams.mOccupiedWidth, 0);
 
-            final int height = mParams.mOccupiedHeight;
-            final int width = mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding * 2
-                    - mParams.mHorizontalCenterPadding;
-            mParams.mHeight = height;
-            mParams.mWidth = width;
-            mParams.mDefaultKeyWidth = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_keyWidth, width, width / 10);
-            mParams.mDefaultRowHeight = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_rowHeight, height, height / 4);
-            mParams.mHorizontalGap = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_horizontalGap, width, 0);
-            mParams.mVerticalGap = (int)getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_verticalGap, height, 0);
+            params.mBaseWidth = params.mOccupiedWidth - params.mHorizontalEdgesPadding * 2
+                    - params.mHorizontalCenterPadding;
+            params.mDefaultKeyWidth = (int)getDimensionOrFraction(keyAttr,
+                    R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth,
+                    params.mBaseWidth / DEFAULT_KEYBOARD_COLUMNS);
+            params.mHorizontalGap = (int)getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_horizontalGap, params.mBaseWidth, 0);
+            params.mVerticalGap = (int)getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_verticalGap, params.mOccupiedHeight, 0);
+            params.mBaseHeight = params.mOccupiedHeight - params.mTopPadding
+                    - params.mBottomPadding + params.mVerticalGap;
+            params.mDefaultRowHeight = (int)getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_rowHeight, params.mBaseHeight,
+                    params.mBaseHeight / DEFAULT_KEYBOARD_ROWS);
 
-            mParams.mIsRtlKeyboard = keyboardAttr.getBoolean(
+            params.mIsRtlKeyboard = keyboardAttr.getBoolean(
                     R.styleable.Keyboard_isRtlKeyboard, false);
-            mParams.mMoreKeysTemplate = keyboardAttr.getResourceId(
+            params.mMoreKeysTemplate = keyboardAttr.getResourceId(
                     R.styleable.Keyboard_moreKeysTemplate, 0);
-            mParams.mMaxMiniKeyboardColumn = keyAttr.getInt(
+            params.mMaxMiniKeyboardColumn = keyAttr.getInt(
                     R.styleable.Keyboard_Key_maxMoreKeysColumn, 5);
 
-            mParams.mIconsSet.loadIcons(keyboardAttr);
+            params.mIconsSet.loadIcons(keyboardAttr);
         } finally {
             keyAttr.recycle();
             keyboardAttr.recycle();
@@ -667,7 +743,6 @@
     }
 
     private void startRow(Row row) {
-        row.mCurrentX = 0;
         addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
         mCurrentRow = row;
         mLeftEdge = true;
@@ -678,11 +753,11 @@
         if (mCurrentRow == null)
             throw new InflateException("orphant end row tag");
         if (mRightEdgeKey != null) {
-            mRightEdgeKey.addEdgeFlags(Keyboard.EDGE_RIGHT);
+            mRightEdgeKey.markAsRightEdge(mParams);
             mRightEdgeKey = null;
         }
         addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
-        mCurrentY += mCurrentRow.mRowHeight;
+        mCurrentY += row.mRowHeight;
         mCurrentRow = null;
         mTopEdge = false;
     }
@@ -690,11 +765,11 @@
     private void endKey(Key key) {
         mParams.onAddKey(key);
         if (mLeftEdge) {
-            key.addEdgeFlags(Keyboard.EDGE_LEFT);
+            key.markAsLeftEdge(mParams);
             mLeftEdge = false;
         }
         if (mTopEdge) {
-            key.addEdgeFlags(Keyboard.EDGE_TOP);
+            key.markAsTopEdge(mParams);
         }
         mRightEdgeKey = key;
     }
@@ -703,7 +778,7 @@
     }
 
     private void addEdgeSpace(float width, Row row) {
-        row.mCurrentX += width;
+        row.advanceXPos(width);
         mLeftEdge = false;
         mRightEdgeKey = null;
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
index 593c3dc..97f58fa 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
@@ -31,12 +31,15 @@
 
 public class KeyboardParams {
     public KeyboardId mId;
+    public int mThemeId;
 
+    /** Total height and width of the keyboard, including the paddings and keys */
     public int mOccupiedHeight;
     public int mOccupiedWidth;
 
-    public int mHeight;
-    public int mWidth;
+    /** Base height and width of the keyboard used to calculate rows' or keys' heights and widths */
+    public int mBaseHeight;
+    public int mBaseWidth;
 
     public int mTopPadding;
     public int mBottomPadding;
@@ -62,6 +65,7 @@
     public final Map<Key, Drawable> mUnshiftedIcons = new HashMap<Key, Drawable>();
     public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
 
+    public int mMostCommonKeyHeight = 0;
     public int mMostCommonKeyWidth = 0;
 
     protected void clearKeys() {
@@ -89,21 +93,39 @@
         mShiftedIcons.put(key, icon);
     }
 
-    private int mMaxCount = 0;
-    private final Map<Integer, Integer> mHistogram = new HashMap<Integer, Integer>();
+    private int mMaxHeightCount = 0;
+    private int mMaxWidthCount = 0;
+    private final Map<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>();
+    private final Map<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>();
 
     private void clearHistogram() {
+        mMostCommonKeyHeight = 0;
+        mMaxHeightCount = 0;
+        mHeightHistogram.clear();
+
+        mMaxWidthCount = 0;
         mMostCommonKeyWidth = 0;
-        mMaxCount = 0;
-        mHistogram.clear();
+        mWidthHistogram.clear();
+    }
+
+    private static int updateHistogramCounter(Map<Integer, Integer> histogram, Integer key) {
+        final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1;
+        histogram.put(key, count);
+        return count;
     }
 
     private void updateHistogram(Key key) {
+        final Integer height = key.mHeight + key.mVerticalGap;
+        final int heightCount = updateHistogramCounter(mHeightHistogram, height);
+        if (heightCount > mMaxHeightCount) {
+            mMaxHeightCount = heightCount;
+            mMostCommonKeyHeight = height;
+        }
+
         final Integer width = key.mWidth + key.mHorizontalGap;
-        final int count = (mHistogram.containsKey(width) ? mHistogram.get(width) : 0) + 1;
-        mHistogram.put(width, count);
-        if (count > mMaxCount) {
-            mMaxCount = count;
+        final int widthCount = updateHistogramCounter(mWidthHistogram, width);
+        if (widthCount > mMaxWidthCount) {
+            mMaxWidthCount = widthCount;
             mMostCommonKeyWidth = width;
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java
new file mode 100644
index 0000000..0dcb811
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/InputView.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.LinearLayout;
+
+public class InputView extends LinearLayout {
+    private View mSuggestionsContainer;
+    private View mKeyboardView;
+    private int mKeyboardTopPadding;
+
+    private boolean mIsForwardingEvent;
+    private final Rect mInputViewRect = new Rect();
+    private final Rect mEventForwardingRect = new Rect();
+    private final Rect mEventReceivingRect = new Rect();
+
+    public InputView(Context context, AttributeSet attrs) {
+        super(context, attrs, 0);
+    }
+
+    public void setKeyboardGeometry(int keyboardTopPadding) {
+        mKeyboardTopPadding = keyboardTopPadding;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mSuggestionsContainer = findViewById(R.id.suggestions_container);
+        mKeyboardView = findViewById(R.id.keyboard_view);
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent me) {
+        if (mSuggestionsContainer.getVisibility() == VISIBLE
+                && mKeyboardView.getVisibility() == VISIBLE
+                && forwardTouchEvent(me)) {
+            return true;
+        }
+        return super.dispatchTouchEvent(me);
+    }
+
+    // The touch events that hit the top padding of keyboard should be forwarded to SuggestionsView.
+    private boolean forwardTouchEvent(MotionEvent me) {
+        final Rect rect = mInputViewRect;
+        this.getGlobalVisibleRect(rect);
+        final int x = (int)me.getX() + rect.left;
+        final int y = (int)me.getY() + rect.top;
+
+        final Rect forwardingRect = mEventForwardingRect;
+        mKeyboardView.getGlobalVisibleRect(forwardingRect);
+        if (!mIsForwardingEvent && !forwardingRect.contains(x, y)) {
+            return false;
+        }
+
+        final int forwardingLimitY = forwardingRect.top + mKeyboardTopPadding;
+        boolean sendToTarget = false;
+
+        switch (me.getAction()) {
+        case MotionEvent.ACTION_DOWN:
+            if (y < forwardingLimitY) {
+                // This down event and further move and up events should be forwarded to the target.
+                mIsForwardingEvent = true;
+                sendToTarget = true;
+            }
+            break;
+        case MotionEvent.ACTION_MOVE:
+            sendToTarget = mIsForwardingEvent;
+            break;
+        case MotionEvent.ACTION_UP:
+        case MotionEvent.ACTION_CANCEL:
+            sendToTarget = mIsForwardingEvent;
+            mIsForwardingEvent = false;
+            break;
+        }
+
+        if (!sendToTarget) {
+            return false;
+        }
+
+        final Rect receivingRect = mEventReceivingRect;
+        mSuggestionsContainer.getGlobalVisibleRect(receivingRect);
+        final int translatedX = x - receivingRect.left;
+        final int translatedY;
+        if (y < forwardingLimitY) {
+            // The forwarded event should have coordinates that are inside of the target.
+            translatedY = Math.min(y - receivingRect.top, receivingRect.height() - 1);
+        } else {
+            translatedY = y - receivingRect.top;
+        }
+        me.setLocation(translatedX, translatedY);
+        mSuggestionsContainer.dispatchTouchEvent(me);
+        return true;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index ff02266..32649d5 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -28,6 +28,7 @@
 import android.inputmethodservice.InputMethodService;
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
+import android.os.Build;
 import android.os.Debug;
 import android.os.Message;
 import android.os.SystemClock;
@@ -56,6 +57,7 @@
 import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
 import com.android.inputmethod.compat.InputTypeCompatUtils;
 import com.android.inputmethod.compat.SuggestionSpanUtils;
+import com.android.inputmethod.compat.VibratorCompatWrapper;
 import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
 import com.android.inputmethod.deprecated.VoiceProxy;
 import com.android.inputmethod.deprecated.recorrection.Recorrection;
@@ -207,9 +209,12 @@
     private long mLastKeyTime;
 
     private AudioManager mAudioManager;
-    private static float mFxVolume = -1.0f; // just a default value to be updated runtime
+    private float mFxVolume = -1.0f; // default volume
     private boolean mSilentModeOn; // System-wide current configuration
 
+    private VibratorCompatWrapper mVibrator;
+    private long mKeypressVibrationDuration = -1;
+
     // TODO: Move this flag to VoiceProxy
     private boolean mConfigurationChanging;
 
@@ -236,7 +241,8 @@
         private static final int MSG_SET_BIGRAM_PREDICTIONS = 7;
         private static final int MSG_START_ORIENTATION_CHANGE = 8;
         private static final int MSG_START_INPUT_VIEW = 9;
-        private static final int MSG_RESTORE_KEYBOARD_LAYOUT = 10;
+        private static final int MSG_DISPLAY_COMPLETIONS = 10;
+        private static final int MSG_RESTORE_KEYBOARD_LAYOUT = 11;
 
         public UIHandler(LatinIME outerInstance) {
             super(outerInstance);
@@ -288,6 +294,9 @@
             case MSG_START_INPUT_VIEW:
                 latinIme.onStartInputView((EditorInfo)msg.obj, false);
                 break;
+            case MSG_DISPLAY_COMPLETIONS:
+                latinIme.onDisplayCompletions((CompletionInfo[])msg.obj);
+                break;
             case MSG_RESTORE_KEYBOARD_LAYOUT:
                 removeMessages(MSG_UPDATE_SHIFT_STATE);
                 ((KeyboardLayoutState)msg.obj).restore();
@@ -412,6 +421,18 @@
             }
             return false;
         }
+
+        public boolean postDisplayCompletions(CompletionInfo[] applicationSpecifiedCompletions) {
+            if (hasMessages(MSG_START_INPUT_VIEW) || hasMessages(MSG_DISPLAY_COMPLETIONS)) {
+                removeMessages(MSG_DISPLAY_COMPLETIONS);
+                // Postpone onDisplayCompletions by ACCUMULATE_START_INPUT_VIEW_DELAY.
+                sendMessageDelayed(
+                        obtainMessage(MSG_DISPLAY_COMPLETIONS, applicationSpecifiedCompletions),
+                        ACCUMULATE_START_INPUT_VIEW_DELAY);
+                return true;
+            }
+            return false;
+        }
     }
 
     @Override
@@ -433,13 +454,14 @@
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         mKeyboardSwitcher = KeyboardSwitcher.getInstance();
         mRecorrection = Recorrection.getInstance();
+        mVibrator = VibratorCompatWrapper.getInstance(this);
         DEBUG = LatinImeLogger.sDBG;
 
-        loadSettings();
-
         final Resources res = getResources();
         mResources = res;
 
+        loadSettings();
+
         Utils.GCUtils.getInstance().reset();
         boolean tryGC = true;
         for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
@@ -480,6 +502,7 @@
         mSettingsValues = new Settings.Values(mPrefs, this, mSubtypeSwitcher.getInputLocaleStr());
         resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
         updateSoundEffectVolume();
+        updateKeypressVibrationDuration();
     }
 
     private void initSuggest() {
@@ -611,6 +634,9 @@
         mSuggestionsView = (SuggestionsView) view.findViewById(R.id.suggestions_view);
         if (mSuggestionsView != null)
             mSuggestionsView.setListener(this, view);
+        if (LatinImeLogger.sVISUALDEBUG) {
+            mKeyPreviewBackingView.setBackgroundColor(0x10FF0000);
+        }
     }
 
     @Override
@@ -913,6 +939,9 @@
 
     @Override
     public void onDisplayCompletions(CompletionInfo[] applicationSpecifiedCompletions) {
+        if (mHandler.postDisplayCompletions(applicationSpecifiedCompletions)) {
+            return;
+        }
         if (DEBUG) {
             Log.i(TAG, "Received completions:");
             if (applicationSpecifiedCompletions != null) {
@@ -1496,8 +1525,6 @@
             if (!TextUtils.isEmpty(typedWord) && !typedWord.equals(mBestWord)) {
                 InputConnectionCompatUtils.commitCorrection(
                         ic, mLastSelectionEnd - typedWord.length(), typedWord, mBestWord);
-                if (mSuggestionsView != null)
-                    mSuggestionsView.onAutoCorrectionInverted(mBestWord);
             }
         }
         if (Keyboard.CODE_SPACE == primaryCode) {
@@ -2052,13 +2079,14 @@
 
     // update sound effect volume
     private void updateSoundEffectVolume() {
-        if (mAudioManager == null) {
-            mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
-            if (mAudioManager == null) return;
+        final String[] volumePerHardwareList = mResources.getStringArray(R.array.keypress_volumes);
+        final String hardwarePrefix = Build.HARDWARE + ",";
+        for (final String element : volumePerHardwareList) {
+            if (element.startsWith(hardwarePrefix)) {
+                mFxVolume = Float.parseFloat(element.substring(element.lastIndexOf(',') + 1));
+                break;
+            }
         }
-        // This aligns with the current media volume minus 6dB
-        mFxVolume = (float) mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
-                / (float) mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) / 4.0f;
     }
 
     // update flags for silent mode
@@ -2070,6 +2098,19 @@
         mSilentModeOn = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL);
     }
 
+    private void updateKeypressVibrationDuration() {
+        final String[] durationPerHardwareList = mResources.getStringArray(
+                R.array.keypress_vibration_durations);
+        final String hardwarePrefix = Build.HARDWARE + ",";
+        for (final String element : durationPerHardwareList) {
+            if (element.startsWith(hardwarePrefix)) {
+                mKeypressVibrationDuration =
+                        Long.parseLong(element.substring(element.lastIndexOf(',') + 1));
+                break;
+            }
+        }
+    }
+
     private void playKeyClick(int primaryCode) {
         // if mAudioManager is null, we don't have the ringer state yet
         // mAudioManager will be set by updateRingerMode
@@ -2079,17 +2120,20 @@
             }
         }
         if (isSoundOn()) {
-            int sound = AudioManager.FX_KEYPRESS_STANDARD;
+            final int sound;
             switch (primaryCode) {
-                case Keyboard.CODE_DELETE:
-                    sound = AudioManager.FX_KEYPRESS_DELETE;
-                    break;
-                case Keyboard.CODE_ENTER:
-                    sound = AudioManager.FX_KEYPRESS_RETURN;
-                    break;
-                case Keyboard.CODE_SPACE:
-                    sound = AudioManager.FX_KEYPRESS_SPACEBAR;
-                    break;
+            case Keyboard.CODE_DELETE:
+                sound = AudioManager.FX_KEYPRESS_DELETE;
+                break;
+            case Keyboard.CODE_ENTER:
+                sound = AudioManager.FX_KEYPRESS_RETURN;
+                break;
+            case Keyboard.CODE_SPACE:
+                sound = AudioManager.FX_KEYPRESS_SPACEBAR;
+                break;
+            default:
+                sound = AudioManager.FX_KEYPRESS_STANDARD;
+                break;
             }
             mAudioManager.playSoundEffect(sound, mFxVolume);
         }
@@ -2099,11 +2143,16 @@
         if (!mSettingsValues.mVibrateOn) {
             return;
         }
-        LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
-        if (inputView != null) {
-            inputView.performHapticFeedback(
-                    HapticFeedbackConstants.KEYBOARD_TAP,
-                    HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+        if (mKeypressVibrationDuration < 0) {
+            // Go ahead with the system default
+            LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+            if (inputView != null) {
+                inputView.performHapticFeedback(
+                        HapticFeedbackConstants.KEYBOARD_TAP,
+                        HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+            }
+        } else if (mVibrator != null) {
+            mVibrator.vibrate(mKeypressVibrationDuration);
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/MoreSuggestions.java
index 24011c4..9a59ef2 100644
--- a/java/src/com/android/inputmethod/latin/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/MoreSuggestions.java
@@ -92,8 +92,9 @@
                 }
                 mNumColumnsInRow[row] = pos - rowStartPos;
                 mNumRows = row + 1;
-                mWidth = mOccupiedWidth = Math.max(minWidth, calcurateMaxRowWidth(fromPos, pos));
-                mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
+                mBaseWidth = mOccupiedWidth = Math.max(
+                        minWidth, calcurateMaxRowWidth(fromPos, pos));
+                mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
                 return pos - fromPos;
             }
 
@@ -149,26 +150,22 @@
 
             public int getWidth(int pos) {
                 final int numColumnInRow = getNumColumnInRow(pos);
-                return (mWidth - mDividerWidth * (numColumnInRow - 1)) / numColumnInRow;
+                return (mOccupiedWidth - mDividerWidth * (numColumnInRow - 1)) / numColumnInRow;
             }
 
-            public int getFlags(int pos) {
-                int rowFlags = 0;
-
+            public void markAsEdgeKey(Key key, int pos) {
                 final int row = mRowNumbers[pos];
                 if (row == 0)
-                    rowFlags |= Keyboard.EDGE_BOTTOM;
+                    key.markAsBottomEdge(this);
                 if (row == mNumRows - 1)
-                    rowFlags |= Keyboard.EDGE_TOP;
+                    key.markAsTopEdge(this);
 
                 final int numColumnInRow = mNumColumnsInRow[row];
                 final int column = getColumnNumber(pos);
                 if (column == 0)
-                    rowFlags |= Keyboard.EDGE_LEFT;
+                    key.markAsLeftEdge(this);
                 if (column == numColumnInRow - 1)
-                    rowFlags |= Keyboard.EDGE_RIGHT;
-
-                return rowFlags;
+                    key.markAsRightEdge(this);
             }
         }
 
@@ -213,7 +210,8 @@
                 final int index = pos + SUGGESTION_CODE_BASE;
                 final Key key = new Key(
                         params, word, info, null, index, null, x, y, width,
-                        params.mDefaultRowHeight, params.getFlags(pos));
+                        params.mDefaultRowHeight);
+                params.markAsEdgeKey(key, pos);
                 params.onAddKey(key);
                 final int columnNumber = params.getColumnNumber(pos);
                 final int numColumnInRow = params.getNumColumnInRow(pos);
diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
index 15a0cec..5a2eb16 100644
--- a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
@@ -145,13 +145,6 @@
         // Nothing to do with.
     }
 
-    private final View.OnTouchListener mMotionEventDelegate = new View.OnTouchListener() {
-        @Override
-        public boolean onTouch(View view, MotionEvent me) {
-            return MoreSuggestionsView.this.dispatchTouchEvent(me);
-        }
-    };
-
     @Override
     public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY,
             PopupWindow window, KeyboardActionListener listener) {
@@ -170,9 +163,7 @@
                 - (container.getMeasuredHeight() - container.getPaddingBottom())
                 + parentView.getPaddingTop() + mCoordinates[1];
 
-        container.setOnTouchListener(mMotionEventDelegate);
         window.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-        window.setFocusable(true);
         window.setOutsideTouchable(true);
         window.setContentView(container);
         window.setWidth(container.getMeasuredWidth());
@@ -193,6 +184,7 @@
 
     @Override
     public boolean dismissMoreKeysPanel() {
+        if (mController == null) return false;
         return mController.dismissMoreKeysPanel();
     }
 
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index c97f567..d706cd0 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -171,7 +171,8 @@
 
             // Get the settings preferences
             final boolean hasVibrator = VibratorCompatWrapper.getInstance(context).hasVibrator();
-            mVibrateOn = hasVibrator && prefs.getBoolean(Settings.PREF_VIBRATE_ON, false);
+            mVibrateOn = hasVibrator && prefs.getBoolean(Settings.PREF_VIBRATE_ON,
+                    res.getBoolean(R.bool.config_default_vibration_enabled));
             mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON,
                     res.getBoolean(R.bool.config_default_sound_enabled));
             mKeyPreviewPopupOn = isKeyPreviewPopupEnabled(prefs, res);
diff --git a/java/src/com/android/inputmethod/latin/SuggestionsView.java b/java/src/com/android/inputmethod/latin/SuggestionsView.java
index 13beb44..9d0e42a 100644
--- a/java/src/com/android/inputmethod/latin/SuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/SuggestionsView.java
@@ -19,8 +19,14 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Message;
 import android.os.SystemClock;
@@ -29,12 +35,11 @@
 import android.text.Spanned;
 import android.text.TextPaint;
 import android.text.TextUtils;
-import android.text.style.BackgroundColorSpan;
 import android.text.style.CharacterStyle;
-import android.text.style.ForegroundColorSpan;
 import android.text.style.StyleSpan;
 import android.text.style.UnderlineSpan;
 import android.util.AttributeSet;
+import android.view.GestureDetector;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -86,7 +91,6 @@
 
     private Listener mListener;
     private SuggestedWords mSuggestions = SuggestedWords.EMPTY;
-    private boolean mShowingAutoCorrectionInverted;
 
     private final SuggestionsViewParams mParams;
     private static final float MIN_TEXT_XSCALE = 0.70f;
@@ -95,10 +99,8 @@
 
     private static class UiHandler extends StaticInnerHandlerWrapper<SuggestionsView> {
         private static final int MSG_HIDE_PREVIEW = 0;
-        private static final int MSG_UPDATE_SUGGESTION = 1;
 
         private static final long DELAY_HIDE_PREVIEW = 1300;
-        private static final long DELAY_UPDATE_SUGGESTION = 300;
 
         public UiHandler(SuggestionsView outerInstance) {
             super(outerInstance);
@@ -111,9 +113,6 @@
             case MSG_HIDE_PREVIEW:
                 suggestionsView.hidePreview();
                 break;
-            case MSG_UPDATE_SUGGESTION:
-                suggestionsView.updateSuggestions();
-                break;
             }
         }
 
@@ -126,19 +125,8 @@
             removeMessages(MSG_HIDE_PREVIEW);
         }
 
-        public void postUpdateSuggestions() {
-            cancelUpdateSuggestions();
-            sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION),
-                    DELAY_UPDATE_SUGGESTION);
-        }
-
-        public void cancelUpdateSuggestions() {
-            removeMessages(MSG_UPDATE_SUGGESTION);
-        }
-
         public void cancelAllMessages() {
             cancelHidePreview();
-            cancelUpdateSuggestions();
         }
     }
 
@@ -167,15 +155,13 @@
         private final float mCenterSuggestionWeight;
         private final int mCenterSuggestionIndex;
         private final Drawable mMoreSuggestionsHint;
+        private static final String MORE_SUGGESTIONS_HINT = "\u2026";
 
         private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
         private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
-        private final CharacterStyle mInvertedForegroundColorSpan;
-        private final CharacterStyle mInvertedBackgroundColorSpan;
         private static final int AUTO_CORRECT_BOLD = 0x01;
         private static final int AUTO_CORRECT_UNDERLINE = 0x02;
-        private static final int AUTO_CORRECT_INVERT = 0x04;
-        private static final int VALID_TYPED_WORD_BOLD = 0x08;
+        private static final int VALID_TYPED_WORD_BOLD = 0x04;
 
         private final int mSuggestionStripOption;
 
@@ -225,7 +211,6 @@
             mCenterSuggestionWeight = getPercent(a,
                     R.styleable.SuggestionsView_centerSuggestionPercentile,
                     DEFAULT_CENTER_SUGGESTION_PERCENTILE);
-            mMoreSuggestionsHint = a.getDrawable(R.styleable.SuggestionsView_moreSuggestionsHint);
             mMaxMoreSuggestionsRow = a.getInt(
                     R.styleable.SuggestionsView_maxMoreSuggestionsRow,
                     DEFAULT_MAX_MORE_SUGGESTIONS_ROW);
@@ -233,19 +218,35 @@
                     R.styleable.SuggestionsView_minMoreSuggestionsWidth);
             a.recycle();
 
+            mMoreSuggestionsHint = getMoreSuggestionsHint(res,
+                    res.getDimension(R.dimen.more_suggestions_hint_text_size), mColorAutoCorrect);
             mCenterSuggestionIndex = mSuggestionsCountInStrip / 2;
             mMoreSuggestionsBottomGap = res.getDimensionPixelOffset(
                     R.dimen.more_suggestions_bottom_gap);
 
-            mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff);
-            mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord);
-
             final LayoutInflater inflater = LayoutInflater.from(context);
             mWordToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null);
             mHintToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null);
             mHintToSaveText = context.getText(R.string.hint_add_to_dictionary);
         }
 
+        private static Drawable getMoreSuggestionsHint(Resources res, float textSize, int color) {
+            final Paint paint = new Paint();
+            paint.setAntiAlias(true);
+            paint.setTextAlign(Align.CENTER);
+            paint.setTextSize(textSize);
+            paint.setColor(color);
+            final Rect bounds = new Rect();
+            paint.getTextBounds(MORE_SUGGESTIONS_HINT, 0, MORE_SUGGESTIONS_HINT.length(), bounds);
+            final int width = Math.round(bounds.width() + 0.5f);
+            final int height = Math.round(bounds.height() + 0.5f);
+            final Bitmap buffer = Bitmap.createBitmap(
+                    width, (height * 3 / 2), Bitmap.Config.ARGB_8888);
+            final Canvas canvas = new Canvas(buffer);
+            canvas.drawText(MORE_SUGGESTIONS_HINT, width / 2, height, paint);
+            return new BitmapDrawable(res, buffer);
+        }
+
         // Read integer value in TypedArray as percent.
         private static float getPercent(TypedArray a, int index, int defValue) {
             return a.getInt(index, defValue) / 100.0f;
@@ -320,16 +321,6 @@
             return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color));
         }
 
-        public CharSequence getInvertedText(CharSequence text) {
-            if ((mSuggestionStripOption & AUTO_CORRECT_INVERT) == 0)
-                return null;
-            final int len = text.length();
-            final Spannable word = new SpannableString(text);
-            word.setSpan(mInvertedBackgroundColorSpan, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-            word.setSpan(mInvertedForegroundColorSpan, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-            return word;
-        }
-
         public void layout(SuggestedWords suggestions, ViewGroup stripView, ViewGroup placer,
                 int stripWidth) {
             if (suggestions.isPunctuationSuggestions()) {
@@ -522,8 +513,25 @@
         final Resources res = context.getResources();
         mMoreSuggestionsModalTolerance = res.getDimensionPixelOffset(
                 R.dimen.more_suggestions_modal_tolerance);
+        mMoreSuggestionsSlidingDetector = new GestureDetector(
+                context, mMoreSuggestionsSlidingListener);
     }
 
+    private final View.OnTouchListener mMoreSuggestionsCanceller = new View.OnTouchListener() {
+        @Override
+        public boolean onTouch(View view, MotionEvent me) {
+            if (!mMoreSuggestionsWindow.isShowing()) return false;
+
+            switch (me.getAction()) {
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_POINTER_UP:
+                return mMoreSuggestionsView.dismissMoreKeysPanel();
+            default:
+                return true;
+            }
+        }
+    };
+
     /**
      * A connection back to the input method.
      * @param listener
@@ -534,21 +542,11 @@
     }
 
     public void setSuggestions(SuggestedWords suggestions) {
-        if (suggestions == null)
+        if (suggestions == null || suggestions.size() == 0)
             return;
-        mSuggestions = suggestions;
-        if (mShowingAutoCorrectionInverted) {
-            mHandler.postUpdateSuggestions();
-        } else {
-            updateSuggestions();
-        }
-    }
 
-    private void updateSuggestions() {
         clear();
-        if (mSuggestions.size() == 0)
-            return;
-
+        mSuggestions = suggestions;
         mParams.layout(mSuggestions, mSuggestionsStrip, this, getWidth());
     }
 
@@ -637,15 +635,6 @@
         }
     }
 
-    public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) {
-        final CharSequence inverted = mParams.getInvertedText(autoCorrectedWord);
-        if (inverted == null)
-            return;
-        final TextView tv = mWords.get(1);
-        tv.setText(inverted);
-        mShowingAutoCorrectionInverted = true;
-    }
-
     public boolean isShowingAddToDictionaryHint() {
         return mSuggestionsStrip.getChildCount() > 0
                 && mSuggestionsStrip.getChildAt(0) == mParams.mWordToSaveView;
@@ -669,7 +658,6 @@
     }
 
     public void clear() {
-        mShowingAutoCorrectionInverted = false;
         mSuggestionsStrip.removeAllViews();
         removeAllViews();
         addView(mSuggestionsStrip);
@@ -739,6 +727,7 @@
         if (mMoreSuggestionsWindow.isShowing()) {
             mMoreSuggestionsWindow.dismiss();
             mKeyboardView.dimEntireKeyboard(false);
+            mKeyboardView.setOnTouchListener(null);
             return true;
         }
         return false;
@@ -750,6 +739,10 @@
 
     @Override
     public boolean onLongClick(View view) {
+        return showMoreSuggestions();
+    }
+
+    private boolean showMoreSuggestions() {
         final SuggestionsViewParams params = mParams;
         if (params.mMoreSuggestionsAvailable) {
             final int stripWidth = getWidth();
@@ -770,29 +763,51 @@
             moreKeysPanel.showMoreKeysPanel(
                     this, mMoreSuggestionsController, pointX, pointY,
                     mMoreSuggestionsWindow, mMoreSuggestionsListener);
-            mCheckingIfModalOrSlidingMode = true;
+            mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
             mOriginX = mLastX;
             mOriginY = mLastY;
-            view.setPressed(false);
             mKeyboardView.dimEntireKeyboard(true);
+            mKeyboardView.setOnTouchListener(mMoreSuggestionsCanceller);
+            for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
+                mWords.get(i).setPressed(false);
+            }
             return true;
         }
         return false;
     }
 
     // Working variables for onLongClick and dispatchTouchEvent.
-    private boolean mCheckingIfModalOrSlidingMode;
+    private int mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_MODAL_MODE;
+    private static final int MORE_SUGGESTIONS_IN_MODAL_MODE = 0;
+    private static final int MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING = 1;
+    private static final int MORE_SUGGESTIONS_IN_SLIDING_MODE = 2;
     private int mLastX;
     private int mLastY;
     private int mOriginX;
     private int mOriginY;
     private final int mMoreSuggestionsModalTolerance;
+    private final GestureDetector mMoreSuggestionsSlidingDetector;
+    private final GestureDetector.OnGestureListener mMoreSuggestionsSlidingListener =
+            new GestureDetector.SimpleOnGestureListener() {
+        @Override
+        public boolean onScroll(MotionEvent down, MotionEvent me, float deltaX, float deltaY) {
+            final float dy = me.getY() - down.getY();
+            if (deltaY > 0 && dy < 0) {
+                return showMoreSuggestions();
+            }
+            return false;
+        }
+    };
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent me) {
-        if (!mMoreSuggestionsWindow.isShowing()) {
+        if (!mMoreSuggestionsWindow.isShowing()
+                || mMoreSuggestionsMode == MORE_SUGGESTIONS_IN_MODAL_MODE) {
             mLastX = (int)me.getX();
             mLastY = (int)me.getY();
+            if (mMoreSuggestionsSlidingDetector.onTouchEvent(me)) {
+                return true;
+            }
             return super.dispatchTouchEvent(me);
         }
 
@@ -807,22 +822,22 @@
         final int translatedX = moreKeysPanel.translateX(x);
         final int translatedY = moreKeysPanel.translateY(y);
 
-        if (mCheckingIfModalOrSlidingMode) {
+        if (mMoreSuggestionsMode == MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING) {
             if (Math.abs(x - mOriginX) >= mMoreSuggestionsModalTolerance
                     || mOriginY - y >= mMoreSuggestionsModalTolerance) {
                 // Decided to be in the sliding input mode only when the touch point has been moved
                 // upward.
-                mCheckingIfModalOrSlidingMode = false;
+                mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_SLIDING_MODE;
                 tracker.onShowMoreKeysPanel(
                         translatedX, translatedY, SystemClock.uptimeMillis(), moreKeysPanel);
             } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) {
                 // Decided to be in the modal input mode
-                mCheckingIfModalOrSlidingMode = false;
+                mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_MODAL_MODE;
             }
             return true;
         }
 
-        // Process sliding motion events
+        // MORE_SUGGESTIONS_IN_SLIDING_MODE
         tracker.processMotionEvent(action, translatedX, translatedY, eventTime, moreKeysPanel);
         return true;
     }
@@ -847,7 +862,7 @@
     }
 
     @Override
-    public void onDetachedFromWindow() {
+    protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mHandler.cancelAllMessages();
         hidePreview();
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 1e5b877..2546df0 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -60,8 +60,11 @@
     private static final int CAPITALIZE_ALL = 2; // All caps
 
     private final static String[] EMPTY_STRING_ARRAY = new String[0];
-    private final static SuggestionsInfo EMPTY_SUGGESTIONS_INFO =
+    private final static SuggestionsInfo NOT_IN_DICT_EMPTY_SUGGESTIONS =
             new SuggestionsInfo(0, EMPTY_STRING_ARRAY);
+    private final static SuggestionsInfo IN_DICT_EMPTY_SUGGESTIONS =
+            new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY,
+                    EMPTY_STRING_ARRAY);
     private Map<String, DictionaryPool> mDictionaryPools =
             Collections.synchronizedMap(new TreeMap<String, DictionaryPool>());
     private Map<String, Dictionary> mUserDictionaries =
@@ -82,10 +85,10 @@
     private static class SuggestionsGatherer implements WordCallback {
         public static class Result {
             public final String[] mSuggestions;
-            public final boolean mLooksLikeTypo;
-            public Result(final String[] gatheredSuggestions, final boolean looksLikeTypo) {
+            public final boolean mHasLikelySuggestions;
+            public Result(final String[] gatheredSuggestions, final boolean hasLikelySuggestions) {
                 mSuggestions = gatheredSuggestions;
-                mLooksLikeTypo = looksLikeTypo;
+                mHasLikelySuggestions = hasLikelySuggestions;
             }
         }
 
@@ -146,19 +149,19 @@
         public Result getResults(final CharSequence originalText, final double threshold,
                 final int capitalizeType, final Locale locale) {
             final String[] gatheredSuggestions;
-            final boolean looksLikeTypo;
+            final boolean hasLikelySuggestions;
             if (0 == mLength) {
                 // Either we found no suggestions, or we found some BUT the max length was 0.
                 // If we found some mBestSuggestion will not be null. If it is null, then
                 // we found none, regardless of the max length.
                 if (null == mBestSuggestion) {
                     gatheredSuggestions = null;
-                    looksLikeTypo = false;
+                    hasLikelySuggestions = false;
                 } else {
                     gatheredSuggestions = EMPTY_STRING_ARRAY;
                     final double normalizedScore =
                             Utils.calcNormalizedScore(originalText, mBestSuggestion, mBestScore);
-                    looksLikeTypo = (normalizedScore > threshold);
+                    hasLikelySuggestions = (normalizedScore > threshold);
                 }
             } else {
                 if (DBG) {
@@ -192,14 +195,14 @@
                 final CharSequence bestSuggestion = mSuggestions.get(0);
                 final double normalizedScore =
                         Utils.calcNormalizedScore(originalText, bestSuggestion, bestScore);
-                looksLikeTypo = (normalizedScore > threshold);
+                hasLikelySuggestions = (normalizedScore > threshold);
                 if (DBG) {
                     Log.i(TAG, "Best suggestion : " + bestSuggestion + ", score " + bestScore);
                     Log.i(TAG, "Normalized score = " + normalizedScore + " (threshold " + threshold
-                            + ") => looksLikeTypo = " + looksLikeTypo);
+                            + ") => hasLikelySuggestions = " + hasLikelySuggestions);
                 }
             }
-            return new Result(gatheredSuggestions, looksLikeTypo);
+            return new Result(gatheredSuggestions, hasLikelySuggestions);
         }
     }
 
@@ -330,8 +333,23 @@
             try {
                 final String text = textInfo.getText();
 
-                if (shouldFilterOut(text)) return EMPTY_SUGGESTIONS_INFO;
+                if (shouldFilterOut(text)) {
+                    DictAndProximity dictInfo = null;
+                    try {
+                        dictInfo = mDictionaryPool.takeOrGetNull();
+                        if (null == dictInfo) return NOT_IN_DICT_EMPTY_SUGGESTIONS;
+                        return dictInfo.mDictionary.isValidWord(text) ? IN_DICT_EMPTY_SUGGESTIONS
+                                : NOT_IN_DICT_EMPTY_SUGGESTIONS;
+                    } finally {
+                        if (null != dictInfo) {
+                            if (!mDictionaryPool.offer(dictInfo)) {
+                                Log.e(TAG, "Can't re-insert a dictionary into its pool");
+                            }
+                        }
+                    }
+                }
 
+                // TODO: Don't gather suggestions if the limit is <= 0 unless necessary
                 final SuggestionsGatherer suggestionsGatherer =
                         new SuggestionsGatherer(suggestionsLimit);
                 final WordComposer composer = new WordComposer();
@@ -353,8 +371,10 @@
 
                 final int capitalizeType = getCapitalizationType(text);
                 boolean isInDict = true;
+                DictAndProximity dictInfo = null;
                 try {
-                    final DictAndProximity dictInfo = mDictionaryPool.take();
+                    dictInfo = mDictionaryPool.takeOrGetNull();
+                    if (null == dictInfo) return NOT_IN_DICT_EMPTY_SUGGESTIONS;
                     dictInfo.mDictionary.getWords(composer, suggestionsGatherer,
                             dictInfo.mProximityInfo);
                     isInDict = dictInfo.mDictionary.isValidWord(text);
@@ -364,12 +384,12 @@
                         // want to test a lowercase version of it.
                         isInDict = dictInfo.mDictionary.isValidWord(text.toLowerCase(mLocale));
                     }
-                    if (!mDictionaryPool.offer(dictInfo)) {
-                        Log.e(TAG, "Can't re-insert a dictionary into its pool");
+                } finally {
+                    if (null != dictInfo) {
+                        if (!mDictionaryPool.offer(dictInfo)) {
+                            Log.e(TAG, "Can't re-insert a dictionary into its pool");
+                        }
                     }
-                } catch (InterruptedException e) {
-                    // I don't think this can happen.
-                    return EMPTY_SUGGESTIONS_INFO;
                 }
 
                 final SuggestionsGatherer.Result result = suggestionsGatherer.getResults(text,
@@ -378,17 +398,18 @@
                 if (DBG) {
                     Log.i(TAG, "Spell checking results for " + text + " with suggestion limit "
                             + suggestionsLimit);
-                    Log.i(TAG, "IsInDict = " + result.mLooksLikeTypo);
-                    Log.i(TAG, "LooksLikeTypo = " + result.mLooksLikeTypo);
+                    Log.i(TAG, "IsInDict = " + isInDict);
+                    Log.i(TAG, "LooksLikeTypo = " + (!isInDict));
+                    Log.i(TAG, "HasLikelySuggestions = " + result.mHasLikelySuggestions);
                     for (String suggestion : result.mSuggestions) {
                         Log.i(TAG, suggestion);
                     }
                 }
 
+                // TODO: actually use result.mHasLikelySuggestions
                 final int flags =
-                        (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY : 0)
-                                | (result.mLooksLikeTypo
-                                        ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0);
+                        (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
+                                : SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO);
                 return new SuggestionsInfo(flags, result.mSuggestions);
             } catch (RuntimeException e) {
                 // Don't kill the keyboard if there is a bug in the spell checker
@@ -396,7 +417,7 @@
                     throw e;
                 } else {
                     Log.e(TAG, "Exception while spellcheking: " + e);
-                    return EMPTY_SUGGESTIONS_INFO;
+                    return NOT_IN_DICT_EMPTY_SUGGESTIONS;
                 }
             }
         }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index ee294f6..dec18c1 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -56,6 +56,15 @@
         }
     }
 
+    // Convenience method
+    public DictAndProximity takeOrGetNull() {
+        try {
+            return take();
+        } catch (InterruptedException e) {
+            return null;
+        }
+    }
+
     public void close() {
         synchronized(this) {
             mClosed = true;
diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
index f3e2a7e..07cee40 100644
--- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
+++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
@@ -30,10 +30,25 @@
 
 static jint latinime_Keyboard_setProximityInfo(JNIEnv *env, jobject object,
         jint maxProximityCharsSize, jint displayWidth, jint displayHeight, jint gridWidth,
-        jint gridHeight, jintArray proximityCharsArray) {
-    jint* proximityChars = env->GetIntArrayElements(proximityCharsArray, NULL);
+        jint gridHeight, jintArray proximityCharsArray, jint keyCount,
+        jintArray keyXCoordinateArray, jintArray keyYCoordinateArray, jintArray keyWidthArray,
+        jintArray keyHeightArray, jintArray keyCharCodeArray, jint themeId) {
+    jint *proximityChars = env->GetIntArrayElements(proximityCharsArray, NULL);
+    jint *keyXCoordinates = safeGetIntArrayElements(env, keyXCoordinateArray);
+    jint *keyYCoordinates = safeGetIntArrayElements(env, keyYCoordinateArray);
+    jint *keyWidths = safeGetIntArrayElements(env, keyWidthArray);
+    jint *keyHeights = safeGetIntArrayElements(env, keyHeightArray);
+    jint *keyCharCodes = safeGetIntArrayElements(env, keyCharCodeArray);
     ProximityInfo *proximityInfo = new ProximityInfo(maxProximityCharsSize, displayWidth,
-            displayHeight, gridWidth, gridHeight, (const uint32_t *)proximityChars);
+            displayHeight, gridWidth, gridHeight, (const uint32_t*)proximityChars,
+            keyCount, (const int32_t*)keyXCoordinates, (const int32_t*)keyYCoordinates,
+            (const int32_t*)keyWidths, (const int32_t*)keyHeights, (const int32_t*)keyCharCodes,
+            themeId);
+    safeReleaseIntArrayElements(env, keyCharCodeArray, keyCharCodes);
+    safeReleaseIntArrayElements(env, keyHeightArray, keyHeights);
+    safeReleaseIntArrayElements(env, keyWidthArray, keyWidths);
+    safeReleaseIntArrayElements(env, keyYCoordinateArray, keyYCoordinates);
+    safeReleaseIntArrayElements(env, keyXCoordinateArray, keyXCoordinates);
     env->ReleaseIntArrayElements(proximityCharsArray, proximityChars, 0);
     return (jint)proximityInfo;
 }
@@ -45,12 +60,12 @@
 }
 
 static JNINativeMethod sKeyboardMethods[] = {
-    {"setProximityInfoNative", "(IIIII[I)I", (void*)latinime_Keyboard_setProximityInfo},
+    {"setProximityInfoNative", "(IIIII[II[I[I[I[I[II)I", (void*)latinime_Keyboard_setProximityInfo},
     {"releaseProximityInfoNative", "(I)V", (void*)latinime_Keyboard_release}
 };
 
 int register_ProximityInfo(JNIEnv *env) {
-    const char* const kClassPathName = "com/android/inputmethod/keyboard/ProximityInfo";
+    const char *const kClassPathName = "com/android/inputmethod/keyboard/ProximityInfo";
     return registerNativeMethods(env, kClassPathName, sKeyboardMethods,
             sizeof(sKeyboardMethods) / sizeof(sKeyboardMethods[0]));
 }
diff --git a/native/jni/jni_common.h b/native/jni/jni_common.h
index c502fa3..dbf6d3e 100644
--- a/native/jni/jni_common.h
+++ b/native/jni/jni_common.h
@@ -18,13 +18,29 @@
 #ifndef LATINIME_JNI_COMMON_H
 #define LATINIME_JNI_COMMON_H
 
+#include <stdlib.h>
+
 #include "jni.h"
 
 namespace latinime {
 
-int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* methods,
+int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods,
         int numMethods);
 
+inline jint *safeGetIntArrayElements(JNIEnv *env, jintArray jArray) {
+    if (jArray) {
+        return env->GetIntArrayElements(jArray, NULL);
+    } else {
+        return NULL;
+    }
+}
+
+inline void safeReleaseIntArrayElements(JNIEnv *env, jintArray jArray, jint *cArray) {
+    if (jArray) {
+        env->ReleaseIntArrayElements(jArray, cArray, 0);
+    }
+}
+
 } // namespace latinime
 
 #endif // LATINIME_JNI_COMMON_H
diff --git a/native/src/correction.h b/native/src/correction.h
index f3194b7..41130ad 100644
--- a/native/src/correction.h
+++ b/native/src/correction.h
@@ -119,8 +119,9 @@
     int mTerminalInputIndex;
     int mTerminalOutputIndex;
     unsigned short mWord[MAX_WORD_LENGTH_INTERNAL];
+    // Edit distance calculation requires a buffer with (N+1)^2 length for the input length N.
     // Caveat: Do not create multiple tables per thread as this table eats up RAM a lot.
-    int mEditDistanceTable[MAX_WORD_LENGTH_INTERNAL * MAX_WORD_LENGTH_INTERNAL];
+    int mEditDistanceTable[(MAX_WORD_LENGTH_INTERNAL + 1) * (MAX_WORD_LENGTH_INTERNAL + 1)];
 
     CorrectionState mCorrectionStates[MAX_WORD_LENGTH_INTERNAL];
 
diff --git a/native/src/defines.h b/native/src/defines.h
index 009d0ad..55469a7 100644
--- a/native/src/defines.h
+++ b/native/src/defines.h
@@ -131,7 +131,7 @@
 #endif // FLAG_DBG
 
 #ifndef U_SHORT_MAX
-#define U_SHORT_MAX 1 << 16
+#define U_SHORT_MAX 65535    // ((1 << 16) - 1)
 #endif
 #ifndef S_INT_MAX
 #define S_INT_MAX 2147483647 // ((1 << 31) - 1)
diff --git a/native/src/defines_touch_position_correction.h b/native/src/defines_touch_position_correction.h
new file mode 100644
index 0000000..1229068
--- /dev/null
+++ b/native/src/defines_touch_position_correction.h
@@ -0,0 +1,61 @@
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef LATINIME_DEFINES_TOUCH_POSITION_CORRECTION_H
+#define LATINIME_DEFINES_TOUCH_POSITION_CORRECTION_H
+
+#define OUTER_SWEET_SPOT_RADIUS_RATIO 1.3
+
+static const char* TOUCH_POSITION_CORRECTION_GROUPS[] = {
+        "qwertyuiop",
+        "a",
+        "sdfghjk",
+        "l",
+        "zxcvbnm",
+};
+
+// (center X) / (key width)
+static const float RELATIVE_TOUCH_CENTER_X[] = {
+        0,          // qwertyuiop
+        -0.26871,   // a
+        0,          // sdfghjk
+        0.028050,   // l
+        0,          // zxcvbnm
+};
+
+// (center Y) / (key height)
+static const float RELATIVE_TOUCH_CENTER_Y[] = {
+        0.192088,   // qwertyuiop
+        0.214100,   // a
+        0.216640,   // sdfghjk
+        0.233288,   // l
+        0.286598,   // zxcvbnm
+};
+
+// (sweet spot radius) / ((key width) + (key height))
+static const float SWEET_SPOT_RADIUS[] = {
+        0.148955,   // qwertyuiop
+        0.185437,   // a
+        0.145522,   // sdfghjk
+        0.156882,   // l
+        0.144211,   // zxcvbnm
+};
+
+#define CORRECTION_GROUP_COUNT \
+    ((int)(sizeof(TOUCH_POSITION_CORRECTION_GROUPS) / sizeof(TOUCH_POSITION_CORRECTION_GROUPS[0])))
+
+#endif // LATINIME_DEFINES_TOUCH_POSITION_CORRECTION_H
diff --git a/native/src/proximity_info.cpp b/native/src/proximity_info.cpp
index 361bdac..58842b9 100644
--- a/native/src/proximity_info.cpp
+++ b/native/src/proximity_info.cpp
@@ -14,29 +14,76 @@
  * limitations under the License.
  */
 
+#include <assert.h>
 #include <stdio.h>
 #include <string.h>
 
 #define LOG_TAG "LatinIME: proximity_info.cpp"
 
+#include "defines_touch_position_correction.h"
 #include "dictionary.h"
 #include "proximity_info.h"
 
 namespace latinime {
 
+inline void copyOrFillZero(void *to, const void *from, size_t size) {
+    if (from) {
+        memcpy(to, from, size);
+    } else {
+        memset(to, 0, size);
+    }
+}
+
 ProximityInfo::ProximityInfo(const int maxProximityCharsSize, const int keyboardWidth,
         const int keyboardHeight, const int gridWidth, const int gridHeight,
-        const uint32_t *proximityCharsArray)
+        const uint32_t *proximityCharsArray, const int keyCount, const int32_t *keyXCoordinates,
+        const int32_t *keyYCoordinates, const int32_t *keyWidths, const int32_t *keyHeights,
+        const int32_t *keyCharCodes, int themeId)
         : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth),
           KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight),
           CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth),
-          CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight) {
+          CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight),
+          KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)), THEME_ID(themeId) {
     const int len = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
     mProximityCharsArray = new uint32_t[len];
     if (DEBUG_PROXIMITY_INFO) {
         LOGI("Create proximity info array %d", len);
     }
     memcpy(mProximityCharsArray, proximityCharsArray, len * sizeof(mProximityCharsArray[0]));
+
+    copyOrFillZero(mKeyXCoordinates, keyXCoordinates, KEY_COUNT * sizeof(mKeyXCoordinates[0]));
+    copyOrFillZero(mKeyYCoordinates, keyYCoordinates, KEY_COUNT * sizeof(mKeyYCoordinates[0]));
+    copyOrFillZero(mKeyWidths, keyWidths, KEY_COUNT * sizeof(mKeyWidths[0]));
+    copyOrFillZero(mKeyHeights, keyHeights, KEY_COUNT * sizeof(mKeyHeights[0]));
+    copyOrFillZero(mKeyCharCodes, keyCharCodes, KEY_COUNT * sizeof(mKeyCharCodes[0]));
+
+    initializeCodeToGroup();
+    initializeCodeToKeyIndex();
+}
+
+// Build the reversed look up table from the char code to the index in its group.
+// see TOUCH_POSITION_CORRECTION_GROUPS
+void ProximityInfo::initializeCodeToGroup() {
+    memset(mCodeToGroup, -1, (MAX_GROUPED_CHAR_CODE + 1) * sizeof(mCodeToGroup[0]));
+    for (int i = 0; i < CORRECTION_GROUP_COUNT; ++i) {
+        const char *group = TOUCH_POSITION_CORRECTION_GROUPS[i];
+        for (int j = 0; group[j]; ++j) {
+            const int code = group[j];
+            if (0 <= code && code <= MAX_GROUPED_CHAR_CODE)
+                mCodeToGroup[code] = i;
+        }
+    }
+}
+
+// Build the reversed look up table from the char code to the index in mKeyXCoordinates,
+// mKeyYCoordinates, mKeyWidths, mKeyHeights, mKeyCharCodes.
+void ProximityInfo::initializeCodeToKeyIndex() {
+    memset(mCodeToKeyIndex, -1, (MAX_GROUPED_CHAR_CODE + 1) * sizeof(mCodeToKeyIndex[0]));
+    for (int i = 0; i < KEY_COUNT; ++i) {
+        const int code = mKeyCharCodes[i];
+        if (0 <= code && code <= MAX_GROUPED_CHAR_CODE)
+            mCodeToKeyIndex[code] = i;
+    }
 }
 
 ProximityInfo::~ProximityInfo() {
@@ -162,4 +209,7 @@
     return true;
 }
 
+const int ProximityInfo::MAX_KEY_COUNT_IN_A_KEYBOARD;
+const int ProximityInfo::MAX_GROUPED_CHAR_CODE;
+
 } // namespace latinime
diff --git a/native/src/proximity_info.h b/native/src/proximity_info.h
index 75fc8fb..3190e73 100644
--- a/native/src/proximity_info.h
+++ b/native/src/proximity_info.h
@@ -35,7 +35,9 @@
 
     ProximityInfo(const int maxProximityCharsSize, const int keyboardWidth,
             const int keybaordHeight, const int gridWidth, const int gridHeight,
-            const uint32_t *proximityCharsArray);
+            const uint32_t *proximityCharsArray, const int keyCount, const int32_t *keyXCoordinates,
+            const int32_t *keyYCoordinates, const int32_t *keyWidths, const int32_t *keyHeights,
+            const int32_t *keyCharCodes, int themeId);
     ~ProximityInfo();
     bool hasSpaceProximity(const int x, const int y) const;
     void setInputParams(const int* inputCodes, const int inputLength);
@@ -51,7 +53,14 @@
     }
 
 private:
+    // The max number of the keys in one keyboard layout
+    static const int MAX_KEY_COUNT_IN_A_KEYBOARD = 64;
+    // The upper limit of the char code in TOUCH_POSITION_CORRECTION_GROUP
+    static const int MAX_GROUPED_CHAR_CODE = 127;
+
     int getStartIndexFromCoordinates(const int x, const int y) const;
+    void initializeCodeToGroup();
+    void initializeCodeToKeyIndex();
     const int MAX_PROXIMITY_CHARS_SIZE;
     const int KEYBOARD_WIDTH;
     const int KEYBOARD_HEIGHT;
@@ -59,10 +68,19 @@
     const int GRID_HEIGHT;
     const int CELL_WIDTH;
     const int CELL_HEIGHT;
+    const int KEY_COUNT;
+    const int THEME_ID;
     const int *mInputCodes;
     uint32_t *mProximityCharsArray;
+    int32_t mKeyXCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];
+    int32_t mKeyYCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];
+    int32_t mKeyWidths[MAX_KEY_COUNT_IN_A_KEYBOARD];
+    int32_t mKeyHeights[MAX_KEY_COUNT_IN_A_KEYBOARD];
+    int32_t mKeyCharCodes[MAX_KEY_COUNT_IN_A_KEYBOARD];
     int mInputLength;
     unsigned short mPrimaryInputWord[MAX_WORD_LENGTH_INTERNAL];
+    int mCodeToGroup[MAX_GROUPED_CHAR_CODE + 1];
+    int mCodeToKeyIndex[MAX_GROUPED_CHAR_CODE + 1];
 };
 
 } // namespace latinime
diff --git a/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java b/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java
index 031f35d..f6220ee 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java
@@ -217,7 +217,7 @@
         int nodeIndex = findIndexOfChar(mRoot, word[charIndex]);
         while (CHARACTER_NOT_FOUND != nodeIndex) {
             currentGroup = currentNode.mData.get(nodeIndex);
-            differentCharIndex = compareArrays(currentGroup.mChars, word, charIndex) ;
+            differentCharIndex = compareArrays(currentGroup.mChars, word, charIndex);
             if (ARRAYS_ARE_EQUAL != differentCharIndex
                     && differentCharIndex < currentGroup.mChars.length) break;
             if (null == currentGroup.mChildren) break;
@@ -268,7 +268,7 @@
                                     + new String(word, 0, word.length));
                         }
                         final CharGroup newGroup = new CharGroup(word,
-                                currentGroup.mBigrams, frequency);
+                                currentGroup.mBigrams, frequency, currentGroup.mChildren);
                         currentNode.mData.set(nodeIndex, newGroup);
                     }
                 } else {