am ef0d2123: (-s ours) am 20ffde8d: (-s ours) am 52fbad88: Support Turkish keyboard (DO NOT MERGE)

* commit 'ef0d2123871d18813bab796672e92a169a4fcaf2':
  Support Turkish keyboard (DO NOT MERGE)
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index e0eecfc..cb0a9a2 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -33,7 +33,7 @@
             </intent-filter>
         </activity>
 
-        <activity android:name="InputLanguageSelection"
+        <activity android:name="com.android.inputmethod.deprecated.languageswitcher.InputLanguageSelection"
                 android:label="@string/language_selection_title">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -41,5 +41,11 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+
+        <receiver android:name="SuggestionSpanPickedNotificationReceiver" android:enabled="true">
+            <intent-filter>
+                <action android:name="android.text.style.SUGGESTION_PICKED" />
+            </intent-filter>
+        </receiver>
     </application>
 </manifest>
diff --git a/java/proguard.flags b/java/proguard.flags
index 729f4ad..9094be4 100644
--- a/java/proguard.flags
+++ b/java/proguard.flags
@@ -18,3 +18,11 @@
 -keep class com.android.inputmethod.latin.AutoCorrection {
   java.lang.CharSequence getAutoCorrectionWord();
 }
+
+-keep class com.android.inputmethod.latin.Utils {
+  boolean equalsIgnoreCase(...);
+}
+
+-keep class com.android.inputmethod.latin.spellcheck.SpellChecker {
+  *;
+}
diff --git a/java/res/anim/key_preview_fadein.xml b/java/res/anim/key_preview_fadein.xml
deleted file mode 100644
index 9fad7b9..0000000
--- a/java/res/anim/key_preview_fadein.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:interpolator="@android:anim/decelerate_interpolator"
->
-    <alpha
-        android:fromAlpha="0.5"
-        android:toAlpha="1.0"
-        android:duration="@integer/config_preview_fadein_anim_time" />
-</set>
diff --git a/java/res/anim/key_preview_fadeout.xml b/java/res/anim/key_preview_fadeout.xml
deleted file mode 100644
index 7de5123..0000000
--- a/java/res/anim/key_preview_fadeout.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:interpolator="@android:anim/accelerate_interpolator"
->
-    <alpha
-        android:fromAlpha="1.0"
-        android:toAlpha="0.0"
-        android:duration="@integer/config_preview_fadeout_anim_time" />
-</set>
diff --git a/java/res/anim/mini_keyboard_fadein.xml b/java/res/anim/mini_keyboard_fadein.xml
index 9fad7b9..f80e8b8 100644
--- a/java/res/anim/mini_keyboard_fadein.xml
+++ b/java/res/anim/mini_keyboard_fadein.xml
@@ -25,5 +25,5 @@
     <alpha
         android:fromAlpha="0.5"
         android:toAlpha="1.0"
-        android:duration="@integer/config_preview_fadein_anim_time" />
+        android:duration="@integer/config_mini_keyboard_fadein_anim_time" />
 </set>
diff --git a/java/res/anim/mini_keyboard_fadeout.xml b/java/res/anim/mini_keyboard_fadeout.xml
index 7de5123..535b100 100644
--- a/java/res/anim/mini_keyboard_fadeout.xml
+++ b/java/res/anim/mini_keyboard_fadeout.xml
@@ -25,5 +25,5 @@
     <alpha
         android:fromAlpha="1.0"
         android:toAlpha="0.0"
-        android:duration="@integer/config_preview_fadeout_anim_time" />
+        android:duration="@integer/config_mini_keyboard_fadeout_anim_time" />
 </set>
diff --git a/java/res/drawable-hdpi/btn_center_default.9.png b/java/res/drawable-hdpi/btn_center_default.9.png
index d5ec36b..4f5f01c 100644
--- a/java/res/drawable-hdpi/btn_center_default.9.png
+++ b/java/res/drawable-hdpi/btn_center_default.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_center_pressed.9.png b/java/res/drawable-hdpi/btn_center_pressed.9.png
index 593a679..213b482 100644
--- a/java/res/drawable-hdpi/btn_center_pressed.9.png
+++ b/java/res/drawable-hdpi/btn_center_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_center_selected.9.png b/java/res/drawable-hdpi/btn_center_selected.9.png
index f1914a8..213b482 100644
--- a/java/res/drawable-hdpi/btn_center_selected.9.png
+++ b/java/res/drawable-hdpi/btn_center_selected.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_close_candidates_pane.9.png b/java/res/drawable-hdpi/btn_close_candidates_pane.9.png
new file mode 100644
index 0000000..bdd9495
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_close_candidates_pane.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_expand_candidates_pane.9.png b/java/res/drawable-hdpi/btn_expand_candidates_pane.9.png
new file mode 100644
index 0000000..63015ec
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_expand_candidates_pane.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png
index a8c1688..2dc1bed 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off_holo.9.png
index 8296476..575781f 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_holo.9.png
index 020a65d..6c5b42e 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png
index 88b27c0..8c999f6 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
index 87497bc..8c7c2f3 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
index f0d76df..02497b4 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png b/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png
index b6c234c..a524168 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png b/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png
index 73a8cd1..4395e97 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png
index 3115fa4..a13b200 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png
index dc08102..0b76855 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal.9.png
index 42c7c14..6ec7e65 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_normal.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_normal.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png
index 01e2506..995780c 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_off_stone.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_off_stone.9.png
index fad0ec4..1388b66 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_normal_off_stone.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_off_stone.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png
index 83c6eb3..7215782 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_on_stone.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_stone.9.png
index 215f815..5a94cb6 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_normal_on_stone.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_stone.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_stone.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_stone.9.png
index 88acdd7..c6373a8 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_normal_stone.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_stone.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_popup_background_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_popup_background_holo.9.png
new file mode 100644
index 0000000..0b27589
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_key_popup_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_popup_selected_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_popup_selected_holo.9.png
index 5ecdaf4..166de2a 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_popup_selected_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_popup_selected_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed.9.png
index e047eaf..0bd49a0 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_pressed.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png
index 218a2d2..634419f 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png
index afe4951..8474f9f 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/candidate_feedback_background.9.png b/java/res/drawable-hdpi/candidate_feedback_background.9.png
index 203c4e6..1649900 100644
--- a/java/res/drawable-hdpi/candidate_feedback_background.9.png
+++ b/java/res/drawable-hdpi/candidate_feedback_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/caution.png b/java/res/drawable-hdpi/caution.png
index 5cb6c54..61eb4dd 100644
--- a/java/res/drawable-hdpi/caution.png
+++ b/java/res/drawable-hdpi/caution.png
Binary files differ
diff --git a/java/res/drawable-hdpi/hint_popup.9.png b/java/res/drawable-hdpi/hint_popup.9.png
index b5ec003..5b2ad53 100644
--- a/java/res/drawable-hdpi/hint_popup.9.png
+++ b/java/res/drawable-hdpi/hint_popup.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/hint_popup_holo.9.png b/java/res/drawable-hdpi/hint_popup_holo.9.png
index 2ffc6ea..c44af88 100644
--- a/java/res/drawable-hdpi/hint_popup_holo.9.png
+++ b/java/res/drawable-hdpi/hint_popup_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_dialog_keyboard.png b/java/res/drawable-hdpi/ic_dialog_keyboard.png
index c772956..3054416 100644
--- a/java/res/drawable-hdpi/ic_dialog_keyboard.png
+++ b/java/res/drawable-hdpi/ic_dialog_keyboard.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_mic_dialog.png b/java/res/drawable-hdpi/ic_mic_dialog.png
index 349dc4b..6107f87 100644
--- a/java/res/drawable-hdpi/ic_mic_dialog.png
+++ b/java/res/drawable-hdpi/ic_mic_dialog.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_subtype_keyboard.png b/java/res/drawable-hdpi/ic_subtype_keyboard.png
index 7015e26..4843056 100644
--- a/java/res/drawable-hdpi/ic_subtype_keyboard.png
+++ b/java/res/drawable-hdpi/ic_subtype_keyboard.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_subtype_mic.png b/java/res/drawable-hdpi/ic_subtype_mic.png
index cb86a55..25c9ee2 100644
--- a/java/res/drawable-hdpi/ic_subtype_mic.png
+++ b/java/res/drawable-hdpi/ic_subtype_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_suggest_strip_microphone.png b/java/res/drawable-hdpi/ic_suggest_strip_microphone.png
index c00b4aa..189a861 100644
--- a/java/res/drawable-hdpi/ic_suggest_strip_microphone.png
+++ b/java/res/drawable-hdpi/ic_suggest_strip_microphone.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_suggest_strip_microphone_swipe.png b/java/res/drawable-hdpi/ic_suggest_strip_microphone_swipe.png
index 256dc3d..b4a6e37 100644
--- a/java/res/drawable-hdpi/ic_suggest_strip_microphone_swipe.png
+++ b/java/res/drawable-hdpi/ic_suggest_strip_microphone_swipe.png
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_at_holo.9.png b/java/res/drawable-hdpi/key_hint_at_holo.9.png
deleted file mode 100644
index b93c2d3..0000000
--- a/java/res/drawable-hdpi/key_hint_at_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_at_large_holo.9.png b/java/res/drawable-hdpi/key_hint_at_large_holo.9.png
deleted file mode 100644
index 2b92951..0000000
--- a/java/res/drawable-hdpi/key_hint_at_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_colon_holo.9.png b/java/res/drawable-hdpi/key_hint_colon_holo.9.png
deleted file mode 100644
index 8fa17e5..0000000
--- a/java/res/drawable-hdpi/key_hint_colon_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_colon_large_holo.9.png b/java/res/drawable-hdpi/key_hint_colon_large_holo.9.png
deleted file mode 100644
index 1271341..0000000
--- a/java/res/drawable-hdpi/key_hint_colon_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_comma_holo.9.png b/java/res/drawable-hdpi/key_hint_comma_holo.9.png
deleted file mode 100644
index 44220ec..0000000
--- a/java/res/drawable-hdpi/key_hint_comma_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-hdpi/key_hint_comma_large_holo.9.png
deleted file mode 100644
index 444b4d0..0000000
--- a/java/res/drawable-hdpi/key_hint_comma_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_exclamation_holo.9.png b/java/res/drawable-hdpi/key_hint_exclamation_holo.9.png
deleted file mode 100644
index d5e6884..0000000
--- a/java/res/drawable-hdpi/key_hint_exclamation_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_exclamation_large_holo.9.png b/java/res/drawable-hdpi/key_hint_exclamation_large_holo.9.png
deleted file mode 100644
index 5dab2f6..0000000
--- a/java/res/drawable-hdpi/key_hint_exclamation_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_num0.9.png b/java/res/drawable-hdpi/key_hint_num0.9.png
deleted file mode 100644
index 271264e..0000000
--- a/java/res/drawable-hdpi/key_hint_num0.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_num1.9.png b/java/res/drawable-hdpi/key_hint_num1.9.png
deleted file mode 100644
index eaf3742..0000000
--- a/java/res/drawable-hdpi/key_hint_num1.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_num2.9.png b/java/res/drawable-hdpi/key_hint_num2.9.png
deleted file mode 100644
index 8a16571..0000000
--- a/java/res/drawable-hdpi/key_hint_num2.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_num3.9.png b/java/res/drawable-hdpi/key_hint_num3.9.png
deleted file mode 100644
index 34b5011..0000000
--- a/java/res/drawable-hdpi/key_hint_num3.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_num4.9.png b/java/res/drawable-hdpi/key_hint_num4.9.png
deleted file mode 100644
index d4cc250..0000000
--- a/java/res/drawable-hdpi/key_hint_num4.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_num5.9.png b/java/res/drawable-hdpi/key_hint_num5.9.png
deleted file mode 100644
index 6a054b4..0000000
--- a/java/res/drawable-hdpi/key_hint_num5.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_num6.9.png b/java/res/drawable-hdpi/key_hint_num6.9.png
deleted file mode 100644
index 66e9140..0000000
--- a/java/res/drawable-hdpi/key_hint_num6.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_num7.9.png b/java/res/drawable-hdpi/key_hint_num7.9.png
deleted file mode 100644
index 5eae24f..0000000
--- a/java/res/drawable-hdpi/key_hint_num7.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_num8.9.png b/java/res/drawable-hdpi/key_hint_num8.9.png
deleted file mode 100644
index ea7f512..0000000
--- a/java/res/drawable-hdpi/key_hint_num8.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_num9.9.png b/java/res/drawable-hdpi/key_hint_num9.9.png
deleted file mode 100644
index 0bf85de..0000000
--- a/java/res/drawable-hdpi/key_hint_num9.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_plus_holo.9.png b/java/res/drawable-hdpi/key_hint_plus_holo.9.png
deleted file mode 100644
index a11b4ac..0000000
--- a/java/res/drawable-hdpi/key_hint_plus_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_plus_large_holo.9.png b/java/res/drawable-hdpi/key_hint_plus_large_holo.9.png
deleted file mode 100644
index 3c87c1f..0000000
--- a/java/res/drawable-hdpi/key_hint_plus_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_question_holo.9.png b/java/res/drawable-hdpi/key_hint_question_holo.9.png
deleted file mode 100644
index 670b4b9..0000000
--- a/java/res/drawable-hdpi/key_hint_question_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_question_large_holo.9.png b/java/res/drawable-hdpi/key_hint_question_large_holo.9.png
deleted file mode 100644
index 692f4a6..0000000
--- a/java/res/drawable-hdpi/key_hint_question_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_quote_holo.9.png b/java/res/drawable-hdpi/key_hint_quote_holo.9.png
deleted file mode 100644
index 9cc8615..0000000
--- a/java/res/drawable-hdpi/key_hint_quote_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_quote_large_holo.9.png b/java/res/drawable-hdpi/key_hint_quote_large_holo.9.png
deleted file mode 100644
index 664467b..0000000
--- a/java/res/drawable-hdpi/key_hint_quote_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_underline_holo.9.png b/java/res/drawable-hdpi/key_hint_underline_holo.9.png
deleted file mode 100644
index 2cb4077..0000000
--- a/java/res/drawable-hdpi/key_hint_underline_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/key_hint_underline_large_holo.9.png b/java/res/drawable-hdpi/key_hint_underline_large_holo.9.png
deleted file mode 100644
index 6b9a491..0000000
--- a/java/res/drawable-hdpi/key_hint_underline_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_background.9.png b/java/res/drawable-hdpi/keyboard_background.9.png
index edffac5..d57463f 100644
--- a/java/res/drawable-hdpi/keyboard_background.9.png
+++ b/java/res/drawable-hdpi/keyboard_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_background_holo.9.png b/java/res/drawable-hdpi/keyboard_background_holo.9.png
index 76fe2c8..39746ff 100644
--- a/java/res/drawable-hdpi/keyboard_background_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_dark_background.9.png b/java/res/drawable-hdpi/keyboard_dark_background.9.png
index f315cbd..fa3d449 100644
--- a/java/res/drawable-hdpi/keyboard_dark_background.9.png
+++ b/java/res/drawable-hdpi/keyboard_dark_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_background_holo.9.png
index 943f9e4..fd7a56e 100644
--- a/java/res/drawable-hdpi/keyboard_key_feedback_background_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_left_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_left_background_holo.9.png
new file mode 100644
index 0000000..60d9203
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_left_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_left_more_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_left_more_background_holo.9.png
new file mode 100644
index 0000000..f694b79
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_left_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_holo.9.png
index c21240f..3165d08 100644
--- a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_right_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_right_background_holo.9.png
new file mode 100644
index 0000000..3136073
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_right_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_right_more_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_right_more_background_holo.9.png
new file mode 100644
index 0000000..13d5af9
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_right_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png b/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png
index d6b2c79..baff809 100644
--- a/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png
+++ b/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_suggest_strip.9.png b/java/res/drawable-hdpi/keyboard_suggest_strip.9.png
index 0ccdb6a..7cab5a8 100644
--- a/java/res/drawable-hdpi/keyboard_suggest_strip.9.png
+++ b/java/res/drawable-hdpi/keyboard_suggest_strip.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_suggest_strip_divider.png b/java/res/drawable-hdpi/keyboard_suggest_strip_divider.png
index 7ca3e61..a62daf9 100644
--- a/java/res/drawable-hdpi/keyboard_suggest_strip_divider.png
+++ b/java/res/drawable-hdpi/keyboard_suggest_strip_divider.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png b/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png
index f55bcc9..f250034 100644
--- a/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/mic_base.png b/java/res/drawable-hdpi/mic_base.png
index 53e29ff..504a1aa 100644
--- a/java/res/drawable-hdpi/mic_base.png
+++ b/java/res/drawable-hdpi/mic_base.png
Binary files differ
diff --git a/java/res/drawable-hdpi/mic_full.png b/java/res/drawable-hdpi/mic_full.png
index e3e3dfa..3f4a676 100644
--- a/java/res/drawable-hdpi/mic_full.png
+++ b/java/res/drawable-hdpi/mic_full.png
Binary files differ
diff --git a/java/res/drawable-hdpi/mic_slash.png b/java/res/drawable-hdpi/mic_slash.png
index 1dd05c5..c3b1092 100644
--- a/java/res/drawable-hdpi/mic_slash.png
+++ b/java/res/drawable-hdpi/mic_slash.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png
index 3e4eff6..af6082d 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_delete.png b/java/res/drawable-hdpi/sym_bkeyboard_delete.png
index 1d24cc8..999a182 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_delete.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_delete.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_mic.png
index 512f460..5c73600 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_mic.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num0.png b/java/res/drawable-hdpi/sym_bkeyboard_num0.png
index 678a790..02f2811 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_num0.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num0.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num1.png b/java/res/drawable-hdpi/sym_bkeyboard_num1.png
index 4e68e35..304acfd 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_num1.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num1.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num2.png b/java/res/drawable-hdpi/sym_bkeyboard_num2.png
index 546663f..295eba1 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_num2.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num2.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num3.png b/java/res/drawable-hdpi/sym_bkeyboard_num3.png
index 57f9a8d..9ee6e44 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_num3.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num3.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num4.png b/java/res/drawable-hdpi/sym_bkeyboard_num4.png
index de50438..ab4392d 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_num4.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num4.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num5.png b/java/res/drawable-hdpi/sym_bkeyboard_num5.png
index 1d2e1ef..004ecd7 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_num5.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num5.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num6.png b/java/res/drawable-hdpi/sym_bkeyboard_num6.png
index 39788b7..2aa317d 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_num6.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num6.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num7.png b/java/res/drawable-hdpi/sym_bkeyboard_num7.png
index fff6f27..721361c 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_num7.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num7.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num8.png b/java/res/drawable-hdpi/sym_bkeyboard_num8.png
index 8cc1a95..941359f 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_num8.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num8.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num9.png b/java/res/drawable-hdpi/sym_bkeyboard_num9.png
index 0217425..74e08a0 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_num9.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_numalt.png b/java/res/drawable-hdpi/sym_bkeyboard_numalt.png
deleted file mode 100644
index 200714f..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_numalt.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_numpound.png b/java/res/drawable-hdpi/sym_bkeyboard_numpound.png
deleted file mode 100644
index 0a46122..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_numpound.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_numstar.png b/java/res/drawable-hdpi/sym_bkeyboard_numstar.png
deleted file mode 100644
index ca22bd5..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_numstar.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_return.png b/java/res/drawable-hdpi/sym_bkeyboard_return.png
index 426e159..91bb397 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_return.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_return.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_search.png b/java/res/drawable-hdpi/sym_bkeyboard_search.png
index 1b6f884..0ac0814 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_search.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_search.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_settings.png b/java/res/drawable-hdpi/sym_bkeyboard_settings.png
index 08ba18f..5b6217a 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_settings.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_settings.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_shift.png b/java/res/drawable-hdpi/sym_bkeyboard_shift.png
index 5a22dd3..5ef01b8 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_shift.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_shift.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png b/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png
index 5664491..7d36dcb 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_space.png b/java/res/drawable-hdpi/sym_bkeyboard_space.png
index cd0ebe2..77518cc 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_space.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_space.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_tab.png b/java/res/drawable-hdpi/sym_bkeyboard_tab.png
index 3466e12..5db4cef 100644
--- a/java/res/drawable-hdpi/sym_bkeyboard_tab.png
+++ b/java/res/drawable-hdpi/sym_bkeyboard_tab.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_123_mic.png b/java/res/drawable-hdpi/sym_keyboard_123_mic.png
index 6266980..6f0aec2 100644
--- a/java/res/drawable-hdpi/sym_keyboard_123_mic.png
+++ b/java/res/drawable-hdpi/sym_keyboard_123_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_delete.png b/java/res/drawable-hdpi/sym_keyboard_delete.png
index 459ebcf..0591b82 100644
--- a/java/res/drawable-hdpi/sym_keyboard_delete.png
+++ b/java/res/drawable-hdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_delete_holo.png b/java/res/drawable-hdpi/sym_keyboard_delete_holo.png
index ff2a4ac..9d87ba6 100644
--- a/java/res/drawable-hdpi/sym_keyboard_delete_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_delete_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_123_mic.png b/java/res/drawable-hdpi/sym_keyboard_feedback_123_mic.png
index eef7896..e56799c 100644
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_123_mic.png
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_123_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_delete.png b/java/res/drawable-hdpi/sym_keyboard_feedback_delete.png
index 8322e8e..960bb4e 100644
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_delete.png
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_delete.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_left.png b/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_left.png
index 889477c..5d1cb78 100644
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_left.png
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_left.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_right.png b/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_right.png
index b0f6d7f..3a56d47 100644
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_right.png
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_right.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_mic.png b/java/res/drawable-hdpi/sym_keyboard_feedback_mic.png
index f82c33a..a88a19d 100644
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_mic.png
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_numalt.png b/java/res/drawable-hdpi/sym_keyboard_feedback_numalt.png
index 819236c..7d418d9 100644
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_numalt.png
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_numalt.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_return.png b/java/res/drawable-hdpi/sym_keyboard_feedback_return.png
index f038d3a..f73ff3c 100644
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_return.png
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_return.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_search.png b/java/res/drawable-hdpi/sym_keyboard_feedback_search.png
index 337f9e4..0e69157 100644
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_search.png
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_search.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_settings.png b/java/res/drawable-hdpi/sym_keyboard_feedback_settings.png
index 8a02be0..6304b00 100644
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_settings.png
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_settings.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_shift.png b/java/res/drawable-hdpi/sym_keyboard_feedback_shift.png
index abf15f8..fc858f2 100644
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_shift.png
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_shift.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_space.png b/java/res/drawable-hdpi/sym_keyboard_feedback_space.png
index 70debca..67311fc 100644
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_space.png
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_space.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_tab.png b/java/res/drawable-hdpi/sym_keyboard_feedback_tab.png
index d2efb16..d75fcac 100644
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_tab.png
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_tab.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_language_arrows_left.png b/java/res/drawable-hdpi/sym_keyboard_language_arrows_left.png
index dcc4bd5..79f235f 100644
--- a/java/res/drawable-hdpi/sym_keyboard_language_arrows_left.png
+++ b/java/res/drawable-hdpi/sym_keyboard_language_arrows_left.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_language_arrows_right.png b/java/res/drawable-hdpi/sym_keyboard_language_arrows_right.png
index ecf61a9..e1444e8 100644
--- a/java/res/drawable-hdpi/sym_keyboard_language_arrows_right.png
+++ b/java/res/drawable-hdpi/sym_keyboard_language_arrows_right.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_mic.png b/java/res/drawable-hdpi/sym_keyboard_mic.png
index c8dca62..520a40f 100644
--- a/java/res/drawable-hdpi/sym_keyboard_mic.png
+++ b/java/res/drawable-hdpi/sym_keyboard_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num0.png b/java/res/drawable-hdpi/sym_keyboard_num0.png
index 10ac70b..2024f41 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num0.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num0.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num0_holo.png b/java/res/drawable-hdpi/sym_keyboard_num0_holo.png
index ec8b5a8..fbd55d6 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num0_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num0_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num1.png b/java/res/drawable-hdpi/sym_keyboard_num1.png
index 0fc03ef..5e66b9a 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num1.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num1.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num1_holo.png b/java/res/drawable-hdpi/sym_keyboard_num1_holo.png
index 60c8ab8..e651c3e 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num1_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num1_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num2.png b/java/res/drawable-hdpi/sym_keyboard_num2.png
index 283560b..3933270 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num2.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num2.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num2_holo.png b/java/res/drawable-hdpi/sym_keyboard_num2_holo.png
index 578e37d..8458f53 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num2_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num2_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num3.png b/java/res/drawable-hdpi/sym_keyboard_num3.png
index 9a3b329..c695483 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num3.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num3.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num3_holo.png b/java/res/drawable-hdpi/sym_keyboard_num3_holo.png
index fb62506..81025c6 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num3_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num3_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num4.png b/java/res/drawable-hdpi/sym_keyboard_num4.png
index f13ff1a..ef20add 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num4.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num4.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num4_holo.png b/java/res/drawable-hdpi/sym_keyboard_num4_holo.png
index c0e54a5..30a0c77 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num4_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num4_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num5.png b/java/res/drawable-hdpi/sym_keyboard_num5.png
index c251329..9a2f51f 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num5.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num5.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num5_holo.png b/java/res/drawable-hdpi/sym_keyboard_num5_holo.png
index b581a46..147f056 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num5_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num5_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num6.png b/java/res/drawable-hdpi/sym_keyboard_num6.png
index 4acba4c..6829f81 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num6.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num6.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num6_holo.png b/java/res/drawable-hdpi/sym_keyboard_num6_holo.png
index 0791802..b968308 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num6_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num6_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num7.png b/java/res/drawable-hdpi/sym_keyboard_num7.png
index 14931c1..dc608d4 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num7.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num7.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num7_holo.png b/java/res/drawable-hdpi/sym_keyboard_num7_holo.png
index 7b3d3a8..7d44bfb 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num7_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num7_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num8.png b/java/res/drawable-hdpi/sym_keyboard_num8.png
index d4973fd..f0978c6 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num8.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num8.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num8_holo.png b/java/res/drawable-hdpi/sym_keyboard_num8_holo.png
index e076aed..093cd74 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num8_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num8_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num9.png b/java/res/drawable-hdpi/sym_keyboard_num9.png
index 49cec66..4b8328d 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num9.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num9_holo.png b/java/res/drawable-hdpi/sym_keyboard_num9_holo.png
index 4189cda..f35e80d 100644
--- a/java/res/drawable-hdpi/sym_keyboard_num9_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_num9_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_numalt.png b/java/res/drawable-hdpi/sym_keyboard_numalt.png
deleted file mode 100644
index 3cc5311..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_numalt.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_numbpound_holo.png b/java/res/drawable-hdpi/sym_keyboard_numbpound_holo.png
deleted file mode 100644
index 73f8be0..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_numbpound_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_numbstar_holo.png b/java/res/drawable-hdpi/sym_keyboard_numbstar_holo.png
deleted file mode 100644
index fcb891b..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_numbstar_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_numpound.png b/java/res/drawable-hdpi/sym_keyboard_numpound.png
deleted file mode 100644
index d091339..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_numpound.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_numstar.png b/java/res/drawable-hdpi/sym_keyboard_numstar.png
deleted file mode 100644
index e838e16..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_numstar.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_return.png b/java/res/drawable-hdpi/sym_keyboard_return.png
index 9d97e1e..9743c7f 100644
--- a/java/res/drawable-hdpi/sym_keyboard_return.png
+++ b/java/res/drawable-hdpi/sym_keyboard_return.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_return_holo.png b/java/res/drawable-hdpi/sym_keyboard_return_holo.png
index ca3c02d..ef04d67 100644
--- a/java/res/drawable-hdpi/sym_keyboard_return_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_search.png b/java/res/drawable-hdpi/sym_keyboard_search.png
index 1aa22d7..8cd28c6 100644
--- a/java/res/drawable-hdpi/sym_keyboard_search.png
+++ b/java/res/drawable-hdpi/sym_keyboard_search.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_settings.png b/java/res/drawable-hdpi/sym_keyboard_settings.png
index 35d1ed6..1e5bf93 100644
--- a/java/res/drawable-hdpi/sym_keyboard_settings.png
+++ b/java/res/drawable-hdpi/sym_keyboard_settings.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_settings_holo.png b/java/res/drawable-hdpi/sym_keyboard_settings_holo.png
index 471bd0b..29085e5 100644
--- a/java/res/drawable-hdpi/sym_keyboard_settings_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_settings_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_shift.png b/java/res/drawable-hdpi/sym_keyboard_shift.png
index bf217d1..8e3d032 100644
--- a/java/res/drawable-hdpi/sym_keyboard_shift.png
+++ b/java/res/drawable-hdpi/sym_keyboard_shift.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_holo.png b/java/res/drawable-hdpi/sym_keyboard_shift_holo.png
index 375c1b4..1e074a7 100644
--- a/java/res/drawable-hdpi/sym_keyboard_shift_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_shift_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_locked.png b/java/res/drawable-hdpi/sym_keyboard_shift_locked.png
index d11b397..d345634 100644
--- a/java/res/drawable-hdpi/sym_keyboard_shift_locked.png
+++ b/java/res/drawable-hdpi/sym_keyboard_shift_locked.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo.png b/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo.png
index 57362ea..ab77b95 100644
--- a/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_space.png b/java/res/drawable-hdpi/sym_keyboard_space.png
index fcd20de..780733e 100644
--- a/java/res/drawable-hdpi/sym_keyboard_space.png
+++ b/java/res/drawable-hdpi/sym_keyboard_space.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_space_holo.png b/java/res/drawable-hdpi/sym_keyboard_space_holo.png
index a8e5f7d..143a899 100644
--- a/java/res/drawable-hdpi/sym_keyboard_space_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_space_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_space_led.9.png b/java/res/drawable-hdpi/sym_keyboard_space_led.9.png
index 2c6f4a9..c76f64b 100644
--- a/java/res/drawable-hdpi/sym_keyboard_space_led.9.png
+++ b/java/res/drawable-hdpi/sym_keyboard_space_led.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_tab_holo.png b/java/res/drawable-hdpi/sym_keyboard_tab_holo.png
new file mode 100644
index 0000000..321c541
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_tab_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_holo.png b/java/res/drawable-hdpi/sym_keyboard_voice_holo.png
index 5ea2edc..d3cb46e 100644
--- a/java/res/drawable-hdpi/sym_keyboard_voice_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_voice_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_off_holo.png b/java/res/drawable-hdpi/sym_keyboard_voice_off_holo.png
index 6e6279a..45e3e79 100644
--- a/java/res/drawable-hdpi/sym_keyboard_voice_off_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_voice_off_holo.png
Binary files differ
diff --git a/java/res/drawable-hdpi/vs_dialog_blue.9.png b/java/res/drawable-hdpi/vs_dialog_blue.9.png
index cf27e8f..4f813ea 100644
--- a/java/res/drawable-hdpi/vs_dialog_blue.9.png
+++ b/java/res/drawable-hdpi/vs_dialog_blue.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/vs_dialog_red.9.png b/java/res/drawable-hdpi/vs_dialog_red.9.png
index 6c08d5a..a205560 100644
--- a/java/res/drawable-hdpi/vs_dialog_red.9.png
+++ b/java/res/drawable-hdpi/vs_dialog_red.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/vs_dialog_yellow.9.png b/java/res/drawable-hdpi/vs_dialog_yellow.9.png
index 2fb06c2..ce664b6 100644
--- a/java/res/drawable-hdpi/vs_dialog_yellow.9.png
+++ b/java/res/drawable-hdpi/vs_dialog_yellow.9.png
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_at_holo.9.png b/java/res/drawable-land-hdpi/key_hint_at_holo.9.png
deleted file mode 100644
index b93c2d3..0000000
--- a/java/res/drawable-land-hdpi/key_hint_at_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_at_large_holo.9.png b/java/res/drawable-land-hdpi/key_hint_at_large_holo.9.png
deleted file mode 100644
index 2b92951..0000000
--- a/java/res/drawable-land-hdpi/key_hint_at_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_colon_holo.9.png b/java/res/drawable-land-hdpi/key_hint_colon_holo.9.png
deleted file mode 100644
index 8fa17e5..0000000
--- a/java/res/drawable-land-hdpi/key_hint_colon_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_colon_large_holo.9.png b/java/res/drawable-land-hdpi/key_hint_colon_large_holo.9.png
deleted file mode 100644
index 1271341..0000000
--- a/java/res/drawable-land-hdpi/key_hint_colon_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_comma_holo.9.png b/java/res/drawable-land-hdpi/key_hint_comma_holo.9.png
deleted file mode 100644
index 44220ec..0000000
--- a/java/res/drawable-land-hdpi/key_hint_comma_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-land-hdpi/key_hint_comma_large_holo.9.png
deleted file mode 100644
index 444b4d0..0000000
--- a/java/res/drawable-land-hdpi/key_hint_comma_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_exclamation_holo.9.png b/java/res/drawable-land-hdpi/key_hint_exclamation_holo.9.png
deleted file mode 100644
index d5e6884..0000000
--- a/java/res/drawable-land-hdpi/key_hint_exclamation_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_exclamation_large_holo.9.png b/java/res/drawable-land-hdpi/key_hint_exclamation_large_holo.9.png
deleted file mode 100644
index 5dab2f6..0000000
--- a/java/res/drawable-land-hdpi/key_hint_exclamation_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_plus_holo.9.png b/java/res/drawable-land-hdpi/key_hint_plus_holo.9.png
deleted file mode 100644
index a11b4ac..0000000
--- a/java/res/drawable-land-hdpi/key_hint_plus_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_plus_large_holo.9.png b/java/res/drawable-land-hdpi/key_hint_plus_large_holo.9.png
deleted file mode 100644
index 3c87c1f..0000000
--- a/java/res/drawable-land-hdpi/key_hint_plus_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_question_holo.9.png b/java/res/drawable-land-hdpi/key_hint_question_holo.9.png
deleted file mode 100644
index 670b4b9..0000000
--- a/java/res/drawable-land-hdpi/key_hint_question_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_question_large_holo.9.png b/java/res/drawable-land-hdpi/key_hint_question_large_holo.9.png
deleted file mode 100644
index 692f4a6..0000000
--- a/java/res/drawable-land-hdpi/key_hint_question_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_quote_holo.9.png b/java/res/drawable-land-hdpi/key_hint_quote_holo.9.png
deleted file mode 100644
index 9cc8615..0000000
--- a/java/res/drawable-land-hdpi/key_hint_quote_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_quote_large_holo.9.png b/java/res/drawable-land-hdpi/key_hint_quote_large_holo.9.png
deleted file mode 100644
index 664467b..0000000
--- a/java/res/drawable-land-hdpi/key_hint_quote_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_underline_holo.9.png b/java/res/drawable-land-hdpi/key_hint_underline_holo.9.png
deleted file mode 100644
index 2cb4077..0000000
--- a/java/res/drawable-land-hdpi/key_hint_underline_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-hdpi/key_hint_underline_large_holo.9.png b/java/res/drawable-land-hdpi/key_hint_underline_large_holo.9.png
deleted file mode 100644
index 6b9a491..0000000
--- a/java/res/drawable-land-hdpi/key_hint_underline_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_at_holo.9.png b/java/res/drawable-land-mdpi/key_hint_at_holo.9.png
deleted file mode 100644
index 8c84654..0000000
--- a/java/res/drawable-land-mdpi/key_hint_at_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_at_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_at_large_holo.9.png
deleted file mode 100644
index b9e095b..0000000
--- a/java/res/drawable-land-mdpi/key_hint_at_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_colon_holo.9.png b/java/res/drawable-land-mdpi/key_hint_colon_holo.9.png
deleted file mode 100644
index 3cd0e3c..0000000
--- a/java/res/drawable-land-mdpi/key_hint_colon_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_colon_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_colon_large_holo.9.png
deleted file mode 100644
index 01b47b2..0000000
--- a/java/res/drawable-land-mdpi/key_hint_colon_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_comma_holo.9.png b/java/res/drawable-land-mdpi/key_hint_comma_holo.9.png
deleted file mode 100644
index b70f71b..0000000
--- a/java/res/drawable-land-mdpi/key_hint_comma_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_comma_large_holo.9.png
deleted file mode 100644
index 1ae82d4..0000000
--- a/java/res/drawable-land-mdpi/key_hint_comma_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_exclamation_holo.9.png b/java/res/drawable-land-mdpi/key_hint_exclamation_holo.9.png
deleted file mode 100644
index 3409535..0000000
--- a/java/res/drawable-land-mdpi/key_hint_exclamation_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_exclamation_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_exclamation_large_holo.9.png
deleted file mode 100644
index 8dd2f27..0000000
--- a/java/res/drawable-land-mdpi/key_hint_exclamation_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_plus_holo.9.png b/java/res/drawable-land-mdpi/key_hint_plus_holo.9.png
deleted file mode 100644
index 51e86ba..0000000
--- a/java/res/drawable-land-mdpi/key_hint_plus_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_plus_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_plus_large_holo.9.png
deleted file mode 100644
index bf30f92..0000000
--- a/java/res/drawable-land-mdpi/key_hint_plus_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_question_holo.9.png b/java/res/drawable-land-mdpi/key_hint_question_holo.9.png
deleted file mode 100644
index 38b5d61..0000000
--- a/java/res/drawable-land-mdpi/key_hint_question_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_question_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_question_large_holo.9.png
deleted file mode 100644
index 448a027..0000000
--- a/java/res/drawable-land-mdpi/key_hint_question_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_quote_holo.9.png b/java/res/drawable-land-mdpi/key_hint_quote_holo.9.png
deleted file mode 100644
index 92fe8ea..0000000
--- a/java/res/drawable-land-mdpi/key_hint_quote_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_quote_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_quote_large_holo.9.png
deleted file mode 100644
index a777d01..0000000
--- a/java/res/drawable-land-mdpi/key_hint_quote_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_underline_holo.9.png b/java/res/drawable-land-mdpi/key_hint_underline_holo.9.png
deleted file mode 100644
index 7a452c5..0000000
--- a/java/res/drawable-land-mdpi/key_hint_underline_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/key_hint_underline_large_holo.9.png b/java/res/drawable-land-mdpi/key_hint_underline_large_holo.9.png
deleted file mode 100644
index f738b02..0000000
--- a/java/res/drawable-land-mdpi/key_hint_underline_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_at_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_at_holo.9.png
deleted file mode 100644
index 1d257cb..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_at_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_at_large_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_at_large_holo.9.png
deleted file mode 100644
index 53de283..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_at_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_colon_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_colon_holo.9.png
deleted file mode 100644
index 73394b5..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_colon_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_colon_large_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_colon_large_holo.9.png
deleted file mode 100644
index a4a6acb..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_colon_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_comma_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_comma_holo.9.png
deleted file mode 100644
index d4dbdf4..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_comma_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_comma_large_holo.9.png
deleted file mode 100644
index aea5c8e..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_comma_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_exclamation_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_exclamation_holo.9.png
deleted file mode 100644
index 078d1d8..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_exclamation_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_exclamation_large_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_exclamation_large_holo.9.png
deleted file mode 100644
index ddd6b13..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_exclamation_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_plus_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_plus_holo.9.png
deleted file mode 100644
index d0ce9c9..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_plus_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_plus_large_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_plus_large_holo.9.png
deleted file mode 100644
index 9cf20e8..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_plus_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_question_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_question_holo.9.png
deleted file mode 100644
index 1e886d8..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_question_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_question_large_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_question_large_holo.9.png
deleted file mode 100644
index 8262287..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_question_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_quote_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_quote_holo.9.png
deleted file mode 100644
index fd8bbad..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_quote_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_quote_large_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_quote_large_holo.9.png
deleted file mode 100644
index 51d5b49..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_quote_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_underline_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_underline_holo.9.png
deleted file mode 100644
index 37d2fa5..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_underline_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-xhdpi/key_hint_underline_large_holo.9.png b/java/res/drawable-land-xhdpi/key_hint_underline_large_holo.9.png
deleted file mode 100644
index 4249a8e..0000000
--- a/java/res/drawable-land-xhdpi/key_hint_underline_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_close_candidates_pane.9.png b/java/res/drawable-mdpi/btn_close_candidates_pane.9.png
new file mode 100644
index 0000000..5ea5692
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_close_candidates_pane.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_expand_candidates_pane.9.png b/java/res/drawable-mdpi/btn_expand_candidates_pane.9.png
new file mode 100644
index 0000000..83cb653
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_expand_candidates_pane.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png
index 1534d99..034d638 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off_holo.9.png
index 936513a..0cb53d5 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_holo.9.png
index b071251..0050a01 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png
index 9fed21e..27f8fda 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
index 3fce559..b8ce952 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
index 3f82b67..4d4d1a7 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png
index eded173..90f471f 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png
index e6a1dce..c850768 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_popup_background_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_popup_background_holo.9.png
new file mode 100644
index 0000000..e9186b7
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_key_popup_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_popup_selected_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_popup_selected_holo.9.png
index d2f5f3b..e96c8c5 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_popup_selected_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_popup_selected_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/hint_popup_holo.9.png b/java/res/drawable-mdpi/hint_popup_holo.9.png
index 01d1139..e9e131c 100644
--- a/java/res/drawable-mdpi/hint_popup_holo.9.png
+++ b/java/res/drawable-mdpi/hint_popup_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_at_holo.9.png b/java/res/drawable-mdpi/key_hint_at_holo.9.png
deleted file mode 100644
index 8c84654..0000000
--- a/java/res/drawable-mdpi/key_hint_at_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_at_large_holo.9.png b/java/res/drawable-mdpi/key_hint_at_large_holo.9.png
deleted file mode 100644
index b9e095b..0000000
--- a/java/res/drawable-mdpi/key_hint_at_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_colon_holo.9.png b/java/res/drawable-mdpi/key_hint_colon_holo.9.png
deleted file mode 100644
index 3cd0e3c..0000000
--- a/java/res/drawable-mdpi/key_hint_colon_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_colon_large_holo.9.png b/java/res/drawable-mdpi/key_hint_colon_large_holo.9.png
deleted file mode 100644
index 01b47b2..0000000
--- a/java/res/drawable-mdpi/key_hint_colon_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_comma_holo.9.png b/java/res/drawable-mdpi/key_hint_comma_holo.9.png
deleted file mode 100644
index b70f71b..0000000
--- a/java/res/drawable-mdpi/key_hint_comma_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-mdpi/key_hint_comma_large_holo.9.png
deleted file mode 100644
index 1ae82d4..0000000
--- a/java/res/drawable-mdpi/key_hint_comma_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_exclamation_holo.9.png b/java/res/drawable-mdpi/key_hint_exclamation_holo.9.png
deleted file mode 100644
index 3409535..0000000
--- a/java/res/drawable-mdpi/key_hint_exclamation_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_exclamation_large_holo.9.png b/java/res/drawable-mdpi/key_hint_exclamation_large_holo.9.png
deleted file mode 100644
index 8dd2f27..0000000
--- a/java/res/drawable-mdpi/key_hint_exclamation_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num0.9.png b/java/res/drawable-mdpi/key_hint_num0.9.png
deleted file mode 100644
index 61ad1b5..0000000
--- a/java/res/drawable-mdpi/key_hint_num0.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num0_holo.9.png b/java/res/drawable-mdpi/key_hint_num0_holo.9.png
deleted file mode 100644
index 271264e..0000000
--- a/java/res/drawable-mdpi/key_hint_num0_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num1.9.png b/java/res/drawable-mdpi/key_hint_num1.9.png
deleted file mode 100644
index cd7772e..0000000
--- a/java/res/drawable-mdpi/key_hint_num1.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num1_holo.9.png b/java/res/drawable-mdpi/key_hint_num1_holo.9.png
deleted file mode 100644
index eaf3742..0000000
--- a/java/res/drawable-mdpi/key_hint_num1_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num2.9.png b/java/res/drawable-mdpi/key_hint_num2.9.png
deleted file mode 100644
index fa5f8b7..0000000
--- a/java/res/drawable-mdpi/key_hint_num2.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num2_holo.9.png b/java/res/drawable-mdpi/key_hint_num2_holo.9.png
deleted file mode 100644
index 8a16571..0000000
--- a/java/res/drawable-mdpi/key_hint_num2_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num3.9.png b/java/res/drawable-mdpi/key_hint_num3.9.png
deleted file mode 100644
index 0c7336c..0000000
--- a/java/res/drawable-mdpi/key_hint_num3.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num3_holo.9.png b/java/res/drawable-mdpi/key_hint_num3_holo.9.png
deleted file mode 100644
index 34b5011..0000000
--- a/java/res/drawable-mdpi/key_hint_num3_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num4.9.png b/java/res/drawable-mdpi/key_hint_num4.9.png
deleted file mode 100644
index 73ef06c..0000000
--- a/java/res/drawable-mdpi/key_hint_num4.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num4_holo.9.png b/java/res/drawable-mdpi/key_hint_num4_holo.9.png
deleted file mode 100644
index d4cc250..0000000
--- a/java/res/drawable-mdpi/key_hint_num4_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num5.9.png b/java/res/drawable-mdpi/key_hint_num5.9.png
deleted file mode 100644
index aea460e..0000000
--- a/java/res/drawable-mdpi/key_hint_num5.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num5_holo.9.png b/java/res/drawable-mdpi/key_hint_num5_holo.9.png
deleted file mode 100644
index 6a054b4..0000000
--- a/java/res/drawable-mdpi/key_hint_num5_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num6.9.png b/java/res/drawable-mdpi/key_hint_num6.9.png
deleted file mode 100644
index 16a9237..0000000
--- a/java/res/drawable-mdpi/key_hint_num6.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num6_holo.9.png b/java/res/drawable-mdpi/key_hint_num6_holo.9.png
deleted file mode 100644
index 66e9140..0000000
--- a/java/res/drawable-mdpi/key_hint_num6_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num7.9.png b/java/res/drawable-mdpi/key_hint_num7.9.png
deleted file mode 100644
index 6747a19..0000000
--- a/java/res/drawable-mdpi/key_hint_num7.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num7_holo.9.png b/java/res/drawable-mdpi/key_hint_num7_holo.9.png
deleted file mode 100644
index 5eae24f..0000000
--- a/java/res/drawable-mdpi/key_hint_num7_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num8.9.png b/java/res/drawable-mdpi/key_hint_num8.9.png
deleted file mode 100644
index 28be2fb..0000000
--- a/java/res/drawable-mdpi/key_hint_num8.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num8_holo.9.png b/java/res/drawable-mdpi/key_hint_num8_holo.9.png
deleted file mode 100644
index ea7f512..0000000
--- a/java/res/drawable-mdpi/key_hint_num8_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num9.9.png b/java/res/drawable-mdpi/key_hint_num9.9.png
deleted file mode 100644
index 731d63b..0000000
--- a/java/res/drawable-mdpi/key_hint_num9.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_num9_holo.9.png b/java/res/drawable-mdpi/key_hint_num9_holo.9.png
deleted file mode 100644
index 0bf85de..0000000
--- a/java/res/drawable-mdpi/key_hint_num9_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_plus_holo.9.png b/java/res/drawable-mdpi/key_hint_plus_holo.9.png
deleted file mode 100644
index 51e86ba..0000000
--- a/java/res/drawable-mdpi/key_hint_plus_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_plus_large_holo.9.png b/java/res/drawable-mdpi/key_hint_plus_large_holo.9.png
deleted file mode 100644
index bf30f92..0000000
--- a/java/res/drawable-mdpi/key_hint_plus_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_question_holo.9.png b/java/res/drawable-mdpi/key_hint_question_holo.9.png
deleted file mode 100644
index 38b5d61..0000000
--- a/java/res/drawable-mdpi/key_hint_question_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_question_large_holo.9.png b/java/res/drawable-mdpi/key_hint_question_large_holo.9.png
deleted file mode 100644
index 448a027..0000000
--- a/java/res/drawable-mdpi/key_hint_question_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_quote_holo.9.png b/java/res/drawable-mdpi/key_hint_quote_holo.9.png
deleted file mode 100644
index 92fe8ea..0000000
--- a/java/res/drawable-mdpi/key_hint_quote_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_quote_large_holo.9.png b/java/res/drawable-mdpi/key_hint_quote_large_holo.9.png
deleted file mode 100644
index a777d01..0000000
--- a/java/res/drawable-mdpi/key_hint_quote_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_underline_holo.9.png b/java/res/drawable-mdpi/key_hint_underline_holo.9.png
deleted file mode 100644
index 7a452c5..0000000
--- a/java/res/drawable-mdpi/key_hint_underline_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/key_hint_underline_large_holo.9.png b/java/res/drawable-mdpi/key_hint_underline_large_holo.9.png
deleted file mode 100644
index f738b02..0000000
--- a/java/res/drawable-mdpi/key_hint_underline_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_background_holo.9.png b/java/res/drawable-mdpi/keyboard_background_holo.9.png
index a93966c..f1d55c6 100644
--- a/java/res/drawable-mdpi/keyboard_background_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_background_holo.9.png
index 3d6a76f..9a1b7aa 100644
--- a/java/res/drawable-mdpi/keyboard_key_feedback_background_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_left_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_left_background_holo.9.png
new file mode 100644
index 0000000..cb222b0
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_left_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_left_more_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_left_more_background_holo.9.png
new file mode 100644
index 0000000..07d9800
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_left_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_holo.9.png
index e3f5be8..05a332b 100644
--- a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_right_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_right_background_holo.9.png
new file mode 100644
index 0000000..fd16755
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_right_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_right_more_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_right_more_background_holo.9.png
new file mode 100644
index 0000000..1acbd82
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_right_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png b/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png
index 1f87a68..17faf86 100644
--- a/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_numalt.png b/java/res/drawable-mdpi/sym_bkeyboard_numalt.png
deleted file mode 100644
index 4fa410b..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_numalt.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_numpound.png b/java/res/drawable-mdpi/sym_bkeyboard_numpound.png
deleted file mode 100644
index 9126eed..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_numpound.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_numstar.png b/java/res/drawable-mdpi/sym_bkeyboard_numstar.png
deleted file mode 100644
index 9b9f1b9..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_numstar.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_delete_holo.png b/java/res/drawable-mdpi/sym_keyboard_delete_holo.png
index 1555791..f8f6e6a 100644
--- a/java/res/drawable-mdpi/sym_keyboard_delete_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_delete_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num0_holo.png b/java/res/drawable-mdpi/sym_keyboard_num0_holo.png
index e1d395b..2c57adf 100644
--- a/java/res/drawable-mdpi/sym_keyboard_num0_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_num0_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num1_holo.png b/java/res/drawable-mdpi/sym_keyboard_num1_holo.png
index 225436a..509e28f 100644
--- a/java/res/drawable-mdpi/sym_keyboard_num1_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_num1_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num2_holo.png b/java/res/drawable-mdpi/sym_keyboard_num2_holo.png
index e513fa4..0da18e7 100644
--- a/java/res/drawable-mdpi/sym_keyboard_num2_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_num2_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num3_holo.png b/java/res/drawable-mdpi/sym_keyboard_num3_holo.png
index b91e005..e847058 100644
--- a/java/res/drawable-mdpi/sym_keyboard_num3_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_num3_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num4_holo.png b/java/res/drawable-mdpi/sym_keyboard_num4_holo.png
index 65f8824..13c216d 100644
--- a/java/res/drawable-mdpi/sym_keyboard_num4_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_num4_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num5_holo.png b/java/res/drawable-mdpi/sym_keyboard_num5_holo.png
index b89ef07..0065153 100644
--- a/java/res/drawable-mdpi/sym_keyboard_num5_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_num5_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num6_holo.png b/java/res/drawable-mdpi/sym_keyboard_num6_holo.png
index 931275a..09fab76 100644
--- a/java/res/drawable-mdpi/sym_keyboard_num6_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_num6_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num7_holo.png b/java/res/drawable-mdpi/sym_keyboard_num7_holo.png
index 9396c4c..d350fdd 100644
--- a/java/res/drawable-mdpi/sym_keyboard_num7_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_num7_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num8_holo.png b/java/res/drawable-mdpi/sym_keyboard_num8_holo.png
index 12e3eef..c40cb89 100644
--- a/java/res/drawable-mdpi/sym_keyboard_num8_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_num8_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num9_holo.png b/java/res/drawable-mdpi/sym_keyboard_num9_holo.png
index 6911c2b..7352693 100644
--- a/java/res/drawable-mdpi/sym_keyboard_num9_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_num9_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_numalt.png b/java/res/drawable-mdpi/sym_keyboard_numalt.png
deleted file mode 100644
index 32a2cf3..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_numalt.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_numbpound_holo.png b/java/res/drawable-mdpi/sym_keyboard_numbpound_holo.png
deleted file mode 100644
index e3a8b49..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_numbpound_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_numbstar_holo.png b/java/res/drawable-mdpi/sym_keyboard_numbstar_holo.png
deleted file mode 100644
index e80e934..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_numbstar_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_numpound.png b/java/res/drawable-mdpi/sym_keyboard_numpound.png
deleted file mode 100644
index b2419d9..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_numpound.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_numstar.png b/java/res/drawable-mdpi/sym_keyboard_numstar.png
deleted file mode 100644
index cb66f96..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_numstar.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_return_holo.png b/java/res/drawable-mdpi/sym_keyboard_return_holo.png
index d519cce..abbce52 100644
--- a/java/res/drawable-mdpi/sym_keyboard_return_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_settings_holo.png b/java/res/drawable-mdpi/sym_keyboard_settings_holo.png
index 784a450..82df903 100644
--- a/java/res/drawable-mdpi/sym_keyboard_settings_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_settings_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_holo.png b/java/res/drawable-mdpi/sym_keyboard_shift_holo.png
index 91d6e32..49b9fc0 100644
--- a/java/res/drawable-mdpi/sym_keyboard_shift_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_shift_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo.png b/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo.png
index 2bd0536..5375beb 100644
--- a/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_space_holo.png b/java/res/drawable-mdpi/sym_keyboard_space_holo.png
index 25e655d..e413ddf 100644
--- a/java/res/drawable-mdpi/sym_keyboard_space_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_space_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_tab_holo.png b/java/res/drawable-mdpi/sym_keyboard_tab_holo.png
new file mode 100644
index 0000000..c41af7b
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_tab_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_holo.png b/java/res/drawable-mdpi/sym_keyboard_voice_holo.png
index c1116dc..158439a 100644
--- a/java/res/drawable-mdpi/sym_keyboard_voice_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_voice_holo.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_off_holo.png b/java/res/drawable-mdpi/sym_keyboard_voice_off_holo.png
index 081a130..79026f1 100644
--- a/java/res/drawable-mdpi/sym_keyboard_voice_off_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_voice_off_holo.png
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_at_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_at_holo.9.png
deleted file mode 100644
index 8c84654..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_at_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_at_large_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_at_large_holo.9.png
deleted file mode 100644
index b9e095b..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_at_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_colon_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_colon_holo.9.png
deleted file mode 100644
index 3cd0e3c..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_colon_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_colon_large_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_colon_large_holo.9.png
deleted file mode 100644
index 01b47b2..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_colon_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_comma_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_comma_holo.9.png
deleted file mode 100644
index b70f71b..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_comma_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_comma_large_holo.9.png
deleted file mode 100644
index 1ae82d4..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_comma_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_exclamation_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_exclamation_holo.9.png
deleted file mode 100644
index 3409535..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_exclamation_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_exclamation_large_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_exclamation_large_holo.9.png
deleted file mode 100644
index 8dd2f27..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_exclamation_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_plus_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_plus_holo.9.png
deleted file mode 100644
index 51e86ba..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_plus_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_plus_large_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_plus_large_holo.9.png
deleted file mode 100644
index bf30f92..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_plus_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_question_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_question_holo.9.png
deleted file mode 100644
index 38b5d61..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_question_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_question_large_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_question_large_holo.9.png
deleted file mode 100644
index 448a027..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_question_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_quote_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_quote_holo.9.png
deleted file mode 100644
index 92fe8ea..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_quote_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_quote_large_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_quote_large_holo.9.png
deleted file mode 100644
index a777d01..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_quote_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_underline_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_underline_holo.9.png
deleted file mode 100644
index 7a452c5..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_underline_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/key_hint_underline_large_holo.9.png b/java/res/drawable-sw600dp-mdpi/key_hint_underline_large_holo.9.png
deleted file mode 100644
index f738b02..0000000
--- a/java/res/drawable-sw600dp-mdpi/key_hint_underline_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_at_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_at_holo.9.png
deleted file mode 100644
index 5b946ff..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_at_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_at_large_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_at_large_holo.9.png
deleted file mode 100644
index 852f899..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_at_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_colon_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_colon_holo.9.png
deleted file mode 100644
index 1d9346e..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_colon_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_colon_large_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_colon_large_holo.9.png
deleted file mode 100644
index 17e9091..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_colon_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_comma_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_comma_holo.9.png
deleted file mode 100644
index c2a913c..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_comma_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_comma_large_holo.9.png
deleted file mode 100644
index 846f213..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_comma_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_exclamation_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_exclamation_holo.9.png
deleted file mode 100644
index ce8e8de..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_exclamation_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_exclamation_large_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_exclamation_large_holo.9.png
deleted file mode 100644
index 035dcf8..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_exclamation_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_plus_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_plus_holo.9.png
deleted file mode 100644
index 931390b..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_plus_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_plus_large_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_plus_large_holo.9.png
deleted file mode 100644
index e6f9f8a..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_plus_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_question_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_question_holo.9.png
deleted file mode 100644
index 6cbeb59..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_question_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_question_large_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_question_large_holo.9.png
deleted file mode 100644
index bfd58de..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_question_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_quote_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_quote_holo.9.png
deleted file mode 100644
index 3b361b7..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_quote_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_quote_large_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_quote_large_holo.9.png
deleted file mode 100644
index 2a08aa1..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_quote_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_underline_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_underline_holo.9.png
deleted file mode 100644
index 06f3efb..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_underline_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-land-mdpi/key_hint_underline_large_holo.9.png b/java/res/drawable-sw768dp-land-mdpi/key_hint_underline_large_holo.9.png
deleted file mode 100644
index 50f99a1..0000000
--- a/java/res/drawable-sw768dp-land-mdpi/key_hint_underline_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_at_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_at_holo.9.png
deleted file mode 100644
index 8c84654..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_at_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_at_large_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_at_large_holo.9.png
deleted file mode 100644
index b9e095b..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_at_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_colon_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_colon_holo.9.png
deleted file mode 100644
index 3cd0e3c..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_colon_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_colon_large_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_colon_large_holo.9.png
deleted file mode 100644
index 01b47b2..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_colon_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_comma_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_comma_holo.9.png
deleted file mode 100644
index b70f71b..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_comma_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_comma_large_holo.9.png
deleted file mode 100644
index 1ae82d4..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_comma_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_exclamation_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_exclamation_holo.9.png
deleted file mode 100644
index 3409535..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_exclamation_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_exclamation_large_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_exclamation_large_holo.9.png
deleted file mode 100644
index 8dd2f27..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_exclamation_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_plus_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_plus_holo.9.png
deleted file mode 100644
index 51e86ba..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_plus_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_plus_large_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_plus_large_holo.9.png
deleted file mode 100644
index bf30f92..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_plus_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_question_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_question_holo.9.png
deleted file mode 100644
index 38b5d61..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_question_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_question_large_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_question_large_holo.9.png
deleted file mode 100644
index 448a027..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_question_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_quote_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_quote_holo.9.png
deleted file mode 100644
index 92fe8ea..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_quote_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_quote_large_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_quote_large_holo.9.png
deleted file mode 100644
index a777d01..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_quote_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_underline_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_underline_holo.9.png
deleted file mode 100644
index 7a452c5..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_underline_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/key_hint_underline_large_holo.9.png b/java/res/drawable-sw768dp-mdpi/key_hint_underline_large_holo.9.png
deleted file mode 100644
index f738b02..0000000
--- a/java/res/drawable-sw768dp-mdpi/key_hint_underline_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw768dp-mdpi/sym_keyboard_tab_holo.png b/java/res/drawable-sw768dp-mdpi/sym_keyboard_tab_holo.png
deleted file mode 100644
index 5acd12c..0000000
--- a/java/res/drawable-sw768dp-mdpi/sym_keyboard_tab_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_candidate_pressed.9.png b/java/res/drawable-xhdpi/btn_candidate_pressed.9.png
new file mode 100644
index 0000000..41e126a
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_candidate_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_center_default.9.png b/java/res/drawable-xhdpi/btn_center_default.9.png
new file mode 100644
index 0000000..e847425
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_center_default.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_center_pressed.9.png b/java/res/drawable-xhdpi/btn_center_pressed.9.png
new file mode 100644
index 0000000..facfd43
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_center_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_center_selected.9.png b/java/res/drawable-xhdpi/btn_center_selected.9.png
new file mode 100644
index 0000000..facfd43
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_center_selected.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_close_candidates_pane.9.png b/java/res/drawable-xhdpi/btn_close_candidates_pane.9.png
new file mode 100644
index 0000000..9d797ed
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_close_candidates_pane.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_expand_candidates_pane.9.png b/java/res/drawable-xhdpi/btn_expand_candidates_pane.9.png
new file mode 100644
index 0000000..88d01c9
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_expand_candidates_pane.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png
index 3e4a812..b9667be 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off_holo.9.png
index d61dd2b..1719992 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_holo.9.png
index c127e2a..0eb9c9f 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png
index ac3f358..5114bf5 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
index e5de3b0..9923950 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
index c63b69a..474951c 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_fulltrans_normal.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_fulltrans_normal.9.png
new file mode 100644
index 0000000..f7e32f7
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_fulltrans_normal.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_fulltrans_pressed.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_fulltrans_pressed.9.png
new file mode 100644
index 0000000..00c4476
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_fulltrans_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png
index e1361ed..4a255f7 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png
index 6efd3a7..5bcd1c9 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal.9.png
new file mode 100644
index 0000000..f53b404
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_off.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_off.9.png
new file mode 100644
index 0000000..3c6ae6b
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_off.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_stone.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_stone.9.png
new file mode 100644
index 0000000..eebe7d3
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_stone.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_on.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on.9.png
new file mode 100644
index 0000000..655bfb2
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_stone.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_stone.9.png
new file mode 100644
index 0000000..336248a
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_stone.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_stone.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_stone.9.png
new file mode 100644
index 0000000..bb1c72b
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_stone.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_popup_background_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_popup_background_holo.9.png
new file mode 100644
index 0000000..10d788d
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_popup_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_popup_selected_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_popup_selected_holo.9.png
index 3ed9b1e..0a9a9e4 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_popup_selected_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_popup_selected_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_pressed.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed.9.png
new file mode 100644
index 0000000..f739116
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off.9.png
new file mode 100644
index 0000000..34a948f
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on.9.png
new file mode 100644
index 0000000..520f12d
--- /dev/null
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/candidate_feedback_background.9.png b/java/res/drawable-xhdpi/candidate_feedback_background.9.png
new file mode 100644
index 0000000..0ec90e5
--- /dev/null
+++ b/java/res/drawable-xhdpi/candidate_feedback_background.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/caution.png b/java/res/drawable-xhdpi/caution.png
new file mode 100644
index 0000000..cfc3f75
--- /dev/null
+++ b/java/res/drawable-xhdpi/caution.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/hint_popup.9.png b/java/res/drawable-xhdpi/hint_popup.9.png
new file mode 100644
index 0000000..add4743
--- /dev/null
+++ b/java/res/drawable-xhdpi/hint_popup.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/hint_popup_holo.9.png b/java/res/drawable-xhdpi/hint_popup_holo.9.png
index bc37fa8..a2b114e 100644
--- a/java/res/drawable-xhdpi/hint_popup_holo.9.png
+++ b/java/res/drawable-xhdpi/hint_popup_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_dialog_keyboard.png b/java/res/drawable-xhdpi/ic_dialog_keyboard.png
new file mode 100644
index 0000000..f114c93
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_dialog_keyboard.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_mic_dialog.png b/java/res/drawable-xhdpi/ic_mic_dialog.png
new file mode 100644
index 0000000..5d6399c
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_mic_dialog.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_subtype_keyboard.png b/java/res/drawable-xhdpi/ic_subtype_keyboard.png
new file mode 100644
index 0000000..a79bb34
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_subtype_keyboard.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_subtype_mic.png b/java/res/drawable-xhdpi/ic_subtype_mic.png
new file mode 100644
index 0000000..fc8d18c
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_subtype_mic.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_suggest_strip_microphone.png b/java/res/drawable-xhdpi/ic_suggest_strip_microphone.png
new file mode 100644
index 0000000..d65d287
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_suggest_strip_microphone.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_suggest_strip_microphone_swipe.png b/java/res/drawable-xhdpi/ic_suggest_strip_microphone_swipe.png
new file mode 100644
index 0000000..889378a
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_suggest_strip_microphone_swipe.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_at_holo.9.png b/java/res/drawable-xhdpi/key_hint_at_holo.9.png
deleted file mode 100644
index 1d257cb..0000000
--- a/java/res/drawable-xhdpi/key_hint_at_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_at_large_holo.9.png b/java/res/drawable-xhdpi/key_hint_at_large_holo.9.png
deleted file mode 100644
index 53de283..0000000
--- a/java/res/drawable-xhdpi/key_hint_at_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_colon_holo.9.png b/java/res/drawable-xhdpi/key_hint_colon_holo.9.png
deleted file mode 100644
index 73394b5..0000000
--- a/java/res/drawable-xhdpi/key_hint_colon_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_colon_large_holo.9.png b/java/res/drawable-xhdpi/key_hint_colon_large_holo.9.png
deleted file mode 100644
index a4a6acb..0000000
--- a/java/res/drawable-xhdpi/key_hint_colon_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_comma_holo.9.png b/java/res/drawable-xhdpi/key_hint_comma_holo.9.png
deleted file mode 100644
index d4dbdf4..0000000
--- a/java/res/drawable-xhdpi/key_hint_comma_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_comma_large_holo.9.png b/java/res/drawable-xhdpi/key_hint_comma_large_holo.9.png
deleted file mode 100644
index aea5c8e..0000000
--- a/java/res/drawable-xhdpi/key_hint_comma_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_exclamation_holo.9.png b/java/res/drawable-xhdpi/key_hint_exclamation_holo.9.png
deleted file mode 100644
index 078d1d8..0000000
--- a/java/res/drawable-xhdpi/key_hint_exclamation_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_exclamation_large_holo.9.png b/java/res/drawable-xhdpi/key_hint_exclamation_large_holo.9.png
deleted file mode 100644
index ddd6b13..0000000
--- a/java/res/drawable-xhdpi/key_hint_exclamation_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_plus_holo.9.png b/java/res/drawable-xhdpi/key_hint_plus_holo.9.png
deleted file mode 100644
index d0ce9c9..0000000
--- a/java/res/drawable-xhdpi/key_hint_plus_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_plus_large_holo.9.png b/java/res/drawable-xhdpi/key_hint_plus_large_holo.9.png
deleted file mode 100644
index 9cf20e8..0000000
--- a/java/res/drawable-xhdpi/key_hint_plus_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_question_holo.9.png b/java/res/drawable-xhdpi/key_hint_question_holo.9.png
deleted file mode 100644
index 1e886d8..0000000
--- a/java/res/drawable-xhdpi/key_hint_question_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_question_large_holo.9.png b/java/res/drawable-xhdpi/key_hint_question_large_holo.9.png
deleted file mode 100644
index 8262287..0000000
--- a/java/res/drawable-xhdpi/key_hint_question_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_quote_holo.9.png b/java/res/drawable-xhdpi/key_hint_quote_holo.9.png
deleted file mode 100644
index fd8bbad..0000000
--- a/java/res/drawable-xhdpi/key_hint_quote_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_quote_large_holo.9.png b/java/res/drawable-xhdpi/key_hint_quote_large_holo.9.png
deleted file mode 100644
index 51d5b49..0000000
--- a/java/res/drawable-xhdpi/key_hint_quote_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_underline_holo.9.png b/java/res/drawable-xhdpi/key_hint_underline_holo.9.png
deleted file mode 100644
index 37d2fa5..0000000
--- a/java/res/drawable-xhdpi/key_hint_underline_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/key_hint_underline_large_holo.9.png b/java/res/drawable-xhdpi/key_hint_underline_large_holo.9.png
deleted file mode 100644
index 4249a8e..0000000
--- a/java/res/drawable-xhdpi/key_hint_underline_large_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_background.9.png b/java/res/drawable-xhdpi/keyboard_background.9.png
new file mode 100644
index 0000000..2639963
--- /dev/null
+++ b/java/res/drawable-xhdpi/keyboard_background.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_background_holo.9.png
new file mode 100644
index 0000000..58d1514
--- /dev/null
+++ b/java/res/drawable-xhdpi/keyboard_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_dark_background.9.png b/java/res/drawable-xhdpi/keyboard_dark_background.9.png
new file mode 100644
index 0000000..27b7a10
--- /dev/null
+++ b/java/res/drawable-xhdpi/keyboard_dark_background.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_background_holo.9.png
index 943f9e4..44675ba 100644
--- a/java/res/drawable-xhdpi/keyboard_key_feedback_background_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_left_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_left_background_holo.9.png
new file mode 100644
index 0000000..cfd1f52
--- /dev/null
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_left_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_left_more_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_left_more_background_holo.9.png
new file mode 100644
index 0000000..a01c173
--- /dev/null
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_left_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_holo.9.png
index f66e545..1d2abc2 100644
--- a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_right_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_right_background_holo.9.png
new file mode 100644
index 0000000..5616d19
--- /dev/null
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_right_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_right_more_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_right_more_background_holo.9.png
new file mode 100644
index 0000000..ecf9c2c
--- /dev/null
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_right_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_popup_panel_background.9.png b/java/res/drawable-xhdpi/keyboard_popup_panel_background.9.png
new file mode 100644
index 0000000..79f7ab0
--- /dev/null
+++ b/java/res/drawable-xhdpi/keyboard_popup_panel_background.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_suggest_strip.9.png b/java/res/drawable-xhdpi/keyboard_suggest_strip.9.png
new file mode 100644
index 0000000..1b568df
--- /dev/null
+++ b/java/res/drawable-xhdpi/keyboard_suggest_strip.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_suggest_strip_divider.png b/java/res/drawable-xhdpi/keyboard_suggest_strip_divider.png
new file mode 100644
index 0000000..cd7c2c7
--- /dev/null
+++ b/java/res/drawable-xhdpi/keyboard_suggest_strip_divider.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png b/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png
new file mode 100644
index 0000000..f4ff7dc
--- /dev/null
+++ b/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/mic_base.png b/java/res/drawable-xhdpi/mic_base.png
new file mode 100644
index 0000000..5c060be
--- /dev/null
+++ b/java/res/drawable-xhdpi/mic_base.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/mic_full.png b/java/res/drawable-xhdpi/mic_full.png
new file mode 100644
index 0000000..32ffe12
--- /dev/null
+++ b/java/res/drawable-xhdpi/mic_full.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/mic_slash.png b/java/res/drawable-xhdpi/mic_slash.png
new file mode 100644
index 0000000..18b2254
--- /dev/null
+++ b/java/res/drawable-xhdpi/mic_slash.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_123_mic.png b/java/res/drawable-xhdpi/sym_bkeyboard_123_mic.png
new file mode 100644
index 0000000..0aefaa1
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_123_mic.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_delete.png b/java/res/drawable-xhdpi/sym_bkeyboard_delete.png
new file mode 100644
index 0000000..b84ee76
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_delete.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_mic.png b/java/res/drawable-xhdpi/sym_bkeyboard_mic.png
new file mode 100644
index 0000000..8c3f11d
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_mic.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_num0.png b/java/res/drawable-xhdpi/sym_bkeyboard_num0.png
new file mode 100644
index 0000000..da8d50a
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_num0.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_num1.png b/java/res/drawable-xhdpi/sym_bkeyboard_num1.png
new file mode 100644
index 0000000..62443eb
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_num1.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_num2.png b/java/res/drawable-xhdpi/sym_bkeyboard_num2.png
new file mode 100644
index 0000000..4b3ef7e
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_num2.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_num3.png b/java/res/drawable-xhdpi/sym_bkeyboard_num3.png
new file mode 100644
index 0000000..48315e4
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_num3.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_num4.png b/java/res/drawable-xhdpi/sym_bkeyboard_num4.png
new file mode 100644
index 0000000..17958d7
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_num4.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_num5.png b/java/res/drawable-xhdpi/sym_bkeyboard_num5.png
new file mode 100644
index 0000000..43716c4
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_num5.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_num6.png b/java/res/drawable-xhdpi/sym_bkeyboard_num6.png
new file mode 100644
index 0000000..09f2b42
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_num6.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_num7.png b/java/res/drawable-xhdpi/sym_bkeyboard_num7.png
new file mode 100644
index 0000000..72438ec
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_num7.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_num8.png b/java/res/drawable-xhdpi/sym_bkeyboard_num8.png
new file mode 100644
index 0000000..403b53f
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_num8.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_num9.png b/java/res/drawable-xhdpi/sym_bkeyboard_num9.png
new file mode 100644
index 0000000..644c2a4
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_num9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_return.png b/java/res/drawable-xhdpi/sym_bkeyboard_return.png
new file mode 100644
index 0000000..1632ecd
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_return.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_search.png b/java/res/drawable-xhdpi/sym_bkeyboard_search.png
new file mode 100644
index 0000000..69d8b22
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_search.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_settings.png b/java/res/drawable-xhdpi/sym_bkeyboard_settings.png
new file mode 100644
index 0000000..050154a
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_settings.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_shift.png b/java/res/drawable-xhdpi/sym_bkeyboard_shift.png
new file mode 100644
index 0000000..d15d11a
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_shift.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_shift_locked.png b/java/res/drawable-xhdpi/sym_bkeyboard_shift_locked.png
new file mode 100644
index 0000000..83b287f
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_shift_locked.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_space.png b/java/res/drawable-xhdpi/sym_bkeyboard_space.png
new file mode 100644
index 0000000..5ca62c7
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_space.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_tab.png b/java/res/drawable-xhdpi/sym_bkeyboard_tab.png
new file mode 100644
index 0000000..6ca1997
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_tab.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_voice_off.png b/java/res/drawable-xhdpi/sym_bkeyboard_voice_off.png
new file mode 100644
index 0000000..fc6a4eb
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_voice_off.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_123_mic.png b/java/res/drawable-xhdpi/sym_keyboard_123_mic.png
new file mode 100644
index 0000000..bcb0979
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_123_mic.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete.png b/java/res/drawable-xhdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..3c0b8b1
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete_holo.png b/java/res/drawable-xhdpi/sym_keyboard_delete_holo.png
new file mode 100644
index 0000000..5150073
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_delete_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_feedback_123_mic.png b/java/res/drawable-xhdpi/sym_keyboard_feedback_123_mic.png
new file mode 100644
index 0000000..5238c16
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_feedback_123_mic.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_feedback_delete.png b/java/res/drawable-xhdpi/sym_keyboard_feedback_delete.png
new file mode 100644
index 0000000..f0c29e7
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_feedback_delete.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_feedback_language_arrows_left.png b/java/res/drawable-xhdpi/sym_keyboard_feedback_language_arrows_left.png
new file mode 100644
index 0000000..a90b8da
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_feedback_language_arrows_left.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_feedback_language_arrows_right.png b/java/res/drawable-xhdpi/sym_keyboard_feedback_language_arrows_right.png
new file mode 100644
index 0000000..1123bf3
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_feedback_language_arrows_right.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_feedback_mic.png b/java/res/drawable-xhdpi/sym_keyboard_feedback_mic.png
new file mode 100644
index 0000000..5ac27eb
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_feedback_mic.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_feedback_numalt.png b/java/res/drawable-xhdpi/sym_keyboard_feedback_numalt.png
new file mode 100644
index 0000000..b68e606
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_feedback_numalt.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_feedback_return.png b/java/res/drawable-xhdpi/sym_keyboard_feedback_return.png
new file mode 100644
index 0000000..c777d00
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_feedback_return.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_feedback_search.png b/java/res/drawable-xhdpi/sym_keyboard_feedback_search.png
new file mode 100644
index 0000000..32e0789
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_feedback_search.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_feedback_settings.png b/java/res/drawable-xhdpi/sym_keyboard_feedback_settings.png
new file mode 100644
index 0000000..021cafa
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_feedback_settings.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_feedback_shift.png b/java/res/drawable-xhdpi/sym_keyboard_feedback_shift.png
new file mode 100644
index 0000000..d8c0325
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_feedback_shift.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_feedback_space.png b/java/res/drawable-xhdpi/sym_keyboard_feedback_space.png
new file mode 100644
index 0000000..cee1056
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_feedback_space.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_feedback_tab.png b/java/res/drawable-xhdpi/sym_keyboard_feedback_tab.png
new file mode 100644
index 0000000..0650e01
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_feedback_tab.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_language_arrows_left.png b/java/res/drawable-xhdpi/sym_keyboard_language_arrows_left.png
new file mode 100644
index 0000000..226f2e6
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_language_arrows_left.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_language_arrows_right.png b/java/res/drawable-xhdpi/sym_keyboard_language_arrows_right.png
new file mode 100644
index 0000000..1e02e7e
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_language_arrows_right.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_mic.png b/java/res/drawable-xhdpi/sym_keyboard_mic.png
new file mode 100644
index 0000000..1323b6d
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_mic.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num0.png b/java/res/drawable-xhdpi/sym_keyboard_num0.png
new file mode 100644
index 0000000..06bd3bf
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num0.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num0_holo.png b/java/res/drawable-xhdpi/sym_keyboard_num0_holo.png
new file mode 100644
index 0000000..33e80bd
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num0_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num1.png b/java/res/drawable-xhdpi/sym_keyboard_num1.png
new file mode 100644
index 0000000..dfa0cfe
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num1.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num1_holo.png b/java/res/drawable-xhdpi/sym_keyboard_num1_holo.png
new file mode 100644
index 0000000..dce3ee8
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num1_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num2.png b/java/res/drawable-xhdpi/sym_keyboard_num2.png
new file mode 100644
index 0000000..4bd2ead
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num2.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num2_holo.png b/java/res/drawable-xhdpi/sym_keyboard_num2_holo.png
new file mode 100644
index 0000000..e776efb
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num2_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num3.png b/java/res/drawable-xhdpi/sym_keyboard_num3.png
new file mode 100644
index 0000000..39db112
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num3.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num3_holo.png b/java/res/drawable-xhdpi/sym_keyboard_num3_holo.png
new file mode 100644
index 0000000..66e7b37
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num3_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num4.png b/java/res/drawable-xhdpi/sym_keyboard_num4.png
new file mode 100644
index 0000000..6035694
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num4.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num4_holo.png b/java/res/drawable-xhdpi/sym_keyboard_num4_holo.png
new file mode 100644
index 0000000..bf70e28
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num4_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num5.png b/java/res/drawable-xhdpi/sym_keyboard_num5.png
new file mode 100644
index 0000000..3238b3f
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num5.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num5_holo.png b/java/res/drawable-xhdpi/sym_keyboard_num5_holo.png
new file mode 100644
index 0000000..98d0b5c
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num5_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num6.png b/java/res/drawable-xhdpi/sym_keyboard_num6.png
new file mode 100644
index 0000000..c8da5a9
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num6.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num6_holo.png b/java/res/drawable-xhdpi/sym_keyboard_num6_holo.png
new file mode 100644
index 0000000..67340ef
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num6_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num7.png b/java/res/drawable-xhdpi/sym_keyboard_num7.png
new file mode 100644
index 0000000..553cf5f
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num7.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num7_holo.png b/java/res/drawable-xhdpi/sym_keyboard_num7_holo.png
new file mode 100644
index 0000000..8432249
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num7_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num8.png b/java/res/drawable-xhdpi/sym_keyboard_num8.png
new file mode 100644
index 0000000..eaaf533
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num8.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num8_holo.png b/java/res/drawable-xhdpi/sym_keyboard_num8_holo.png
new file mode 100644
index 0000000..6d520bc
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num8_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num9.png b/java/res/drawable-xhdpi/sym_keyboard_num9.png
new file mode 100644
index 0000000..b510d37
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_num9_holo.png b/java/res/drawable-xhdpi/sym_keyboard_num9_holo.png
new file mode 100644
index 0000000..158c879
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_num9_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_return.png b/java/res/drawable-xhdpi/sym_keyboard_return.png
new file mode 100644
index 0000000..ad06122
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_return.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_return_holo.png b/java/res/drawable-xhdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..6e2e1c0
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_search.png b/java/res/drawable-xhdpi/sym_keyboard_search.png
new file mode 100644
index 0000000..aa785a2
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_search.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings.png b/java/res/drawable-xhdpi/sym_keyboard_settings.png
new file mode 100644
index 0000000..5070425
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_settings.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings_holo.png b/java/res/drawable-xhdpi/sym_keyboard_settings_holo.png
new file mode 100644
index 0000000..018e45d
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_settings_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift.png b/java/res/drawable-xhdpi/sym_keyboard_shift.png
new file mode 100644
index 0000000..2901706
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_shift.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_holo.png b/java/res/drawable-xhdpi/sym_keyboard_shift_holo.png
new file mode 100644
index 0000000..41d4ca1
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_shift_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_locked.png b/java/res/drawable-xhdpi/sym_keyboard_shift_locked.png
new file mode 100644
index 0000000..a5deb60
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_shift_locked.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo.png b/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo.png
new file mode 100644
index 0000000..b811b74
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_space.png b/java/res/drawable-xhdpi/sym_keyboard_space.png
new file mode 100644
index 0000000..66fc3e9
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_space.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_space_holo.png b/java/res/drawable-xhdpi/sym_keyboard_space_holo.png
new file mode 100644
index 0000000..da89264
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_space_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_space_led.9.png b/java/res/drawable-xhdpi/sym_keyboard_space_led.9.png
new file mode 100644
index 0000000..6525fef
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_space_led.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_tab.png b/java/res/drawable-xhdpi/sym_keyboard_tab.png
new file mode 100644
index 0000000..0ef2ab5
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_tab.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_tab_holo.png b/java/res/drawable-xhdpi/sym_keyboard_tab_holo.png
new file mode 100644
index 0000000..dd207ca
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_tab_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_holo.png b/java/res/drawable-xhdpi/sym_keyboard_voice_holo.png
new file mode 100644
index 0000000..d73e499
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_voice_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo.png b/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo.png
new file mode 100644
index 0000000..8d34a5a
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/vs_dialog_blue.9.png b/java/res/drawable-xhdpi/vs_dialog_blue.9.png
new file mode 100644
index 0000000..3284d78
--- /dev/null
+++ b/java/res/drawable-xhdpi/vs_dialog_blue.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/vs_dialog_red.9.png b/java/res/drawable-xhdpi/vs_dialog_red.9.png
new file mode 100644
index 0000000..5af2465
--- /dev/null
+++ b/java/res/drawable-xhdpi/vs_dialog_red.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/vs_dialog_yellow.9.png b/java/res/drawable-xhdpi/vs_dialog_yellow.9.png
new file mode 100644
index 0000000..4f50439
--- /dev/null
+++ b/java/res/drawable-xhdpi/vs_dialog_yellow.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/vs_popup_mic_edge.png b/java/res/drawable-xhdpi/vs_popup_mic_edge.png
new file mode 100644
index 0000000..1063cb4
--- /dev/null
+++ b/java/res/drawable-xhdpi/vs_popup_mic_edge.png
Binary files differ
diff --git a/java/res/drawable/btn_candidate_holo.xml b/java/res/drawable/btn_candidate_ics.xml
similarity index 93%
rename from java/res/drawable/btn_candidate_holo.xml
rename to java/res/drawable/btn_candidate_ics.xml
index 66cd246..e4257e3 100644
--- a/java/res/drawable/btn_candidate_holo.xml
+++ b/java/res/drawable/btn_candidate_ics.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** 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.
diff --git a/java/res/drawable/btn_keyboard_key_honeycomb.xml b/java/res/drawable/btn_keyboard_key_ics.xml
similarity index 92%
rename from java/res/drawable/btn_keyboard_key_honeycomb.xml
rename to java/res/drawable/btn_keyboard_key_ics.xml
index 3dab843..7335cc2 100644
--- a/java/res/drawable/btn_keyboard_key_honeycomb.xml
+++ b/java/res/drawable/btn_keyboard_key_ics.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- 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.
@@ -14,7 +14,6 @@
      limitations under the License.
 -->
 
-<!-- TODO: Remove "gingerbread" from file name and rename this to "btn_keyboard_key.xml". -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 
     <!-- Functional keys. -->
diff --git a/java/res/drawable/btn_keyboard_key_gingerbread_popup.xml b/java/res/drawable/btn_keyboard_key_popup.xml
similarity index 93%
rename from java/res/drawable/btn_keyboard_key_gingerbread_popup.xml
rename to java/res/drawable/btn_keyboard_key_popup.xml
index 9b6d23b..860cfd5 100644
--- a/java/res/drawable/btn_keyboard_key_gingerbread_popup.xml
+++ b/java/res/drawable/btn_keyboard_key_popup.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- 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.
diff --git a/java/res/drawable/btn_keyboard_key_honeycomb_popup.xml b/java/res/drawable/btn_keyboard_key_popup_ics.xml
similarity index 85%
rename from java/res/drawable/btn_keyboard_key_honeycomb_popup.xml
rename to java/res/drawable/btn_keyboard_key_popup_ics.xml
index 6c27136..8f797ac 100644
--- a/java/res/drawable/btn_keyboard_key_honeycomb_popup.xml
+++ b/java/res/drawable/btn_keyboard_key_popup_ics.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- 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.
@@ -17,4 +17,5 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_popup_selected_holo" />
+    <item android:drawable="@drawable/btn_keyboard_key_popup_background_holo" />
 </selector>
diff --git a/java/res/drawable/btn_keyboard_key_stone.xml b/java/res/drawable/btn_keyboard_key_stone.xml
index a6040a0..27932e8 100644
--- a/java/res/drawable/btn_keyboard_key_stone.xml
+++ b/java/res/drawable/btn_keyboard_key_stone.xml
@@ -16,6 +16,13 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 
+    <!-- Functional keys. -->
+
+    <item android:state_single="true" android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+    <item android:state_single="true"
+          android:drawable="@drawable/btn_keyboard_key_normal_stone" />
+
     <!-- Toggle keys. Use checkable/checked state. -->
 
     <item android:state_checkable="true" android:state_checked="true"
diff --git a/java/res/drawable/keyboard_key_feedback_honeycomb.xml b/java/res/drawable/keyboard_key_feedback_ics.xml
similarity index 93%
rename from java/res/drawable/keyboard_key_feedback_honeycomb.xml
rename to java/res/drawable/keyboard_key_feedback_ics.xml
index a3ea140..04c8679 100644
--- a/java/res/drawable/keyboard_key_feedback_honeycomb.xml
+++ b/java/res/drawable/keyboard_key_feedback_ics.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- 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.
diff --git a/java/res/layout-sw600dp/candidate.xml b/java/res/layout-sw600dp/candidate.xml
deleted file mode 100644
index e672707..0000000
--- a/java/res/layout-sw600dp/candidate.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?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.
-*/
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/candidate_strip_height"
-    android:orientation="horizontal"
-    android:paddingRight="@dimen/candidate_padding"
->
-    <ImageView
-        android:id="@+id/candidate_divider"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/candidate_strip_height"
-        android:visibility="gone"
-        android:focusable="false"
-        android:clickable="false"
-        android:src="@drawable/keyboard_suggest_strip_divider"
-        android:gravity="center_vertical|center_horizontal" />
-    <Button
-        android:id="@+id/candidate_word"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/candidate_strip_height"
-        android:minWidth="@dimen/candidate_min_width"
-        android:textSize="@dimen/candidate_text_size"
-        android:textColor="@color/candidate_normal"
-        android:background="@drawable/btn_candidate_holo"
-        android:focusable="true"
-        android:clickable="true"
-        android:gravity="center_vertical|center_horizontal"
-        android:paddingLeft="@dimen/candidate_padding" />
-    <TextView
-        android:id="@+id/candidate_debug_info"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:visibility="gone"
-        android:textSize="10dip"
-        android:textColor="#ff808080"
-        android:focusable="false"
-        android:clickable="false"
-        android:gravity="bottom"
-        android:paddingLeft="4dip" />
-</LinearLayout>
diff --git a/java/res/layout-sw600dp/candidates.xml b/java/res/layout-sw600dp/candidates.xml
deleted file mode 100644
index 26d6822..0000000
--- a/java/res/layout-sw600dp/candidates.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?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.
-*/
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/candidate_strip_height"
-    android:background="@drawable/keyboard_suggest_strip_holo"
-    android:paddingRight="@dimen/candidate_strip_padding"
-    android:paddingLeft="@dimen/candidate_strip_padding"
->
-    <HorizontalScrollView
-        android:id="@+id/candidates_scroll_view"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/candidate_strip_height"
-        android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length"
-        android:scrollbars="none"
-    >
-        <com.android.inputmethod.latin.CandidateView
-            android:id="@+id/candidates"
-            android:orientation="horizontal"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/candidate_strip_height"
-            android:background="@drawable/keyboard_suggest_strip_holo" />
-    </HorizontalScrollView>
-</LinearLayout>
diff --git a/java/res/layout-sw600dp/keyboard_popup_honeycomb.xml b/java/res/layout-sw600dp/keyboard_popup_honeycomb.xml
deleted file mode 100644
index 49eb936..0000000
--- a/java/res/layout-sw600dp/keyboard_popup_honeycomb.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?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.
-*/
--->
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:background="@drawable/keyboard_popup_panel_background_holo"
-        android:paddingLeft="40dip"
-        android:paddingRight="40dip"
-        >
-    <com.android.inputmethod.keyboard.KeyboardView
-            xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-            android:id="@+id/KeyboardView"
-            android:layout_alignParentBottom="true"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:background="@color/latinkeyboard_transparent"
-
-            latin:keyBackground="@drawable/btn_keyboard_key_honeycomb_popup"
-            latin:keyHysteresisDistance="0dip"
-            latin:verticalCorrection="@dimen/mini_keyboard_vertical_correction"
-            />
-</LinearLayout>
diff --git a/java/res/layout-sw768dp/candidate.xml b/java/res/layout-sw768dp/candidate.xml
deleted file mode 100644
index 74532a1..0000000
--- a/java/res/layout-sw768dp/candidate.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/candidate_strip_height"
-    android:orientation="horizontal"
-    android:paddingRight="@dimen/candidate_padding"
->
-    <ImageView
-        android:id="@+id/candidate_divider"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/candidate_strip_height"
-        android:visibility="gone"
-        android:focusable="false"
-        android:clickable="false"
-        android:src="@drawable/keyboard_suggest_strip_divider"
-        android:gravity="center_vertical|center_horizontal" />
-    <Button
-        android:id="@+id/candidate_word"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/candidate_strip_height"
-        android:minWidth="@dimen/candidate_min_width"
-        android:textSize="@dimen/candidate_text_size"
-        android:textColor="@color/candidate_normal"
-        android:background="@drawable/btn_candidate_holo"
-        android:focusable="true"
-        android:clickable="true"
-        android:gravity="center_vertical|center_horizontal"
-        android:paddingLeft="@dimen/candidate_padding" />
-    <TextView
-        android:id="@+id/candidate_debug_info"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:visibility="gone"
-        android:textSize="10dip"
-        android:textColor="#ff808080"
-        android:focusable="false"
-        android:clickable="false"
-        android:gravity="bottom"
-        android:paddingLeft="4dip" />
-</LinearLayout>
diff --git a/java/res/layout-sw768dp/candidates.xml b/java/res/layout-sw768dp/candidates.xml
deleted file mode 100644
index e2ddb84..0000000
--- a/java/res/layout-sw768dp/candidates.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/candidate_strip_height"
-    android:background="@drawable/keyboard_suggest_strip_holo"
-    android:paddingRight="@dimen/candidate_strip_padding"
-    android:paddingLeft="@dimen/candidate_strip_padding"
->
-    <HorizontalScrollView
-        android:id="@+id/candidates_scroll_view"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/candidate_strip_height"
-        android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length"
-        android:scrollbars="none"
-    >
-        <com.android.inputmethod.latin.CandidateView
-            android:id="@+id/candidates"
-            android:orientation="horizontal"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/candidate_strip_height"
-            android:background="@drawable/keyboard_suggest_strip_holo" />
-    </HorizontalScrollView>
-</LinearLayout>
diff --git a/java/res/layout-sw768dp/keyboard_popup_honeycomb.xml b/java/res/layout-sw768dp/keyboard_popup_honeycomb.xml
deleted file mode 100644
index 0b8229c..0000000
--- a/java/res/layout-sw768dp/keyboard_popup_honeycomb.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:background="@drawable/keyboard_popup_panel_background_holo"
-        android:paddingLeft="40dip"
-        android:paddingRight="40dip"
-        >
-    <com.android.inputmethod.keyboard.KeyboardView
-            xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-            android:id="@+id/KeyboardView"
-            android:layout_alignParentBottom="true"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:background="@color/latinkeyboard_transparent"
-
-            latin:keyBackground="@drawable/btn_keyboard_key_honeycomb_popup"
-            latin:keyHysteresisDistance="0dip"
-            latin:verticalCorrection="@dimen/mini_keyboard_vertical_correction"
-            />
-</LinearLayout>
diff --git a/java/res/layout-sw768dp/recognition_status.xml b/java/res/layout-sw768dp/recognition_status.xml
new file mode 100644
index 0000000..40bc098
--- /dev/null
+++ b/java/res/layout-sw768dp/recognition_status.xml
@@ -0,0 +1,101 @@
+<?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.
+*/
+-->
+<RelativeLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:background="@drawable/background_voice">
+    <LinearLayout
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/popup_layout"
+            android:orientation="vertical"
+            android:layout_height="371dip"
+            android:layout_width="500dip"
+            android:layout_centerInParent="true"
+            android:background="@drawable/vs_dialog_red">
+        <TextView
+                android:id="@+id/text"
+                android:text="@string/voice_error"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:singleLine="true"
+                android:layout_marginTop="10dip"
+                android:textSize="28sp"
+                android:textColor="#ffffff"
+                android:layout_gravity="center"
+                android:visibility="invisible"/>
+        <RelativeLayout
+                android:layout_height="0dip"
+                android:layout_width="match_parent"
+                android:layout_weight="1.0">
+            <com.android.inputmethod.deprecated.voice.SoundIndicator
+                    android:id="@+id/sound_indicator"
+                    android:src="@drawable/mic_full"
+                    android:background="@drawable/mic_base"
+                    android:adjustViewBounds="true"
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:layout_centerInParent="true"
+                    android:visibility="gone"/>
+            <ImageView
+                    android:id="@+id/image"
+                    android:src="@drawable/mic_slash"
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:layout_centerInParent="true"
+                    android:visibility="visible"/>
+            <ProgressBar
+                    android:id="@+id/progress"
+                    android:indeterminate="true"
+                    android:indeterminateOnly="false"
+                    android:layout_height="60dip"
+                    android:layout_width="60dip"
+                    android:layout_centerInParent="true"
+                    android:visibility="gone"/>
+        </RelativeLayout>
+        <!--
+        The text is set by the code. We specify a random text (voice_error), so the
+        text view does not have a zero height. This is necessary to keep the slash
+        mic and the recording mic is the same position
+        -->
+        <TextView
+                android:id="@+id/language"
+                android:text="@string/voice_error"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:singleLine="true"
+                android:textSize="14sp"
+                android:layout_marginBottom="3dip"
+                android:layout_gravity="center"
+                android:textColor="#ffffff"
+                android:visibility="invisible"/>
+        <Button
+                android:id="@+id/button"
+                android:layout_width="match_parent"
+                android:layout_height="54dip"
+                android:singleLine="true"
+                android:focusable="true"
+                android:text="@string/cancel"
+                android:layout_gravity="center_horizontal"
+                android:background="@drawable/btn_center"
+                android:textColor="#ffffff"
+                android:textSize="19sp" />
+    </LinearLayout>
+</RelativeLayout>
diff --git a/java/res/layout/candidate.xml b/java/res/layout/candidate.xml
index f2c4126..94ae2b4 100644
--- a/java/res/layout/candidate.xml
+++ b/java/res/layout/candidate.xml
@@ -18,43 +18,10 @@
 */
 -->
 
-<LinearLayout
+<Button
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/candidate_strip_height"
-    android:orientation="horizontal"
-    android:paddingRight="@dimen/candidate_padding"
->
-    <ImageView
-        android:id="@+id/candidate_divider"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/candidate_strip_height"
-        android:visibility="gone"
-        android:focusable="false"
-        android:clickable="false"
-        android:src="@drawable/keyboard_suggest_strip_divider"
-        android:gravity="center_vertical|center_horizontal" />
-    <Button
-        android:id="@+id/candidate_word"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/candidate_strip_height"
-        android:minWidth="@dimen/candidate_min_width"
-        android:textSize="@dimen/candidate_text_size"
-        android:textColor="@color/candidate_normal"
-        android:background="@drawable/btn_candidate"
-        android:focusable="true"
-        android:clickable="true"
-        android:gravity="center_vertical|center_horizontal"
-        android:paddingLeft="@dimen/candidate_padding" />
-    <TextView
-        android:id="@+id/candidate_debug_info"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:visibility="gone"
-        android:textSize="10dip"
-        android:textColor="#ff808080"
-        android:focusable="false"
-        android:clickable="false"
-        android:gravity="bottom"
-        android:paddingLeft="4dip" />
-</LinearLayout>
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:minWidth="@dimen/candidate_min_width"
+    android:textSize="@dimen/candidate_text_size"
+    style="?attr/suggestionBackgroundStyle" />
diff --git a/java/res/layout-sw768dp/candidate_preview.xml b/java/res/layout/candidate_divider.xml
similarity index 69%
rename from java/res/layout-sw768dp/candidate_preview.xml
rename to java/res/layout/candidate_divider.xml
index 61d5f8e..1d75e52 100644
--- a/java/res/layout-sw768dp/candidate_preview.xml
+++ b/java/res/layout/candidate_divider.xml
@@ -18,12 +18,13 @@
 */
 -->
 
-<TextView
+<ImageView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:textSize="18sp"
-    android:textColor="?android:attr/textColorPrimaryInverse"
-    android:minWidth="32dip"
-    android:gravity="center"
-    android:background="@drawable/keyboard_popup_panel_background_holo" />
+    android:layout_height="match_parent"
+    android:src="@drawable/keyboard_suggest_strip_divider"
+    android:paddingRight="@dimen/candidate_padding"
+    android:paddingLeft="@dimen/candidate_padding"
+    android:focusable="false"
+    android:clickable="false"
+    android:gravity="center_vertical|center_horizontal" />
diff --git a/java/res/layout/candidate_preview.xml b/java/res/layout/candidate_preview.xml
index fe2002d..32705c9 100644
--- a/java/res/layout/candidate_preview.xml
+++ b/java/res/layout/candidate_preview.xml
@@ -19,11 +19,10 @@
 -->
 
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content" 
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:textSize="18sp"
     android:textColor="?android:attr/textColorPrimaryInverse"
     android:minWidth="32dip"
     android:gravity="center"
-    android:background="@drawable/candidate_feedback_background"
-    />
+    style="?attr/suggestionPreviewBackgroundStyle" />
diff --git a/java/res/layout/candidates.xml b/java/res/layout/candidates.xml
deleted file mode 100644
index 1b8d041..0000000
--- a/java/res/layout/candidates.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/candidate_strip_height"
-    android:background="@drawable/keyboard_suggest_strip"
-    android:paddingRight="@dimen/candidate_strip_padding"
-    android:paddingLeft="@dimen/candidate_strip_padding"
->
-    <HorizontalScrollView
-        android:id="@+id/candidates_scroll_view"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/candidate_strip_height"
-        android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length"
-        android:scrollbars="none"
-    >
-        <com.android.inputmethod.latin.CandidateView
-            android:id="@+id/candidates"
-            android:orientation="horizontal"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/candidate_strip_height"
-            android:background="@drawable/keyboard_suggest_strip" />
-    </HorizontalScrollView>
-</LinearLayout>
diff --git a/java/res/layout/candidates_strip.xml b/java/res/layout/candidates_strip.xml
new file mode 100644
index 0000000..88f4c38
--- /dev/null
+++ b/java/res/layout/candidates_strip.xml
@@ -0,0 +1,59 @@
+<?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:android="http://schemas.android.com/apk/res/android"
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        android:id="@+id/candidate_left"
+        layout="@layout/candidate" />
+    <include
+        layout="@layout/candidate_divider" />
+    <include
+        android:id="@+id/candidate_center"
+        layout="@layout/candidate" />
+    <include
+        layout="@layout/candidate_divider" />
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_weight="1.0"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+    >
+        <include
+            android:id="@+id/candidate_right"
+            layout="@layout/candidate" />
+        <!-- Image drawables are set in CandidateView constructor -->
+        <ImageButton
+            android:id="@+id/expand_candidates_pane"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            style="?attr/suggestionsStripBackgroundStyle" />
+        <ImageButton
+            android:id="@+id/close_candidates_pane"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            style="?attr/suggestionsStripBackgroundStyle" />
+    </LinearLayout>
+</merge>
diff --git a/java/res/layout/input_basic.xml b/java/res/layout/input_basic.xml
deleted file mode 100644
index 7b85bae..0000000
--- a/java/res/layout/input_basic.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* 
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
--->
-
-<com.android.inputmethod.keyboard.LatinKeyboardView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-        android:id="@+id/LatinkeyboardBaseView"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@drawable/keyboard_background"
-
-        latin:keyBackground="@drawable/btn_keyboard_key"
-        />
diff --git a/java/res/layout/input_basic_highcontrast.xml b/java/res/layout/input_basic_highcontrast.xml
deleted file mode 100644
index d9200fd..0000000
--- a/java/res/layout/input_basic_highcontrast.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.inputmethod.keyboard.LatinKeyboardView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-
-        android:id="@+id/LatinkeyboardBaseView"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@android:color/black"
-
-        latin:keyBackground="@drawable/btn_keyboard_key3"
-        />
diff --git a/java/res/layout/input_gingerbread.xml b/java/res/layout/input_gingerbread.xml
deleted file mode 100644
index 6233e6d..0000000
--- a/java/res/layout/input_gingerbread.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.inputmethod.keyboard.LatinKeyboardView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-        android:id="@+id/LatinkeyboardBaseView"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingTop="@dimen/keyboard_top_padding"
-        android:paddingBottom="@dimen/keyboard_bottom_padding"
-        android:background="@drawable/keyboard_dark_background"
-
-        latin:keyBackground="@drawable/btn_keyboard_key_gingerbread"
-        latin:keyLetterStyle="bold"
-        />
diff --git a/java/res/layout/input_honeycomb.xml b/java/res/layout/input_honeycomb.xml
deleted file mode 100644
index 6ccc63c..0000000
--- a/java/res/layout/input_honeycomb.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.inputmethod.keyboard.LatinKeyboardView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-        android:id="@+id/LatinkeyboardBaseView"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingTop="@dimen/keyboard_top_padding"
-        android:paddingBottom="@dimen/keyboard_bottom_padding"
-        android:background="@drawable/keyboard_background_holo"
-
-        latin:keyBackground="@drawable/btn_keyboard_key_honeycomb"
-        latin:keyPreviewLayout="@layout/key_preview_honeycomb"
-        latin:popupLayout="@layout/keyboard_popup_honeycomb"
-        latin:keyTextColorDisabled="#FF63666D"
-        latin:keyLetterStyle="bold"
-        latin:shadowColor="#00000000"
-        latin:shadowRadius="0.0"
-        />
diff --git a/java/res/layout/input_stone_bold.xml b/java/res/layout/input_stone_bold.xml
deleted file mode 100644
index 6fdc938..0000000
--- a/java/res/layout/input_stone_bold.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.inputmethod.keyboard.LatinKeyboardView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-        android:id="@+id/LatinkeyboardBaseView"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@drawable/keyboard_background"
-
-        latin:keyBackground="@drawable/btn_keyboard_key_stone"
-        latin:keyTextColor="@color/latinkeyboard_key_color_black"
-        latin:keyTextColorDisabled="#FF808080"
-        latin:shadowColor="@color/latinkeyboard_key_color_white"
-        latin:keyLetterStyle="bold"
-        latin:colorScheme="black"
-        latin:popupLayout="@layout/input_stone_popup"
-        />
diff --git a/java/res/layout/input_stone_normal.xml b/java/res/layout/input_stone_normal.xml
deleted file mode 100644
index 6ae9aed..0000000
--- a/java/res/layout/input_stone_normal.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.inputmethod.keyboard.LatinKeyboardView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-        android:id="@+id/LatinkeyboardBaseView"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@drawable/keyboard_background"
-
-        latin:keyBackground="@drawable/btn_keyboard_key_stone"
-        latin:keyTextColor="@color/latinkeyboard_key_color_black"
-        latin:keyTextColorDisabled="#FF808080"
-        latin:shadowColor="@color/latinkeyboard_key_color_white"
-        latin:colorScheme="black"
-        latin:popupLayout="@layout/input_stone_popup"
-        />
diff --git a/java/res/layout/input_stone_popup.xml b/java/res/layout/input_stone_popup.xml
deleted file mode 100644
index b4da045..0000000
--- a/java/res/layout/input_stone_popup.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:background="@drawable/keyboard_popup_panel_background"
-        >
-    <com.android.inputmethod.keyboard.KeyboardView
-            xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-            android:id="@+id/KeyboardView"
-            android:layout_alignParentBottom="true"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:background="@drawable/keyboard_background"
-
-            latin:keyBackground="@drawable/btn_keyboard_key_stone"
-            latin:keyTextColor="@color/latinkeyboard_key_color_black"
-            latin:shadowColor="@color/latinkeyboard_key_color_white"
-            latin:popupLayout="@layout/input_stone_popup"
-        />
-</LinearLayout>
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml
new file mode 100644
index 0000000..93a0466
--- /dev/null
+++ b/java/res/layout/input_view.xml
@@ -0,0 +1,81 @@
+<?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.
+*/
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+>
+    <!-- On tablets, the candidate strip is centered with horizontal paddings on both sides because
+         width of the landscape mode is too long for the candidate strip. This LinearLayout is
+         required to hold the paddings. -->
+    <LinearLayout
+        android:id="@+id/candidates_container"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/candidate_strip_minimum_height"
+        android:gravity="bottom"
+    >
+        <View
+            android:layout_width="@dimen/candidate_strip_padding"
+            android:layout_height="@dimen/candidate_strip_height"
+            style="?attr/suggestionsStripBackgroundStyle" />
+        <com.android.inputmethod.latin.CandidateView
+            android:id="@+id/candidates"
+            android:layout_weight="1.0"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/candidate_strip_height"
+            android:gravity="center_vertical"
+            style="?attr/candidateViewStyle" />
+        <View
+            android:layout_width="@dimen/candidate_strip_padding"
+            android:layout_height="@dimen/candidate_strip_height"
+            style="?attr/suggestionsStripBackgroundStyle" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/candidates_pane_container"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        style="?attr/suggestionsStripBackgroundStyle"
+    >
+        <View
+            android:layout_width="@dimen/candidate_strip_padding"
+            android:layout_height="@dimen/candidate_strip_height" />
+        <FrameLayout
+            android:id="@+id/candidates_pane"
+            android:layout_weight="1.0"
+            android:layout_width="0dp"
+            android:layout_height="match_parent" />
+        <View
+            android:layout_width="@dimen/candidate_strip_padding"
+            android:layout_height="@dimen/candidate_strip_height" />
+    </LinearLayout>
+
+    <com.android.inputmethod.keyboard.LatinKeyboardView
+        android:id="@+id/keyboard_view"
+        android:layout_alignParentBottom="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/java/res/layout/key_preview.xml b/java/res/layout/key_preview.xml
index de03506..5332d15 100644
--- a/java/res/layout/key_preview.xml
+++ b/java/res/layout/key_preview.xml
@@ -22,8 +22,7 @@
     android:layout_width="wrap_content"
     android:layout_height="80sp"
     android:textSize="40sp"
-    android:textColor="@color/latinkeyboard_key_color_white"
     android:minWidth="32dip"
     android:gravity="center"
-    android:background="@drawable/keyboard_key_feedback"
+    style="?attr/keyPreviewStyle"
     />
diff --git a/java/res/layout/key_preview_honeycomb.xml b/java/res/layout/key_preview_honeycomb.xml
deleted file mode 100644
index a90fe55..0000000
--- a/java/res/layout/key_preview_honeycomb.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="80sp"
-    android:textSize="40sp"
-    android:textColor="@color/latinkeyboard_key_color_white"
-    android:minWidth="24dip"
-    android:gravity="center"
-    android:background="@drawable/keyboard_key_feedback_honeycomb"
-    />
diff --git a/java/res/layout/keyboard_popup.xml b/java/res/layout/keyboard_popup.xml
index ac8134b..e2508da 100644
--- a/java/res/layout/keyboard_popup.xml
+++ b/java/res/layout/keyboard_popup.xml
@@ -19,23 +19,16 @@
 -->
 <LinearLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="horizontal"
-        android:background="@drawable/keyboard_popup_panel_background"
-        android:paddingLeft="16dip"
-        android:paddingRight="16dip"
+        style="?attr/popupMiniKeyboardPanelStyle"
         >
-    <com.android.inputmethod.keyboard.KeyboardView
+    <com.android.inputmethod.keyboard.PopupMiniKeyboardView
             xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-            android:id="@+id/KeyboardView"
+            android:id="@+id/mini_keyboard_view"
             android:layout_alignParentBottom="true"
-            android:layout_width="match_parent"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:background="@color/latinkeyboard_transparent"
-
-            latin:keyBackground="@drawable/btn_keyboard_key_gingerbread_popup"
-            latin:keyHysteresisDistance="0dip"
-            latin:verticalCorrection="@dimen/mini_keyboard_vertical_correction"
             />
 </LinearLayout>
diff --git a/java/res/layout/keyboard_popup_honeycomb.xml b/java/res/layout/keyboard_popup_honeycomb.xml
deleted file mode 100644
index e5fcbd4..0000000
--- a/java/res/layout/keyboard_popup_honeycomb.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:background="@drawable/keyboard_popup_panel_background_holo"
-        android:paddingLeft="24dip"
-        android:paddingRight="24dip"
-        >
-    <com.android.inputmethod.keyboard.KeyboardView
-            xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-            android:id="@+id/KeyboardView"
-            android:layout_alignParentBottom="true"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:background="@color/latinkeyboard_transparent"
-
-            latin:keyBackground="@drawable/btn_keyboard_key_honeycomb_popup"
-            latin:keyHysteresisDistance="0dip"
-            latin:verticalCorrection="@dimen/mini_keyboard_vertical_correction"
-            />
-</LinearLayout>
diff --git a/java/res/layout/recognition_status.xml b/java/res/layout/recognition_status.xml
index 9474d6f..a2ddb7c 100644
--- a/java/res/layout/recognition_status.xml
+++ b/java/res/layout/recognition_status.xml
@@ -1,19 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* 
+/*
 **
 ** Copyright 2009, 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.
 */
 -->
@@ -37,7 +37,7 @@
                 android:layout_width="wrap_content"
                 android:singleLine="true"
                 android:layout_marginTop="10dip"
-                android:textSize="28sp"
+                android:textSize="20sp"
                 android:textColor="#ffffff"
                 android:layout_gravity="center"
                 android:visibility="invisible"/>
@@ -45,7 +45,7 @@
                 android:layout_height="0dip"
                 android:layout_width="match_parent"
                 android:layout_weight="1.0">
-            <com.android.inputmethod.voice.SoundIndicator
+            <com.android.inputmethod.deprecated.voice.SoundIndicator
                     android:id="@+id/sound_indicator"
                     android:src="@drawable/mic_full"
                     android:background="@drawable/mic_base"
@@ -81,7 +81,8 @@
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
                 android:singleLine="true"
-                android:textSize="14sp"
+                android:textSize="15sp"
+                android:layout_marginTop="3dip"
                 android:layout_marginBottom="3dip"
                 android:layout_gravity="center"
                 android:textColor="#ffffff"
@@ -89,13 +90,13 @@
         <Button
                 android:id="@+id/button"
                 android:layout_width="match_parent"
-                android:layout_height="54dip"
+                android:layout_height="30dip"
                 android:singleLine="true"
                 android:focusable="true"
                 android:text="@string/cancel"
                 android:layout_gravity="center_horizontal"
                 android:background="@drawable/btn_center"
                 android:textColor="#ffffff"
-                android:textSize="19sp" />
+                android:textSize="15sp" />
     </LinearLayout>
 </RelativeLayout>
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
new file mode 100644
index 0000000..db62173
--- /dev/null
+++ b/java/res/values-af/strings.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Android-sleutelbord"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Android-sleutelbordinstellings"</string>
+    <string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropsies"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibreer met sleuteldruk"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Klank met sleuteldruk"</string>
+    <string name="popup_on_keypress" msgid="123894815723512944">"Opspring by druk van sleutel"</string>
+    <!-- no translation found for general_category (1859088467017573195) -->
+    <skip />
+    <!-- no translation found for correction_category (2236750915056607613) -->
+    <skip />
+    <!-- no translation found for ngram_category (5337109164339320257) -->
+    <skip />
+    <!-- no translation found for misc_category (6894192814868233453) -->
+    <skip />
+    <!-- no translation found for advanced_settings (362895144495591463) -->
+    <skip />
+    <!-- no translation found for advanced_settings_summary (5193513161106637254) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_no_delay (2096123151571458064) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
+    <string name="auto_cap" msgid="1719746674854628252">"Outohoofletters"</string>
+    <string name="quick_fixes" msgid="5353213327680897927">"Vinnige oplossings"</string>
+    <string name="quick_fixes_summary" msgid="3405028402510332373">"Korrigeer algemene tikfoute"</string>
+    <!-- no translation found for prefs_show_suggestions (8026799663445531637) -->
+    <skip />
+    <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>
+    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Wys in portretmodus"</string>
+    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Versteek altyd"</string>
+    <!-- no translation found for prefs_use_spacebar_language_switch (8828538114550634449) -->
+    <skip />
+    <string name="prefs_settings_key" msgid="4623341240804046498">"Wys instellingsleutel"</string>
+    <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Outomaties"</string>
+    <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Wys altyd"</string>
+    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Versteek altyd"</string>
+    <string name="auto_correction" msgid="4979925752001319458">"Outokorrigering"</string>
+    <!-- outdated translation 6881047311475758267 -->     <string name="auto_correction_summary" msgid="5625751551134658006">"Korrigeer outomaties die vorige woord"</string>
+    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Af"</string>
+    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Nederig"</string>
+    <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressief"</string>
+    <!-- outdated translation 1323347224043514969 -->     <string name="bigram_suggestion" msgid="2636414079905220518">"Bigram-voorstelle"</string>
+    <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Gebruik vorige woord om voorstel te verbeter"</string>
+    <!-- no translation found for bigram_prediction (8914273444762259739) -->
+    <skip />
+    <!-- no translation found for bigram_prediction_summary (1747261921174300098) -->
+    <skip />
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Gestoor"</string>
+    <string name="label_go_key" msgid="1635148082137219148">"Gaan"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Volgende"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Klaar"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Stuur"</string>
+    <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_more_key" msgid="3760239494604948502">"Meer"</string>
+    <string name="label_pause_key" msgid="181098308428035340">"Laat wag"</string>
+    <string name="label_wait_key" msgid="6402152600878093134">"Wag"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Steminvoering"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Steminvoering vir jou taal word nie tans ondersteun nie, maar werk wel in Engels."</string>
+    <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Steminvoer gebruik Google se spraakherkenning. "<a href="http://m.google.com/privacy">"Die Mobiel-privaatheidsbeleid"</a>" is van toepassing."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="3190378129944934856">"Om steminvoer af te skakel, gaan na invoermetode-instellings."</string>
+    <string name="voice_hint_dialog_message" msgid="1420686286820661548">"Om steminvoer te gebruik, druk die mikrofoonknoppie."</string>
+    <string name="voice_listening" msgid="467518160751321844">"Praat nou"</string>
+    <string name="voice_working" msgid="6666937792815731889">"Werkend"</string>
+    <string name="voice_initializing" msgid="661962047129906646"></string>
+    <string name="voice_error" msgid="5140896300312186162">"Fout. Probeer asseblief weer."</string>
+    <string name="voice_network_error" msgid="6649556447401862563">"Kon nie koppel nie"</string>
+    <string name="voice_too_much_speech" msgid="5746973620134227376">"Fout, te veel spraak."</string>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Oudioprobleem"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Bedienerfout"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Geen spraak gehoor nie"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Geen passings gevind nie"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Stemsoek nie geïnstalleer nie"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Wenk:"</b>" Sleep oor die sleutelbord om te praat"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Wenk:"</b>" Probeer volgende keer om leestekens soos \"punt\", \"komma\" of \"vraagteken\" hardop te sê."</string>
+    <string name="cancel" msgid="6830980399865683324">"Kanselleer"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Steminvoering"</string>
+    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Op hoofsleutelbord"</string>
+    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Op simbolesleutelbord"</string>
+    <string name="voice_input_modes_off" msgid="3745699748218082014">"Af"</string>
+    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikrofoon op hoofsleutelbord"</string>
+    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofoon op simbolesleutelbord"</string>
+    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Steminvoer is gedeaktiveer"</string>
+    <string name="selectInputMethod" msgid="315076553378705821">"Kies invoermetode"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Invoertale"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Gly vinger op spasiebalk om taal te verander"</string>
+    <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Raak weer om te stoor"</string>
+    <string name="has_dictionary" msgid="6071847973466625007">"Woordeboek beskikbaar"</string>
+    <string name="prefs_enable_log" msgid="6620424505072963557">"Aktiveer gebruikerterugvoer"</string>
+    <string name="prefs_description_log" msgid="5827825607258246003">"Help hierdie invoermetode-redigeerder te verbeter deur gebruikstatistiek en omvalverslae outomaties na Google te stuur."</string>
+    <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Raak om woorde reg te maak"</string>
+    <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Raak ingevoerde woorde om hulle te korrigeer, net wanneer voorstelle sigbaar is"</string>
+    <string name="keyboard_layout" msgid="437433231038683666">"Sleutelbordtema"</string>
+    <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Tsjeggiese sleutelbord"</string>
+    <!-- no translation found for subtype_mode_ar_keyboard (2655338636329774995) -->
+    <skip />
+    <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Deense sleutelbord"</string>
+    <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Duitse sleutelbord"</string>
+    <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Engelse (VK) sleutelbord"</string>
+    <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Engelse (VS) sleutelbord"</string>
+    <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Spaanse sleutelbord"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
+    <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Franse sleutelbord"</string>
+    <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Franse (Kanada) sleutelbord"</string>
+    <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Franse (Switserland) sleutelbord"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <!-- no translation found for subtype_mode_iw_keyboard (1787536828253289950) -->
+    <skip />
+    <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Italiaanse sleutelbord"</string>
+    <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Noorweegse sleutelbord"</string>
+    <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Nederlandse sleutelbord"</string>
+    <!-- no translation found for subtype_mode_pl_keyboard (2225816414814396047) -->
+    <skip />
+    <!-- no translation found for subtype_mode_pt_keyboard (7503997804861754840) -->
+    <skip />
+    <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Russiese sleutelbord"</string>
+    <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Serwiese sleutelbord"</string>
+    <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Sweedse sleutelbord"</string>
+    <!-- no translation found for subtype_mode_af_voice (7542487489657902699) -->
+    <skip />
+    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Tsjeggiese stem"</string>
+    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Duitse stem"</string>
+    <!-- no translation found for subtype_mode_en_voice (6643420989651848728) -->
+    <skip />
+    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Spaanse stem"</string>
+    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Franse stem"</string>
+    <!-- no translation found for subtype_mode_it_voice (5077373057157441323) -->
+    <skip />
+    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Japannese stem"</string>
+    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Koreaanse stem"</string>
+    <!-- no translation found for subtype_mode_nl_voice (2603552312869575021) -->
+    <skip />
+    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Poolse stem"</string>
+    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Portugese stem"</string>
+    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Russiese stem"</string>
+    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Turkse stem"</string>
+    <!-- no translation found for subtype_mode_yue_voice (1576887891614624263) -->
+    <skip />
+    <!-- no translation found for subtype_mode_zh_voice (4360533229467271152) -->
+    <skip />
+    <!-- no translation found for subtype_mode_zu_voice (1146122571698884636) -->
+    <skip />
+    <string name="prefs_usability_study_mode" msgid="6937813623647419810">"Bruikbaarheidsmodus"</string>
+</resources>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
new file mode 100644
index 0000000..740aefc
--- /dev/null
+++ b/java/res/values-am/strings.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <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="vibrate_on_keypress" msgid="5258079494276955460">"በቁልፍመጫንጊዜ አንዝር"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"በቁልፍ መጫን ላይ የሚወጣ ድምፅ"</string>
+    <string name="popup_on_keypress" msgid="123894815723512944">"ቁልፍ ጫን ላይ ብቅ ባይ"</string>
+    <!-- no translation found for general_category (1859088467017573195) -->
+    <skip />
+    <!-- no translation found for correction_category (2236750915056607613) -->
+    <skip />
+    <!-- no translation found for ngram_category (5337109164339320257) -->
+    <skip />
+    <!-- no translation found for misc_category (6894192814868233453) -->
+    <skip />
+    <!-- no translation found for advanced_settings (362895144495591463) -->
+    <skip />
+    <!-- no translation found for advanced_settings_summary (5193513161106637254) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_no_delay (2096123151571458064) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
+    <string name="auto_cap" msgid="1719746674854628252">"ራስ-ሰር አቢይ ማድረግ"</string>
+    <string name="quick_fixes" msgid="5353213327680897927">"ፈጣንጥገና"</string>
+    <string name="quick_fixes_summary" msgid="3405028402510332373">" የተለመዱ የትየባ ስህተቶችንያስተካክላል"</string>
+    <!-- no translation found for prefs_show_suggestions (8026799663445531637) -->
+    <skip />
+    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"እየተየብክ ተመራጭ ቃላትን አሳይ"</string>
+    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"ሁልጊዜ አሳይ"</string>
+    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"በቁመት ሁነታ አሳይ"</string>
+    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"ሁልጊዜ ደብቅ"</string>
+    <!-- no translation found for prefs_use_spacebar_language_switch (8828538114550634449) -->
+    <skip />
+    <string name="prefs_settings_key" msgid="4623341240804046498">"የቅንብሮች ቁልፍ አሳይ"</string>
+    <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"ራስ ሰር"</string>
+    <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"ሁልጊዜ አሳይ"</string>
+    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"ሁልጊዜ ደብቅ"</string>
+    <string name="auto_correction" msgid="4979925752001319458">"በራስ ማስተካከል"</string>
+    <!-- outdated translation 6881047311475758267 -->     <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>
+    <!-- outdated translation 1323347224043514969 -->     <string name="bigram_suggestion" msgid="2636414079905220518">"ቢግራም ምክሮች"</string>
+    <string name="bigram_suggestion_summary" msgid="4383845146070101531">"ምክርን ለማሻሻል የቀደመ ቃልን ተጠቀም"</string>
+    <!-- no translation found for bigram_prediction (8914273444762259739) -->
+    <skip />
+    <!-- no translation found for bigram_prediction_summary (1747261921174300098) -->
+    <skip />
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : ተቀምጧል"</string>
+    <string name="label_go_key" msgid="1635148082137219148">"ሂድ"</string>
+    <string name="label_next_key" msgid="362972844525672568">"በመቀጠል"</string>
+    <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_more_key" msgid="3760239494604948502">"ተጨማሪ"</string>
+    <string name="label_pause_key" msgid="181098308428035340">"ላፍታ አቁም"</string>
+    <string name="label_wait_key" msgid="6402152600878093134">"ቆይ"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"የድምፅ ግቤ ት"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"የድምፅ ግቤት በአሁኑ ጊዜ ለእርስዎን ቋንቋ አይደግፍም፣ ግን በእንግሊዘኛ ይሰራል።"</string>
+    <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"የድምፅ ግቤት የGoogleን ንግግር ለይቶ ማወቂያ ይጠቀማል።"<a href="http://m.google.com/privacy">"የተንቀሳቃሽ ስልክ ግላዊ ፖሊሲ"</a>" ይተገበራል።"</string>
+    <string name="voice_warning_how_to_turn_off" msgid="3190378129944934856">"የድምፅ ግቤት ለማጥፋት፣ወደ ግቤት ሜተድ ቅንብሮች ሂድ።"</string>
+    <string name="voice_hint_dialog_message" msgid="1420686286820661548">"የድምፅግቤት ለመጠቀም፣ የማይክራፎንየድምፅ ማጉያ አዝራር ተጫን።"</string>
+    <string name="voice_listening" msgid="467518160751321844">"አሁን ተናገር"</string>
+    <string name="voice_working" msgid="6666937792815731889">"ሥራ ላይ"</string>
+    <string name="voice_initializing" msgid="661962047129906646"></string>
+    <string name="voice_error" msgid="5140896300312186162">"ስህተት፣ እባክዎ እንደገና ይሞክሩ።"</string>
+    <string name="voice_network_error" msgid="6649556447401862563">"ማያያዝ አልተቻለም"</string>
+    <string name="voice_too_much_speech" msgid="5746973620134227376">"ስህተት፣ በጣም ብዙ ንግግር።"</string>
+    <string name="voice_audio_error" msgid="5072707727016414454">"የድምፅ ችግር"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"የአገልጋይ ስህተት"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"ምንም ንግግር አልተሰማም"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"ምንም ተመሳሳይ አልተገኘም።"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"የድምፅ ፍለጋአልተጫነም"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"ምክር፡"</b>" ለመናገር በቁልፍሰሌዳ ላይ አንሸራት"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"ምክር፡"</b>" ሌላ ጊዜ፣  እንደ \"period\", \"comma\", ወይም \"question mark\" ያሉ ስርዓተ ነጥቦችን ለመናገር ሞክር።"</string>
+    <string name="cancel" msgid="6830980399865683324">"ይቅር"</string>
+    <string name="ok" msgid="7898366843681727667">"እሺ"</string>
+    <string name="voice_input" msgid="2466640768843347841">"የድምፅ ግቤት"</string>
+    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"በዋናቁልፍ ሰሌዳ ላይ"</string>
+    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"በምልክቶች ቁልፍ ሰሌዳ ላይ"</string>
+    <string name="voice_input_modes_off" msgid="3745699748218082014">"ውጪ"</string>
+    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"ድምፅ ማጉያ በዋናው ቁልፍሰሌዳው ላይ"</string>
+    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"የድምፅ ማጉያ ምልክትበቁልፍ ሰሌዳላይ"</string>
+    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"የድምፅ ግቤት ቦዝኗል"</string>
+    <string name="selectInputMethod" msgid="315076553378705821">"የግቤት ሜተድ ምረጥ"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"ቋንቋዎች አግቤት"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"ቋንቋ ለመለወጥ የቦታ ቁልፍ ላይ ጣትዎን ያንሸራቱ"</string>
+    <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← ለማስቀመጥ ድጋሚ ንካ"</string>
+    <string name="has_dictionary" msgid="6071847973466625007">"መዝገበ ቃላት አለ"</string>
+    <string name="prefs_enable_log" msgid="6620424505072963557">"የተጠቃሚ ግብረ ምላሽ አንቃ"</string>
+    <string name="prefs_description_log" msgid="5827825607258246003">"ወደ Google የተሰናከለ ሪፖርቶች እና አጠቃቀም ስታስቲክስ በራስ ሰር በመላክ ይህን ግቤት ሜተድ አርትኢ እገዛ ያሻሽላል።"</string>
+    <string name="prefs_enable_recorrection" msgid="4588408906649533582">"ቃላትን ለማስተካከል ንካ"</string>
+    <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"የገቡ ቃላትን ለማስተካከል ንካ፣ አማራጮች ሲታዩብቻ"</string>
+    <string name="keyboard_layout" msgid="437433231038683666">"የቁልፍ ሰሌዳ ገጽታ"</string>
+    <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"የቼክ ቁልፍሰሌዳ"</string>
+    <!-- no translation found for subtype_mode_ar_keyboard (2655338636329774995) -->
+    <skip />
+    <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"የዳኒሽኛ ቁልፍሰሌዳ"</string>
+    <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"የጀርመንኛ ቁልፍሰሌዳ"</string>
+    <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"የእንግሊዘኛ(ዩኬ) ቁልፍሰሌዳ"</string>
+    <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"የእንግሊዘኛ(ዩኤስ) ቁልፍሰሌዳ"</string>
+    <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"የስፖኒሽኛቁልፍ ሰሌዳ"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
+    <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"የፈረንሳይኛ ቁልፍሰሌዳ"</string>
+    <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"የፈረንሳይኛ(ካናዳ) ቁልፍሰሌዳ"</string>
+    <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"የፈረንሳይኛ(ስዊዘርላንድ) ቁልፍሰሌዳ"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <!-- no translation found for subtype_mode_iw_keyboard (1787536828253289950) -->
+    <skip />
+    <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"የጣሊያንኛ ቁልፍ ሰሌዳ"</string>
+    <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"የኖርዌጂኛ ቁልፍሰሌዳ"</string>
+    <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"የደች ቁልፍሰሌዳ"</string>
+    <!-- no translation found for subtype_mode_pl_keyboard (2225816414814396047) -->
+    <skip />
+    <!-- no translation found for subtype_mode_pt_keyboard (7503997804861754840) -->
+    <skip />
+    <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"የራሽኛቁልፍ ሰሌዳ"</string>
+    <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"የሩሲኪኛቁልፍ ሰሌዳ"</string>
+    <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"የሲውድናዊ ቁልፍሰሌዳ"</string>
+    <!-- no translation found for subtype_mode_af_voice (7542487489657902699) -->
+    <skip />
+    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"የቼክ ድምፅ"</string>
+    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"የጀርመናዊ ድምፅ"</string>
+    <!-- no translation found for subtype_mode_en_voice (6643420989651848728) -->
+    <skip />
+    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"የስፔናዊ ድምፅ"</string>
+    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"የፈረንሳዊ ድምፅ"</string>
+    <!-- no translation found for subtype_mode_it_voice (5077373057157441323) -->
+    <skip />
+    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"የጃፓናዊ ድምፅ"</string>
+    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"የኮሪያዊ ድምፅ"</string>
+    <!-- no translation found for subtype_mode_nl_voice (2603552312869575021) -->
+    <skip />
+    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"የፖሊሽኛ ድምፅ"</string>
+    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"የፖርቹጊኛ ድምፅ"</string>
+    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"የራሽኛድምፅ"</string>
+    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"የቱርካዊ ድምፅ"</string>
+    <!-- no translation found for subtype_mode_yue_voice (1576887891614624263) -->
+    <skip />
+    <!-- no translation found for subtype_mode_zh_voice (4360533229467271152) -->
+    <skip />
+    <!-- no translation found for subtype_mode_zu_voice (1146122571698884636) -->
+    <skip />
+    <string name="prefs_usability_study_mode" msgid="6937813623647419810">"የተገልጋይነት ጥናት ሁነታ"</string>
+</resources>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index f4bc3ca..455e9f5 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"صوت عند الضغط على مفتاح"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"انبثاق عند الضغط على المفاتيح"</string>
     <string name="general_category" msgid="1859088467017573195">"عام"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"تصحيح النص"</string>
+    <string name="correction_category" msgid="2236750915056607613">"تصحيح النص"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"الاقتراحات بناءً على الكلمات السابقة"</string>
+    <string name="misc_category" msgid="6894192814868233453">"خيارات أخرى"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"الإعدادات المتقدمة"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"خيارات للمستخدمين الخبراء"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"تأخير إزالة النافذة المنبثقة الأساسية"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"بلا تأخير"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"افتراضي"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"استخدام الأحرف الكبيرة تلقائيًا"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"إصلاحات سريعة"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"تصحيح الأخطاء المكتوبة الشائعة"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"عرض دومًا"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"عرض في وضع رأسي"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"إخفاء دومًا"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"استخدام مفتاح المسافة لتبديل اللغة"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"عرض مفتاح الإعدادات"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"تلقائي"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"إظهار بشكل دائم"</string>
@@ -45,8 +57,10 @@
     <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>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"اقتراحات ثنائية"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"اقتراحات ثنائية"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"استخدام الكلمة السابقة لتحسين الاقتراح"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"التنبؤ الثنائي"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"استخدام الكلمة السابقة أيضًا للتنبؤ"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : تم الحفظ"</string>
     <string name="label_go_key" msgid="1635148082137219148">"تنفيذ"</string>
     <string name="label_next_key" msgid="362972844525672568">"التالي"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"المزيد"</string>
     <string name="label_pause_key" msgid="181098308428035340">"توقف مؤقت"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"انتظار"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"حذف"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"رجوع"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"الإعدادات"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"العالي"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"مسافة"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"الرموز"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"الإدخال الصوتي"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"تشغيل الرموز"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"إيقاف الرموز"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"تشغيل العالي"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"إيقاف العالي"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"الإدخال الصوتي"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"الإدخال الصوتي غير معتمد حاليًا للغتك، ولكنه يعمل باللغة الإنجليزية."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"يستخدم الإدخال الصوتي خاصية التعرف على الكلام من Google. تنطبق "<a href="http://m.google.com/privacy">"سياسة خصوصية الجوال"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"المس الكلمات التي تم إدخالها لتصحيحها، وذلك فقط عندما تكون الاقتراحات مرئية."</string>
     <string name="keyboard_layout" msgid="437433231038683666">"مظهر لوحة المفاتيح"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"لوحة مفاتيح تشيكية"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"لوحة المفاتيح العربية"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"لوحة مفاتيح دانماركية"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"لوحة مفاتيح ألمانية"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"لوحة مفاتيح إنجليزية (بريطانيا)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"لوحة مفاتيح إنجليزية (الولايات المتحدة)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"لوحة مفاتيح إسبانية"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"لوحة مفاتيح فرنسية"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"لوحة مفاتيح فرنسية (كندا)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"لوحة مفاتيح فرنسية (سويسرا)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"لوحة المفاتيح العبرية"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"لوحة مفاتيح إيطالية"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"لوحة مفاتيح نرويجية"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"لوحة مفاتيح بولندية"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"لوحة المفاتيح البولندية"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"لوحة المفاتيح البرتغالية"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"لوحة مفاتيح روسية"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"لوحة مفاتيح صربية"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"لوحة مفاتيح سويدية"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 939fd75..05af016 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук при натискане на клавиш"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Изскачащ прозорец при натискане на клавиш"</string>
     <string name="general_category" msgid="1859088467017573195">"Общи"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Корекция на текста"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Корекция на текста"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Предложения въз основа на предишни думи"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Други опции"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Разширени настройки"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Опции за потребителите експерти"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Отхвърляне на подсказката"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без задържане"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По подразбиране"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Автоматично поставяне на главни букви"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Бързи корекции"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Коригира най-честите грешки при въвеждане"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Винаги да се показва"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Показване с вертикална ориентация"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Винаги да се скрива"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Смяна на езика с клавиша за интервал"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Показване на клавиша за настройки"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Автоматично"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Да се показва винаги"</string>
@@ -45,8 +57,10 @@
     <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>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Предложения за биграми"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Предложения за биграми"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Използване на предишната дума за подобряване на предложението"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Предвиждане за биграми"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Използване на предишната дума и за предвиждане"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Запазено"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Старт"</string>
     <string name="label_next_key" msgid="362972844525672568">"Напред"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Още"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Пауза"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Чака"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Изтриване"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Return"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Настройки"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Интервал"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Символи"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Гласово въвеждане"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Символите са включени"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Символите са изключени"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift е включен"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift е изключен"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Гласово въвеждане"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"За вашия език понастоящем не се поддържа гласово въвеждане, но можете да го използвате на английски."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Гласовото въвеждане използва функцията на Google за разпознаване на говор. В сила е "<a href="http://m.google.com/privacy">"Декларацията за поверителност за мобилни устройства"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Докоснете въведените думи, за да ги поправите – само когато предложенията са видими"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Тема на клавиатурата"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"чешка клавиатура"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"клавиатура на арабски"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"датска клавиатура"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"немска клавиатура"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"английска (Великобрит.) клавиатура"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"английска (САЩ) клавиатура"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"испанска клавиатура"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"френска клавиатура"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"френска (Канада) клавиатура"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"френска (Швейцария) клавиатура"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"клавиатура на иврит"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"италианска клавиатура"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"норвежка клавиатура"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"нидерландска клавиатура"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Полска клавиатура"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Португалска клавиатура"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"руска клавиатура"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"сръбска клавиатура"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"шведска клавиатура"</string>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index 8a0b845..01de933 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"So en prémer una tecla"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Finestra emergent en prémer un botó"</string>
     <string name="general_category" msgid="1859088467017573195">"General"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Correcció de text"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Correcció de text"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Suggeriments basats en paraules anteriors"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Altres opcions"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Configuració avançada"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Opcions per a usuaris experts"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retard d\'om. em. de tecla"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sense retard"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminat"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Majúscules automàtiques"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Correccions ràpides"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Corregeix els errors d\'ortografia habituals"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostra sempre"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostra en mode vertical"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Amaga sempre"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Canvi d\'idioma amb la barra espaiadora"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Mostra la tecla de configuració"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automàtic"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mostra sempre"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactiva"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderada"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Total"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Suggeriments Bigram"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Suggeriments Bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Utilitza la paraula anterior per millorar el suggeriment"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Predicció Bigram"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utilitza també la paraula anterior per a la predicció"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: desada"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Vés"</string>
     <string name="label_next_key" msgid="362972844525672568">"Següent"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Més"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Espera"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Suprimeix"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Retorn"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Configuració"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Majúscules"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Espai"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Símbols"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tabulador"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Entrada de veu"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Símbols activats"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Símbols desactivats"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Majúscules activades"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Majúscules desactivades"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Entrada de veu"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Actualment, l\'entrada de veu no és compatible amb el vostre idioma, però funciona en anglès."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"L\'entrada de veu utilitza el reconeixement de veu de Google. S\'hi aplica la "<a href="http://m.google.com/privacy">"Política de privadesa de Google per a mòbils"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Toca les paraules introduïdes per corregir-les, només quan els suggeriments siguin visibles"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tema del teclat"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Teclat txec"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Teclat àrab"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Teclat danès"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Teclat alemany"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Teclat anglès (Regne Unit)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Teclat anglès (Estats Units)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Teclat espanyol"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Teclat francès"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Teclat francès (Canadà)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Teclat francès (Suïssa)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Teclat hebreu"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Teclat italià"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Teclat noruec"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Teclat holandès"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Teclat polonès"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Teclat portuguès"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Teclat rus"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Teclat serbi"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Teclat suec"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index a8091bb..9dbe9ae 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Zvuk při stisku klávesy"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Zobrazit znaky při stisku klávesy"</string>
     <string name="general_category" msgid="1859088467017573195">"Obecné"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Oprava textu"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Oprava textu"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Návrhy na základě předchozích slov"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Další možnosti"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Pokročilá nastavení"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Možnosti pro zkušené uživatele"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Prodleva vysk. okna kláv."</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez prodlevy"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Výchozí"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Velká písmena automaticky"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Rychlé opravy"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Opravuje nejčastější chyby při psaní"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vždy zobrazovat"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Zobrazit v režimu na výšku"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vždy skrývat"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Přepínání jazyků mezerníkem"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Zobrazit klávesu Nastavení"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automaticky"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Vždy zobrazovat"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Vypnuto"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mírné"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivní"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Návrh Bigram"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Návrhy Bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Použít předchozí slovo ke zlepšení návrhu"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Odhady Bigram"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Použít předchozí slovo také pro odhad"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Uloženo"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Přejít"</string>
     <string name="label_next_key" msgid="362972844525672568">"Další"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Další"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pauza"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Čekat"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Smazat"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Enter"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Nastavení"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"mezera"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Symboly"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Karta"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Hlasový vstup"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Symboly jsou zapnuty"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Symboly jsou vypnuty"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Režim Shift je zapnutý"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Režim Shift je vypnutý"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Hlasový vstup"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Pro váš jazyk aktuálně není hlasový vstup podporován, ale funguje v angličtině."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Hlasový vstup používá rozpoznávání hlasu Google a vztahují se na něj "<a href="http://m.google.com/privacy">"Zásady ochrany osobních údajů pro mobilní služby"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Klepnutím na zadaná slova tato slova opravíte, musí však být viditelné návrhy."</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Motiv klávesnice"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Klávesnice – čeština"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Klávesnice – arabština"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Klávesnice – dánština"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Klávesnice – němčina"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Klávesnice – angličtina (VB)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Klávesnice – angličtina (USA)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Klávesnice – španělština"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Klávesnice – francouzština"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Klávesnice – francouzština (Kanada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Klávesnice – francouzština (Švýc.)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Klávesnice – hebrejština"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Klávesnice – italština"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Klávesnice – norština"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Klávesnice – holandština"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Polská klávesnice"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portugalská klávesnice"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Klávesnice – ruština"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Klávesnice – srbština"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Klávesnice – švédština"</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 5c23ccf..b4b4b5a 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Lyd ved tastetryk"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Popup ved tastetryk"</string>
     <string name="general_category" msgid="1859088467017573195">"Generelt"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Tekstkorrigering"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Tekstkorrigering"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Forslag baseret på tidligere ord"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Andre valgmuligheder"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Avancerede indstillinger"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Muligheder for ekspertbrugere"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Forsink. afvis. af taste-popup"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Ingen forsink."</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Skriv aut. med stort"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Hurtige løsninger"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Retter almindelige stavefejl"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vis altid"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Vis i portrættilstand"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Skjul altid"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Brug mellemrumst. som sprogskifter"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Vis indstillingsnøgle"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatisk"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Vis altid"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Fra"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Beskeden"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressiv"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigram-forslag"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigram-forslag"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Brug forrige ord for at forbedre forslag"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigram-forudsigelse"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Brug også tidligere ord til forudsigelse"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Gemt"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Gå"</string>
     <string name="label_next_key" msgid="362972844525672568">"Næste"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Mere"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pause"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Vent"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Slet"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Tilbage"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Indstillinger"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Mellemrum"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Symboler"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Fane"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Stemmeinput"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Symboler: Til"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Symboler: Fra"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift: Til"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift: Fra"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Stemmeinput"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Stemmeinput understøttes i øjeblikket ikke for dit sprog, men fungerer på engelsk."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Stemmeinput anvender Googles stemmegenkendelse. "<a href="http://m.google.com/privacy">"Fortrolighedspolitikken for mobilenheder"</a>" gælder."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Tryk på de indtastede ord for at rette dem. Kun når der er synlige forslag."</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tastaturtema"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Tjekkisk tastatur"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arabisk tastatur"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Dansk tastatur"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Tysk tastatur"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Engelsk tastatur (Storbritannien)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Engelsk tastatur (USA)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Spansk tastatur"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Fransk tastatur"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Fransk tastatur (Canada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Fransk tastatur (Schweiz)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Hebraisk tastatur"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Italiensk tastatur"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Norsk tastatur"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Hollandsk tastatur"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Polsk tastatur"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portugisisk tastatur"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Russisk tastatur"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Serbisk tastatur"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Svensk tastatur"</string>
diff --git a/java/res/values-de-rZZ/donottranslate-altchars.xml b/java/res/values-de-rZZ/donottranslate-altchars.xml
new file mode 100644
index 0000000..a6f8fc8
--- /dev/null
+++ b/java/res/values-de-rZZ/donottranslate-altchars.xml
@@ -0,0 +1,29 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="alternates_for_a">ä,â,à,á,æ,ã,å,ā</string>
+    <string name="alternates_for_e">3,ė</string>
+    <string name="alternates_for_o">9,ö,ô,ò,ó,õ,œ,ø,ō</string>
+    <string name="alternates_for_u">7,ü,û,ù,ú,ū</string>
+    <string name="alternates_for_s">ß,ś,š</string>
+    <string name="alternates_for_n">ñ,ń</string>
+    <string name="alternates_for_y">6</string>
+    <string name="alternates_for_z"></string>
+</resources>
diff --git a/java/res/drawable/btn_candidate_holo.xml b/java/res/values-de/config.xml
similarity index 69%
copy from java/res/drawable/btn_candidate_holo.xml
copy to java/res/values-de/config.xml
index 66cd246..272ff32 100644
--- a/java/res/drawable/btn_candidate_holo.xml
+++ b/java/res/values-de/config.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** 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.
@@ -18,10 +18,6 @@
 */
 -->
 
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android"
->
-    <item
-        android:state_pressed="true"
-        android:drawable="@drawable/btn_keyboard_key_popup_selected_holo" />
-</selector>
+<resources>
+    <bool name="config_require_umlaut_processing">true</bool>
+</resources>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 86adb91..01adc52 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Ton bei Tastendruck"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up bei Tastendruck"</string>
     <string name="general_category" msgid="1859088467017573195">"Allgemein"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Textkorrektur"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Textkorrektur"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Vorschläge basieren auf bisherigen Wörtern"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Sonstige Optionen"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Erweiterte Einstellungen"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Optionen für Experten"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Verzög. Schlüssel-Pop-up"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Keine Verzögerung"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschr."</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Quick Fixes"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Korrigiert gängige Tippfehler"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Immer anzeigen"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Im Hochformat anzeigen"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Immer ausblenden"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Sprache mit Leertaste ändern"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Einstellungstaste anz."</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatisch"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Immer anzeigen"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Aus"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mäßig"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Stark"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigramm-Vorschläge"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigramm-Vorschläge"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Zur Verbesserung des Vorschlags vorheriges Wort verwenden"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigramm-Vervollständigung"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Vorheriges Wort auch für Vervollständigung verwenden"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: gespeichert"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Los"</string>
     <string name="label_next_key" msgid="362972844525672568">"Weiter"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Mehr"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pause"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Warten"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Löschen"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Eingabe"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Einstellungen"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Umschalt"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Leerzeichen"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Symbole"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Spracheingabe"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Symbole an"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Symbole aus"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Umschalt an"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Umschalt aus"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Spracheingabe"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Spracheingaben werden zurzeit nicht für Ihre Sprache unterstützt, funktionieren jedoch in Englisch."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Die Spracheingabe verwendet die Spracherkennung von Google. Es gelten die "<a href="http://m.google.com/privacy">"Google Mobile-Datenschutzbestimmungen"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Tippen Sie zum Korrigieren auf eingegebene Wörter (nur, wenn Vorschläge angezeigt werden)."</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tastaturdesign"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Tschechische Tastatur"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arabische Tastatur"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Dänische Tastatur"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Deutsche Tastatur"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Englische Tastatur (GB)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Englische Tastatur (USA)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Spanische Tastatur"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Französische Tastatur"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Französische Tastatur (Kanada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Französische Tastatur (Schweiz)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Hebräische Tastatur"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Italienische Tastatur"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Norwegische Tastatur"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Niederländische Tastatur"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Polnische Tastatur"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portugiesische Tastatur"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Russische Tastatur"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Serbische Tastatur"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Schwedische Tastatur"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 355d7a3..9d8729e 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Ήχος κατά το πάτημα πλήκτρων"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Εμφάνιση με το πάτημα πλήκτρου"</string>
     <string name="general_category" msgid="1859088467017573195">"Γενικά"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Διόρθωση κειμένου"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Διόρθωση κειμένου"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Προτάσεις που βασίζονται σε προηγούμενες λέξεις"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Άλλες επιλογές"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Σύνθετες ρυθμίσεις"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Επιλογές για έμπειρους χρήστες"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Χρόνος εξαφ. αναδ. παραθ."</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Χωρίς καθυστέρ."</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Προεπιλογή"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Αυτόματη χρήση κεφαλαίων"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Γρήγορες διορθώσεις"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Διορθώνει συνηθισμένα λάθη πληκτρολόγησης"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Να εμφανίζεται πάντα"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Εμφάνιση σε λειτουργία κατακόρυφης προβολής"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Πάντα απόκρυψη"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Χρησιμοποιήστε τη δυνατότητα εναλλαγής γλώσσας του πλήκτρου διαστήματος"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Εμφάνιση πλήκτρου ρυθμίσεων"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Αυτόματο"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Να εμφανίζεται πάντα"</string>
@@ -45,8 +57,10 @@
     <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>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Προτάσεις bigram"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Προτάσεις bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Χρήση προηγούμενης λέξης για τη βελτίωση πρότασης"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Πρόβλεψη bigram"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Χρησιμοποιήστε, επίσης, την προηγούμενη λέξη για πρόβλεψη"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Αποθηκεύτηκε"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Μετ."</string>
     <string name="label_next_key" msgid="362972844525672568">"Επόμενο"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Περισσότερα"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Παύση"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Αναμ."</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Delete"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Return"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Ρυθμίσεις"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Κενό"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Σύμβολα"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Φωνητική εντολή"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Σύμβολα ενεργά"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Σύμβολα ανενεργά"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift ενεργό"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift ανενεργό"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Φωνητική είσοδος"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Η φωνητική είσοδος δεν υποστηρίζεται αυτή τη στιγμή για τη γλώσσα σας, ωστόσο λειτουργεί στα Αγγλικά."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Οι φωνητικές εντολές χρησιμοποιούν την τεχνολογία αναγνώρισης φωνής της Google. Ισχύει "<a href="http://m.google.com/privacy">"η Πολιτική Απορρήτου για κινητά"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Αγγίξτε τις λέξες για να τις διορθώσετε, μόνο όταν οι προτάσεις είναι ορατές"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Θέμα πληκτρολογίου"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Τσεχικό πληκτρολόγιο"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Πληκτρολόγιο με αραβική γραφή"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Δανικό πληκτρολόγιο"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Γερμανικό πληκτρολόγιο"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Αγγλικό (ΗΒ) πληκτρολόγιο"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Αγγλικό (ΗΠΑ) πληκτρολόγιο"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Ισπανικό πληκτρολόγιο"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Γαλλικό πληκτρολόγιο"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Γαλλικό (Καναδάς) πληκτρολόγιο"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Γαλλικό (Ελβετία) πληκτρολόγιο"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Πληκτρολόγιο με εβραϊκή γραφή"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Ιταλικό πληκτρολόγιο"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Νορβηγικό πληκτρολόγιο"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Ολλανδικό πληκτρολόγιο"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Πληκτρολόγιο με πολωνικούς χαρακτήρες"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Πορτογαλικό πληκτρολόγιο"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Ρωσικό πληκτρολόγιο"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Σερβικό πληκτρολόγιο"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Σουηδικό πληκτρολόγιο"</string>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 89a6a2e..3a488f4 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -27,7 +27,16 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Sound on key-press"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up on key press"</string>
     <string name="general_category" msgid="1859088467017573195">"General"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Text correction"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Text correction"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Suggestions based on previous words"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Other Options"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Advanced settings"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Options for expert users"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Key pop-up dismiss delay"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"No delay"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
+    <string name="use_contacts_dict" msgid="4435317977804180815">"Suggest Contact names"</string>
+    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Use names from Contacts for suggestions and corrections"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalisation"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Quick fixes"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Corrects commonly typed mistakes"</string>
@@ -36,6 +45,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Always show"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Show on portrait mode"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Always hide"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Use the spacebar language switcher"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Show settings key"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatic"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Always show"</string>
@@ -45,8 +55,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Off"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Modest"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressive"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigram Suggestions"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigram Suggestions"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Use previous word to improve suggestion"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigram prediction"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Use previous word also for prediction"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Saved"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Go"</string>
     <string name="label_next_key" msgid="362972844525672568">"Next"</string>
@@ -56,18 +68,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"More"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pause"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Wait"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Delete"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Return"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Settings"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Space"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Symbols"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Voice Input"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Symbols on"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Symbols off"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift on"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift off"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Voice input"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Voice input is not currently supported for your language, but does work in English."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Voice input uses Google\'s speech recognition. "<a href="http://m.google.com/privacy">"The Mobile Privacy Policy"</a>" applies."</string>
@@ -106,17 +106,24 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Touch words entered to correct them, only when suggestions are visible"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Keyboard Theme"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Czech Keyboard"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arabic Keyboard"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Danish Keyboard"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"German Keyboard"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"English (UK) Keyboard"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"English (US) Keyboard"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Spanish Keyboard"</string>
+    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Finnish Keyboard"</string>
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"French Keyboard"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"French (Canada) Keyboard"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"French (Switzerland) Keyboard"</string>
+    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Croatian Keyboard"</string>
+    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Hungarian Keyboard"</string>
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Hebrew Keyboard"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Italian Keyboard"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Norwegian Keyboard"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Dutch Keyboard"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Polish keyboard"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portuguese Keyboard"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Russian Keyboard"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Serbian Keyboard"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Swedish Keyboard"</string>
diff --git a/java/res/values-en/whitelist.xml b/java/res/values-en/whitelist.xml
new file mode 100644
index 0000000..9395f4c
--- /dev/null
+++ b/java/res/values-en/whitelist.xml
@@ -0,0 +1,38 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!--
+        An entry of the whitelist word should be:
+        1. (int)frequency
+        2. (String)before
+        3. (String)after
+     -->
+    <string-array name="wordlist_whitelist" translatable="false">
+
+        <item>255</item>
+        <item>ill</item>
+        <item>I\'ll</item>
+
+        <item>255</item>
+        <item>thisd</item>
+        <item>this\'d</item>
+
+    </string-array>
+</resources>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index 9a45219..048f02e 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Sonar al pulsar las teclas"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Aviso emergente al pulsar tecla"</string>
     <string name="general_category" msgid="1859088467017573195">"General"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Corrección de texto"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Corrección de texto"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Sugerencias sobre la base de palabras anteriores"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Otras opciones"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Configuración avanzada"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Opciones para usuarios expertos"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Demora en rechazo de ventana emergente de clave"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin demora"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminada"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Arreglos rápidos"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Corrige errores de escritura comunes"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar siempre"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostrar en modo retrato"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ocultar siempre"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Usa select. de id. de barra espac."</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Mostrar tecla de configuración"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automático"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mostrar siempre"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactivado"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderado"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Total"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Sugerencias de Vigoran"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Sugerencias de bigramas"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Utiliza la palabra anterior para mejorar la sugerencia"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Predicción de biagramas"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Usar la palabra anterior también para predicción."</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
     <string name="label_next_key" msgid="362972844525672568">"Siguiente"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Más"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Espera"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Eliminar"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Volver"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Configuración"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Mayús"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Espacio"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Símbolos"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Entrada de voz"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Símbolos activados"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Símbolos desactivados"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Mayús activado"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Mayús desactivado"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Entrada por voz"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"La entrada por voz no está admitida en tu idioma, pero sí funciona en inglés."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"La entrada de voz usa el reconocimiento de voz de Google. "<a href="http://m.google.com/privacy">"Se aplica la política de privacidad para"</a>" celulares."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Toca las palabras ingresadas que desees corregir solo cuando las sugerencias estén visibles."</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tema del teclado"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Teclado en checo"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Teclado árabe"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Teclado en danés"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Teclado en alemán"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Teclado en inglés (Reino Unido)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Teclado en inglés (EE.UU.)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Teclado en español"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Teclado en francés"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Teclado en francés (Canadá)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Teclado en francés (Suiza)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Teclado hebreo"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Teclado en italiano"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Teclado en noruego"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Teclado en holandés"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Teclado polaco"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Teclado en portugués"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Teclado en ruso"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Teclado en serbio"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Teclado en sueco"</string>
diff --git a/java/res/values-es/donottranslate-altchars.xml b/java/res/values-es/donottranslate-altchars.xml
index 99f1663..16111ec 100644
--- a/java/res/values-es/donottranslate-altchars.xml
+++ b/java/res/values-es/donottranslate-altchars.xml
@@ -25,4 +25,6 @@
     <string name="alternates_for_u">7,ú,ü,ù,û,ū</string>
     <string name="alternates_for_n">ñ,ń</string>
     <string name="alternates_for_c">ç,ć,č</string>
+    <string name="alternates_for_punctuation">"\\,,\?,!,¿,¡,:,-,\',\",),(,/,;,+,&amp;,\@"</string>
+    <string name="alternates_for_web_tab_punctuation">".,\\,,\?,!,¿,¡,:,-,\',\",),(,/,;,+,&amp;,\@"</string>
 </resources>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 1e72a0c..b7fbe96 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Sonido al pulsar tecla"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Popup al pulsar tecla"</string>
     <string name="general_category" msgid="1859088467017573195">"General"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Corrección ortográfica"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Corrección ortográfica"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Sugerencias basadas en palabras anteriores"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Otras opciones"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Ajustes avanzados"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Opciones para usuarios expertos"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retraso de rechazo"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin retraso"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminado"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Correcciones rápidas"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Corrige los errores tipográficos que se cometen con más frecuencia."</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar siempre"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostrar en modo vertical"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ocultar siempre"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Utilizar espacio para cambiar idioma"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Mostrar tecla de ajustes"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automáticamente"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mostrar siempre"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactivada"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Parcial"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Total"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Sugerencias de bigramas"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Sugerencias de bigramas"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Usar palabra anterior para mejorar sugerencias"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Predicción de bigramas"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utilizar también la palabra anterior para realizar la predicción"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
     <string name="label_next_key" msgid="362972844525672568">"Sig."</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Más"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Espera"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Eliminar"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Retroceso"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Ajustes"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Mayús"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Espacio"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Símbolos"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tabulador"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Entrada de voz"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Símbolos activados"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Símbolos desactivados"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Mayús activadas"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Mayús desactivadas"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Introducción de voz"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Actualmente la introducción de voz no está disponible en tu idioma, pero se puede utilizar en inglés."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"La entrada de voz utiliza el reconocimiento de voz de Google. Se aplica la "<a href="http://m.google.com/privacy">"Política de privacidad de Google para móviles"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Toca las palabras introducidas para corregirlas, solo cuando las sugerencias sean visibles."</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tema de teclado"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Teclado checo"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Teclado árabe"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Teclado danés"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Teclado alemán"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Teclado inglés (Reino Unido)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Teclado de inglés (EE.UU.)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Teclado español"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Teclado francés"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Teclado francés (Canadá)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Teclado francés (Suiza)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Teclado hebreo"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Teclado italiano"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Teclado noruego"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Teclado holandés"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Teclado polaco"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Teclado portugués"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Teclado ruso"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Teclado serbio"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Teclado sueco"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index ceeee52..17347b2 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"صدا با فشار کلید"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"بازشو با فشار کلید"</string>
     <string name="general_category" msgid="1859088467017573195">"کلی"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"تصحیح متن"</string>
+    <string name="correction_category" msgid="2236750915056607613">"تصحیح متن"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"پیشنهادهایی بر اساس کلمه های قبلی"</string>
+    <string name="misc_category" msgid="6894192814868233453">"سایر گزینه ها"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"تنظیمات پیشرفته"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"گزینه هایی برای کاربران حرفه ای"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"تأخیر در رد کردن کلید نمایشی"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"بدون تأخیر"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"پیش فرض"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"نوشتن با حروف بزرگ خودکار"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"راه حل های سریع"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"تصحیح خطاهای تایپی رایج"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"همیشه نمایش داده شود"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"نمایش در حالت عمودی"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"همیشه پنهان شود"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"از ویژگی تعویض زبان کلید فاصله استفاده شود"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"نمایش کلید تنظیمات"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"خودکار"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"همیشه نمایش"</string>
@@ -45,8 +57,10 @@
     <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>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"توضیحات بیگرام"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"پیشنهادهای Bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"برای بهبود پیشنهاد از کلمه قبلی استفاده شود"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"پیش بینی Bigram"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"استفاده از کلمه قبلی برای پیش بینی"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : ذخیره شد"</string>
     <string name="label_go_key" msgid="1635148082137219148">"برو"</string>
     <string name="label_next_key" msgid="362972844525672568">"بعدی"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"بیشتر"</string>
     <string name="label_pause_key" msgid="181098308428035340">"توقف موقت"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"منتظر بمانید"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Delete"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Return"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"تنظیمات"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"فاصله"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"نمادها"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"ورودی صوتی"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"نمادها روشن"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"نمادها خاموش"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift روشن"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift خاموش"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"ورودی صوتی"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"ورودی صوتی در حال حاضر برای زبان شما پشتیبانی نمی شود اما برای زبان انگلیسی فعال است."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"ورودی صوتی از تشخیص صدای Google استفاده می کند. "<a href="http://m.google.com/privacy">"خط مشی رازداری Mobile "</a>" اعمال می شود."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"فقط هنگامی که پیشنهادات قابل مشاهده هستند، برای تصحیح کلمات وارد شده آنها را لمس کنید"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"طرح زمینه صفحه کلید"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"صفحه کلید چک"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"صفحه کلید عربی"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"صفحه کلید دانمارکی"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"صفحه کلید آلمانی"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"صفحه کلید انگلیسی (بریتانیایی)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"صفحه کلید انگلیسی (آمریکایی)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"صفحه کلید اسپانیایی"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"صفحه کلید فرانسوی"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"صفحه کلید فرانسوی (کانادایی)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"صفحه کلید فرانسوی (سوئیس)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"صفحه کلید عبری"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"صفحه کلید ایتالیایی"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"صفحه کلید نروژی"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"صفحه کلید هلندی"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"صفحه کلید لهستانی"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"صفحه کلید پرتغالی"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"صفحه کلید روسی"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"صفحه کلید صربی"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"صفحه کلید سوئدی"</string>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index ee49c99..7fda7c7 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Toista ääni näppäimiä painettaessa"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Ponnahdusikkuna painalluksella"</string>
     <string name="general_category" msgid="1859088467017573195">"Yleinen"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Tekstin korjaus"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Tekstin korjaus"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Aiempiin sanoihin perustuvat ehdotukset"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Muut vaihtoehdot"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Lisäasetukset"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Valinnat kokeneille käyttäjille"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Näppäimen hylkäysviive"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Ei viivettä"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Oletus"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Automaattiset isot kirjaimet"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Pikakorjaukset"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Korjaa yleiset kirjoitusvirheet"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Näytä aina"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Näytä pystysuunnassa"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Piilota aina"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Vaihda kieli välil."</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Näytä asetukset-näppäin"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automaattinen"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Näytä aina"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Älä käytä"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Osittainen"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Täysi"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigram-ehdotukset"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigram-ehdotukset"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Paranna ehdotusta aiemman sanan avulla"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigram-ennakointi"</string>
+    <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">"Seuraava"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Lisää"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Tauko"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Odota"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Poista"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Rivinvaihto"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Asetukset"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Välilyönti"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Symbolit"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Sarkain"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Äänisyöte"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Symbolit käytössä"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Symbolit pois käytöstä"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift käytössä"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift pois käytöstä"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Äänisyöte"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Äänisyötettä ei vielä tueta kielelläsi, mutta voit käyttää sitä englanniksi."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Äänisyöte käyttää Googlen puheentunnistusta. "<a href="http://m.google.com/privacy">"Mobile-tietosuojakäytäntö"</a>" on voimassa."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Korjaa annetut sanat napauttamalla. (Vain, kun ehdotuksia on näkyvillä.)"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Näppäimistön teema"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Näppäimistö: tšekki"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arabiankielinen näppäimistö"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Näppäimistö: tanska"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Näppäimistö: saksa"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Näppäimistö: englanti (UK)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Näppäimistö: englanti (US)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Näppäimistö: espanja"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Näppäimistö: ranska"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Näppäimistö: ranska (Kanada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Näppäimistö: ranska (Sveitsi)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Hepreankielinen näppäimistö"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Näppäimistö: italia"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Näppäimistö: norja"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Näppäimistö: hollanti"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Näppäimistö: puola"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Näppäimistö: portugali"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Näppäimistö: venäjä"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Näppäimistö: serbia"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Näppäimistö: ruotsi"</string>
diff --git a/java/res/values-fr/donottranslate-altchars.xml b/java/res/values-fr/donottranslate-altchars.xml
index e01f63f..ae9292f 100644
--- a/java/res/values-fr/donottranslate-altchars.xml
+++ b/java/res/values-fr/donottranslate-altchars.xml
@@ -18,11 +18,11 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">1,à,â,æ,á,ä,ã,å,ā,ª</string>
-    <string name="alternates_for_e">3,é,è,ê,ë,ę,ė,ē</string>
-    <string name="alternates_for_i">8,î,ï,ì,í,į,ī</string>
-    <string name="alternates_for_o">9,ô,œ,ö,ò,ó,õ,ø,ō,º</string>
-    <string name="alternates_for_u">7,û,ù,ü,ú,ū</string>
+    <string name="alternates_for_a">à,â,1,æ,á,ä,ã,å,ā,ª</string>
+    <string name="alternates_for_e">é,è,ê,ë,3,ę,ė,ē</string>
+    <string name="alternates_for_i">î,8,ï,ì,í,į,ī</string>
+    <string name="alternates_for_o">ô,œ,9,ö,ò,ó,õ,ø,ō,º</string>
+    <string name="alternates_for_u">ù,û,7,ü,ú,ū</string>
     <string name="alternates_for_c">ç,ć,č</string>
     <string name="alternates_for_y">6,ÿ</string>
     <string name="alternates_for_q"></string>
diff --git a/java/res/values-fr/donottranslate.xml b/java/res/values-fr/donottranslate.xml
index 6c35362..09c37e3 100644
--- a/java/res/values-fr/donottranslate.xml
+++ b/java/res/values-fr/donottranslate.xml
@@ -18,8 +18,12 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Symbols that are commonly considered word separators in this language -->
-    <string name="word_separators">.\u0009\u0020,;:!?\n()[]*&amp;@{}/&lt;&gt;_+=|\u0022</string>
-    <!-- Symbols that are sentence separators, for purposes of making it hug the last sentence. -->
-    <string name="sentence_separators">.,</string>
+    <!-- Symbols that should be swapped with a magic space -->
+    <string name="magic_space_swapping_symbols">.,\u0022)]}</string>
+    <!-- Symbols that should strip a magic space -->
+    <string name="magic_space_stripping_symbols">\u0009\u0020\u0027\n-/_</string>
+    <!-- Symbols that should promote magic spaces into real space -->
+    <string name="magic_space_promoting_symbols">;:!?([*&amp;@{&lt;&gt;+=|</string>
+    <!-- Symbols that do NOT separate words -->
+    <string name="non_word_separator_symbols">\u0027</string>
 </resources>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 59a1387..2f09277 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Son à chaque touche"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Agrandir les caractères"</string>
     <string name="general_category" msgid="1859088467017573195">"Général"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Correction du texte"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Correction du texte"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Suggestions basées sur les mots précédents"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Autres options"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Paramètres avancés"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Options destinées aux utilisateurs expérimentés"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Délai masq. touche pop-up"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Aucun délai"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Par défaut"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Majuscules auto"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Corrections rapides"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Corrige les fautes de frappe courantes"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Toujours afficher"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Afficher en mode Portrait"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Toujours masquer"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Barre d\'espace pour changer langue"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Afficher touche param."</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatique"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Toujours afficher"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Désactiver"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Simple"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Proactive"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Suggestions de type bigramme"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Suggestions de type bigramme"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Améliorer la suggestion en fonction du mot précédent"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Prédiction bigramme"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utiliser le mot précédent pour la prédiction"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : enregistré"</string>
     <string name="label_go_key" msgid="1635148082137219148">"OK"</string>
     <string name="label_next_key" msgid="362972844525672568">"Suivant"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Plus"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pause"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Attente"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Supprimer"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Entrée"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Paramètres"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Maj"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Espace"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Symboles"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tabulation"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Saisie vocale"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Symboles activés"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Symboles désactivés"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Maj activée"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Maj désactivée"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Saisie vocale"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"La saisie vocale n\'est pas encore prise en charge pour votre langue, mais elle fonctionne en anglais."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"La saisie vocale fait appel à la reconnaissance vocale de Google. Les "<a href="http://m.google.com/privacy">"Règles de confidentialité Google Mobile"</a>" s\'appliquent."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Appuyer sur les mots saisis pour les corriger, uniquement lorsque des suggestions sont visibles"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Thème du clavier"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Clavier tchèque"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Clavier arabe"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Clavier danois"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Clavier allemand"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Clavier anglais (Royaume-Uni)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Clavier anglais (États-Unis)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Clavier espagnol"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Clavier français"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Clavier français (Canada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Clavier français (Suisse)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Clavier hébreu"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Clavier italien"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Clavier norvégien"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Clavier néerlandais"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Clavier polonais"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Clavier portugais"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Clavier russe"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Clavier serbe"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Clavier suédois"</string>
diff --git a/java/res/drawable/btn_candidate_holo.xml b/java/res/values-hdpi/config.xml
similarity index 69%
copy from java/res/drawable/btn_candidate_holo.xml
copy to java/res/values-hdpi/config.xml
index 66cd246..7333e94 100644
--- a/java/res/drawable/btn_candidate_holo.xml
+++ b/java/res/values-hdpi/config.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** 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.
@@ -18,10 +18,7 @@
 */
 -->
 
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android"
->
-    <item
-        android:state_pressed="true"
-        android:drawable="@drawable/btn_keyboard_key_popup_selected_holo" />
-</selector>
+<resources>
+    <!--  Screen metrics for logging. 0 = "mdpi", 1 = "hdpi", 2 = "xlarge" -->
+    <integer name="log_screen_metrics">1</integer>
+</resources>
diff --git a/java/res/layout-sw600dp/candidate_preview.xml b/java/res/values-hr/donottranslate-altchars.xml
similarity index 62%
rename from java/res/layout-sw600dp/candidate_preview.xml
rename to java/res/values-hr/donottranslate-altchars.xml
index 3ef2e6e..d0c9d40 100644
--- a/java/res/layout-sw600dp/candidate_preview.xml
+++ b/java/res/values-hr/donottranslate-altchars.xml
@@ -17,13 +17,11 @@
 ** limitations under the License.
 */
 -->
-
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:textSize="18sp"
-    android:textColor="?android:attr/textColorPrimaryInverse"
-    android:minWidth="32dip"
-    android:gravity="center"
-    android:background="@drawable/keyboard_popup_panel_background_holo" />
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="alternates_for_s">š,ś,ß</string>
+    <string name="alternates_for_n">ñ,ń</string>
+    <string name="alternates_for_y"></string>
+    <string name="alternates_for_z">6,ž,ź,ż</string>
+    <string name="alternates_for_c">č,ć,ç</string>
+    <string name="alternates_for_d">đ</string>
+</resources>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index 1ff4ba2..b3df4ed 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Zvuk pri pritisku tipke"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Povećanja na pritisak tipke"</string>
     <string name="general_category" msgid="1859088467017573195">"Općenito"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Ispravak teksta"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Ispravak teksta"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Prijedlozi na temelju prethodnih riječi"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Ostale opcije"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Napredne postavke"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Opcije za stručne korisnike"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Bez odgode klj. skočnih"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez odgode"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Zadano"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Automatsko pisanje velikih slova"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Brzi popravci"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Ispravlja uobičajene pogreške u pisanju"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Uvijek prikaži"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Prikaži u portretnom načinu"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Uvijek sakrij"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Upotrijebite razmaknicu za prebacivanje jezika"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Prikaži tipku postavki"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatski"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Uvijek prikaži"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Isključeno"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Skromno"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivno"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigram prijedlozi"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigram prijedlozi"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Upotrijebi prethodnu riječ radi poboljšanja prijedloga"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigram predviđanje"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Upotrijebite prethodnu riječ i za predviđanje"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Spremljeno"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Idi"</string>
     <string name="label_next_key" msgid="362972844525672568">"Dalje"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Više"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pauza"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Pričekaj"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Delete"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Enter"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Postavke"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Razmaknica"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Simboli"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tabulator"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Glasovni unos"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Simboli uključeni"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Simboli isključeni"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift uključen"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift isključen"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Glasovni ulaz"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Vaš jezik trenutno nije podržan za glasovni unos, ali radi za engleski."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Glasovni unos upotrebljava Googleovo prepoznavanje govora. Primjenjuju se "<a href="http://m.google.com/privacy">"Pravila o privatnosti za uslugu Mobile"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Dodirnite unesene riječi da biste ih ispravili samo kada su prijedlozi vidljivi"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tema tipkovnice"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Češka tipkovnica"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arapska tipkovnica"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Danska tipkovnica"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Njemačka tipkovnica"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Engleska (UK) tipkovnica"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Engleska (SAD) tipkovnica"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Španjolska tipkovnica"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Francuska tipkovnica"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Francuska (Kanada) tipkovnica"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Francuska (Švicarska) tipkovnica"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Hebrejska tipkovnica"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Talijanska tipkovnica"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Norveška tipkovnica"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Nizozemska tipkovnica"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Poljska tipkovnica"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portugalska tipkovnica"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Ruska tipkovnica"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Srpska tipkovnica"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Švedska tipkovnica"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 923103b..aed535a 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Hangjelzés billentyű megnyomása esetén"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Legyen nagyobb billentyű lenyomásakor"</string>
     <string name="general_category" msgid="1859088467017573195">"Általános"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Szövegjavítás"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Szövegjavítás"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Javaslatok korábbi szavak alapján"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Egyéb beállítások"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Speciális beállítások"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Beállítások gyakorlott felhasználóknak"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Gombeltüntetés késése"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Nincs késés"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Alapbeállítás"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Automatikusan nagy kezdőbetű"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Gyorsjavítások"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Kijavítja a gyakori gépelési hibákat"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mindig látszik"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Megjelenítés álló tájolásban"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Mindig rejtve"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Nyelvváltó: szóköz"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Beállítások billentyű megjelenítése"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatikus"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mindig látszik"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Ki"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mérsékelt"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresszív"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigram javaslatok"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigram javaslatok"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Előző szó használata a javaslatok javításához"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigram előrejelzés"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Az előző szó használata a prediktív bevitelhez is"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : mentve"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Ugrás"</string>
     <string name="label_next_key" msgid="362972844525672568">"Tovább"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Egyebek"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Szün."</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Vár"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Törlés"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Vissza"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Beállítások"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Szóköz"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Szimbólumok"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Hangbevitel"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Szimbólumok be"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Szimbólumok ki"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift be"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift ki"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Hangbevitel"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"A hangbevitel szolgáltatás jelenleg nem támogatja az Ön nyelvét, ám angolul működik."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"A hangbevitel a Google beszédfelismerő technológiáját használja, amelyre a "<a href="http://m.google.com/privacy">"Mobil adatvédelmi irányelvek"</a>" érvényesek."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"A beírt szavakat csak akkor javíthatja ki megérintve, ha látszanak javaslatok"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Billentyűzettéma"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Cseh billentyűzet"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arab billentyűzet"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Dán billentyűzet"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Német billentyűzet"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Angol (UK) billentyűzet"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Angol (US) billentyűzet"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Spanyol billentyűzet"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Francia billentyűzet"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Francia (kanadai) billentyűzet"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Francia (svájci) billentyűzet"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Héber billentyűzet"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Olasz billentyűzet"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Norvég billentyűzet"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Holland billentyűzet"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Lengyel billentyűzet"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portugál billentyűzet"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Orosz billentyűzet"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Szerb billentyűzet"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Svéd billentyűzet"</string>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index 3322109..97706d0 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Berbunyi jika tombol ditekan"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Muncul saat tombol ditekan"</string>
     <string name="general_category" msgid="1859088467017573195">"Umum"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Koreksi teks"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Koreksi teks"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Saran berdasarkan kata sebelumnya"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Opsi lain"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Setelan lanjutan"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Pilihan untuk pengguna ahli"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tundaan singkir munculan kunci"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Tanpa penundaan"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Bawaan"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Kapitalisasi otomatis"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Perbaikan cepat"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Memperbaiki kesalahan ketik umum"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Selalu tampilkan"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Tampilkan pada mode potret"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Selalu sembunyikan"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Gunakan pengalih bahasa bilah spasi"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Lihat tombol setelan"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Otomatis"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Selalu tampilkan"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Mati"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Sederhana"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresif"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Saran Bigram"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Saran bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Gunakan kata sebelumnya untuk meningkatkan sara"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Prediksi bigram"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gunakan kata sebelumnya juga untuk prediksi"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Telah disimpan"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Buka"</string>
     <string name="label_next_key" msgid="362972844525672568">"Berikutnya"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Lainnya"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Jeda"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Tunggu"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Hapus"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Enter"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Setelan"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Spasi"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Simbol"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Masukan Suara"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Simbol hidup"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Simbol mati"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift hidup"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift mati"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Masukan suara"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Masukan suara saat ini tidak didukung untuk bahasa Anda, tetapi bekerja dalam Bahasa Inggris."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Masukan suara menggunakan pengenalan ucapan Google. "<a href="http://m.google.com/privacy">"Kebijakan Privasi Seluler"</a>" berlaku."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Sentuh kata yang dimasukkan untuk memperbaikinya, hanya saat saran dapat dilihat"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tema Keyboard"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Keyboard Cheska"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Papan Tombol Arab"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Keyboard Denmark"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Keyboard Jerman"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Keyboard Inggris (Britania Raya)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Keyboard Inggris (AS)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Keyboard Spanyol"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Keyboard Prancis"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Keyboard Prancis (Kanada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Keyboard Prancis (Swiss)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Papan tombol Ibrani"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Keyboard Italia"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Keyboard Norwegia"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Keyboard Belanda"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Keyboard bahasa Polski"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Keyboard Portugis"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Keyboard Rusia"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Keyboard Serbia"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Keyboard Swedia"</string>
diff --git a/java/res/values-it/donottranslate.xml b/java/res/values-it/donottranslate.xml
index 3e3f3ef..adb2a9a 100644
--- a/java/res/values-it/donottranslate.xml
+++ b/java/res/values-it/donottranslate.xml
@@ -18,6 +18,6 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Symbols that are commonly considered word separators in this language -->
-    <string name="word_separators">.\u0009\u0020,;:!?\'\n()[]*&amp;@{}/&lt;&gt;_+=|\u0022</string>
+    <!-- Symbols that do NOT separate words -->
+    <string name="non_word_separator_symbols"></string>
 </resources>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 337a04e..eb902e0 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Suono tasti"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Popup sui tasti"</string>
     <string name="general_category" msgid="1859088467017573195">"Generali"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Correzione testo"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Correzione testo"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Suggerimenti in base alle parole precedenti"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Altre opzioni"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Impostazioni avanzate"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Opzioni per utenti esperti"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ritardo eliminaz. popup tasto"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Nessun ritardo"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinito"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Maiuscole automatiche"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Correzioni veloci"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Corregge gli errori di digitazione più comuni"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostra sempre"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostra in modalità verticale"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Nascondi sempre"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Selettore lingua da barra spaziatrice"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Mostra tasto impostaz."</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatico"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mostra sempre"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Off"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Media"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Massima"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Suggerimenti sui bigrammi"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Suggerimenti sui bigrammi"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Utilizza parola precedente per migliorare il suggerimento"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Previsione bigramma"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Usa anche la parola precedente per la previsione"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : parola salvata"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Vai"</string>
     <string name="label_next_key" msgid="362972844525672568">"Avanti"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Altro"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Attesa"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Cancella"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Invio"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Impostazioni"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Maiuscolo"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Spazio"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Simboli"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tabulazione"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Input vocale"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Simboli attivati"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Simboli disattivati"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Maiuscole attivate"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Maiuscole disattivate"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Comandi vocali"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"I comandi vocali non sono attualmente supportati per la tua lingua ma funzionano in inglese."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"L\'input vocale utilizza il riconoscimento vocale di Google. Sono valide le "<a href="http://m.google.com/privacy">"norme sulla privacy di Google Mobile"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Tocca le parole inserite per correggerle, solo quando sono visibili i suggerimenti"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tema della tastiera"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Tastiera ceca"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Tastiera araba"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Tastiera danese"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Tastiera tedesca"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Tastiera inglese (Regno Unito)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Tastiera inglese (Stati Uniti)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Tastiera spagnola"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Tastiera francese"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Tastiera francese (Canada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Tastiera francese (Svizzera)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Tastiera ebraica"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Tastiera italiana"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Tastiera norvegese"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Tastiera olandese"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Tastiera polacca"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Tastiera portoghese"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Tastiera russa"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Tastiera serba"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Tastiera svedese"</string>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index e71ab56..c453510 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"צלילים עם לחיצה על מקשים"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"חלון קופץ עם לחיצה על מקשים"</string>
     <string name="general_category" msgid="1859088467017573195">"כללי"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"תיקון טקסט"</string>
+    <string name="correction_category" msgid="2236750915056607613">"תיקון טקסט"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"הצעות המבוססות על מילים קודמות"</string>
+    <string name="misc_category" msgid="6894192814868233453">"אפשרויות אחרות"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"הגדרות מתקדמות"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"אפשרויות עבור משתמשים מתקדמים"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"עיכוב דחייה של מוקפץ ראשי"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ללא עיכוב"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ברירת מחדל"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"הפיכה אוטומטית של אותיות לרישיות"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"תיקונים מהירים"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"מתקן שגיאות הקלדה נפוצות"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"הצג תמיד"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"הצג בפריסה לאורך"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"הסתר תמיד"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"השתמש במחליף השפה שבמקש הרווח"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"הצג מקש הגדרות"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"אוטומטי"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"הצג תמיד"</string>
@@ -45,8 +57,10 @@
     <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>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"הצעות של צמדי אותיות (Bigram)"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"הצעות של צמדי אותיות (Bigram)"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"השתמש במילה הקודמת כדי לשפר את ההצעה"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"חיזוי צמדי אותיות (Bigram)"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"השתמש במילה הקודמת גם עבור חיזוי"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : נשמרה"</string>
     <string name="label_go_key" msgid="1635148082137219148">"בצע"</string>
     <string name="label_next_key" msgid="362972844525672568">"הבא"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"עוד"</string>
     <string name="label_pause_key" msgid="181098308428035340">"השהה"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"המתן"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"מחק"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"חזור"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"הגדרות"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"רווח"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"סמלים"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"כרטיסייה"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"קלט קולי"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"מצב סמלים פועל"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"מצב סמלים כבוי"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift פועל"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift כבוי"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"קלט קולי"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"קלט קולי אינו נתמך בשלב זה בשפתך, אך הוא פועל באנגלית."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"קלט קולי משתמש בזיהוי דיבור של Google.‏ "<a href="http://m.google.com/privacy">"מדיניות הפרטיות של \'Google לנייד\'"</a>" חלה במקרה זה."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"גע במילים שהוזנו כדי לתקן אותן, רק כאשר הצעות מוצגות"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"עיצוב מקלדת"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"מקלדת צ\'כית"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"מקלדת בשפה הערבית"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"מקלדת דנית"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"מקלדת גרמנית "</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"מקלדת אנגלית (בריטניה)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"מקלדת אנגלית (ארה\"ב)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"מקלדת ספרדית"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"מקלדת צרפתית"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"מקלדת צרפתית (קנדה)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"מקלדת צרפתית (שוויץ)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"מקלדת בשפה העברית"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"מקלדת איטלקית"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"מקלדת נורווגית"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"מקלדת הולנדית"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"מקלדת פולנית"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"מקלדת פורטוגזית"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"מקלדת רוסית"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"מקלדת סרבית"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"מקלדת שוודית"</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 5d82a45..2aa55a7 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"キー操作音"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"キー押下時ポップアップ"</string>
     <string name="general_category" msgid="1859088467017573195">"全般"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"テキストの修正"</string>
+    <string name="correction_category" msgid="2236750915056607613">"テキストの修正"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"前の語句に基づいた入力候補表示"</string>
+    <string name="misc_category" msgid="6894192814868233453">"他のオプション"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"詳細設定"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"上級ユーザー向けオプション"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"キーのポップアップ時間"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"すぐに消去"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"デフォルト"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"自動大文字変換"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"クイックフィックス"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"よくある誤字・脱字を修正します"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"常に表示"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"縦向きで表示"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"常に非表示"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"スペースバーで切替"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"設定キーを表示"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"自動"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"常に表示"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"OFF"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"中"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"強"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"バイグラム入力候補表示"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"バイグラム入力候補表示"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"直前の単語から入力候補を予測します"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"バイグラム予測"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"前の語句も予測に使用"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:保存しました"</string>
     <string name="label_go_key" msgid="1635148082137219148">"実行"</string>
     <string name="label_next_key" msgid="362972844525672568">"次へ"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Shift"</string>
     <string name="label_pause_key" msgid="181098308428035340">"停止"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"待機"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Del"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Enter"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"設定"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Space"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"記号"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"音声入力"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"記号ON"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"記号OFF"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift ON"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift OFF"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"音声入力"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"音声入力は現在英語には対応していますが、日本語には対応していません。"</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"音声入力ではGoogleの音声認識技術を利用します。"<a href="http://m.google.com/privacy">"モバイルプライバシーポリシー"</a>"が適用されます。"</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"候補が表示されているときのみ、入力した語句をタップして修正する"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"キーボードテーマ"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"チェコ語のキーボード"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"アラビア語のキーボード"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"デンマーク語のキーボード"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"ドイツ語のキーボード"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"英語(英国)のキーボード"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"英語(米国)のキーボード"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"スペイン語のキーボード"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"フランス語のキーボード"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"フランス語(カナダ)のキーボード"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"フランス語(スイス)のキーボード"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"ヘブライ語のキーボード"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"イタリア語のキーボード"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"ノルウェー語のキーボード"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"オランダ語のキーボード"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"ポーランド語のキーボード"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"ポルトガル語のキーボード"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"ロシア語のキーボード"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"セルビア語のキーボード"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"スウェーデン語のキーボード"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 2cf0ec2..8f47478 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"키를 누를 때 소리 발생"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"키를 누를 때 팝업"</string>
     <string name="general_category" msgid="1859088467017573195">"일반"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"텍스트 수정"</string>
+    <string name="correction_category" msgid="2236750915056607613">"텍스트 수정"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"이전 단어에 기반한 추천"</string>
+    <string name="misc_category" msgid="6894192814868233453">"기타 옵션"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"고급 설정"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"전문 사용자용 옵션"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"키 팝업 해제 지연"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"지연 없음"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"기본값"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"자동 대문자화"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"빠른 수정"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"자주 발생하는 오타를 수정합니다."</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"항상 표시"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"세로 모드로 표시"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"항상 숨기기"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"스페이스 바 언어 교환기 사용"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"설정 키 표시"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"자동"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"항상 표시"</string>
@@ -45,8 +57,10 @@
     <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>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigram 추천"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigram 추천"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"이전 단어를 사용하여 추천 기능 개선"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigram 예측"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"이전 단어를 사용하여 예상 검색어를 표시합니다."</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: 저장됨"</string>
     <string name="label_go_key" msgid="1635148082137219148">"이동"</string>
     <string name="label_next_key" msgid="362972844525672568">"다음"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"더보기"</string>
     <string name="label_pause_key" msgid="181098308428035340">"일시 중지"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"대기"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"삭제"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"리턴"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"설정"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"시프트"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"스페이스"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"기호"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"탭"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"음성 입력"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"기호 사용"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"기호 사용 안함"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"시프트 사용"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"시프트 사용 안함"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"음성 입력"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"음성 입력은 현재 자국어로 지원되지 않으며 영어로 작동됩니다."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"음성 입력에서는 Google의 음성 인식 기능을 사용합니다. "<a href="http://m.google.com/privacy">"모바일 개인정보취급방침"</a>"이 적용됩니다."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"입력한 단어를 터치하여 수정(추천 단어가 표시되는 경우에만)"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"키보드 테마"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"체코어 키보드"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"아랍어 키보드"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"덴마크어 키보드"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"독일어 키보드"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"영어(영국) 키보드"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"영어(미국) 키보드"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"스페인어 키보드"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"프랑스어 키보드"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"프랑스어(캐나다) 키보드"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"프랑스어(스위스) 키보드"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"히브리어 키보드"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"이탈리아어 키보드"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"노르웨이어 키보드"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"네덜란드어 키보드"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"폴란드어 키보드"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"포르투갈어 키보드"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"러시아어 키보드"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"세르비아어 키보드"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"스웨덴어 키보드"</string>
diff --git a/java/res/values-land/dimens.xml b/java/res/values-land/dimens.xml
index 7df124b..b374227 100644
--- a/java/res/values-land/dimens.xml
+++ b/java/res/values-land/dimens.xml
@@ -19,20 +19,31 @@
 -->
 
 <resources>
-    <!-- keyboardHeight = key_height*4 + key_bottom_gap*3 -->
-    <dimen name="keyboardHeight">1.060in</dimen>
+    <!-- keyboardHeight = key_height*4 + key_bottom_gap*3, key_height=0.260in -->
+    <dimen name="keyboardHeight">1.100in</dimen>
+    <fraction name="minKeyboardHeight">45%p</fraction>
     <!-- key_height + key_bottom_gap = popup_key_height -->
-<!--    <dimen name="key_height">0.250in</dimen>-->
+<!--    <dimen name="key_height">0.260in</dimen>-->
     <dimen name="key_bottom_gap">0.020in</dimen>
-    <dimen name="popup_key_height">0.270in</dimen>
-    <dimen name="keyboard_top_padding">0.0in</dimen>
-    <dimen name="keyboard_bottom_padding">0.0in</dimen>
+    <dimen name="popup_key_height">0.280in</dimen>
+    <dimen name="keyboard_top_padding">0.00in</dimen>
+    <dimen name="keyboard_bottom_padding">0.00in</dimen>
+    <dimen name="key_bottom_gap_ics">0.04in</dimen>
+    <dimen name="key_horizontal_gap_ics">0.01in</dimen>
+    <dimen name="keyboard_top_padding_ics">0.03in</dimen>
+    <dimen name="keyboard_bottom_padding_ics">0.00in</dimen>
+    <dimen name="keyboard_horizontal_edges_padding">0.0in</dimen>
+    <fraction name="key_letter_ratio">55%</fraction>
+    <fraction name="key_label_ratio">35%</fraction>
+    <fraction name="key_hint_letter_ratio">28%</fraction>
+    <fraction name="key_uppercase_letter_ratio">40%</fraction>
+    <dimen name="key_preview_offset">0.08in</dimen>
+    <fraction name="key_preview_text_ratio">90%</fraction>
     <dimen name="candidate_strip_height">38dip</dimen>
     <dimen name="candidate_strip_fading_edge_length">63dip</dimen>
-    <dimen name="spacebar_vertical_correction">2dip</dimen>
     <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. -->
     <!-- popup_key_height x 1.2 -->
-    <dimen name="mini_keyboard_slide_allowance">0.324in</dimen>
+    <dimen name="mini_keyboard_slide_allowance">0.336in</dimen>
     <!-- popup_key_height x -1.0 -->
-    <dimen name="mini_keyboard_vertical_correction">-0.270in</dimen>
+    <dimen name="mini_keyboard_vertical_correction">-0.280in</dimen>
 </resources>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index 3ff1543..ae32914 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Klavišo paspaudimo garsas"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Iššoka paspaudus klavišą"</string>
     <string name="general_category" msgid="1859088467017573195">"Bendra"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Teksto taisymas"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Teksto taisymas"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Pasiūlymai pagal ankstesnius žodžius"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Kitos parinktys"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Išplėstiniai nustatymai"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Parinktys ekspertams"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Pagr. išš. l. atsis. d."</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Be delsos"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Numatytasis"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Automatinis didžiųjų raidžių rašymas"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Greiti pataisymai"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Taiso dažnai padarytas rašybos klaidas"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Visada rodyti"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Rodyti stačiuoju režimu"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Visada slėpti"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Naud. tarpo kl. k. jung."</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Rodyti nustatymų raktą"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatinis"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Visada rodyti"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Išjungta"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Vidutinis"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Atkaklus"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Digramų pasiūlymai"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigramų pasiūlymai"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Naudoti ankstesnį žodį pasiūlymui patobulinti"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigramų numatymas"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Numatant naudoti ir ankstesnį žodį"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: išsaugota"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Pradėti"</string>
     <string name="label_next_key" msgid="362972844525672568">"Kitas"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Daugiau"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Prist."</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Lauk."</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Ištrinti"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Grįžti"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Nustatymai"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Keitimas"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Tarpas"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Simboliai"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Skirtukas"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Balso įvestis"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Simboliai įjungti"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Simboliai išjungti"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Keitimas įjungtas"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Keitimas išjungtas"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Balso įvestis"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Šiuo metu balso įvestis jūsų kompiuteryje nepalaikoma, bet ji veikia anglų k."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Balso įvesčiai naudojamas „Google“ kalbos atpažinimas. Taikoma "<a href="http://m.google.com/privacy">"privatumo politika mobiliesiems"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Jei norite ištaisyti įvestus žodžius, palieskite juos tik tada, kai matomi pasiūlymai"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Klaviatūros tema"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Čekiška klaviatūra"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arabiška klaviatūra"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Daniška klaviatūra"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Vokiška klaviatūra"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Angliška (JK) klaviatūra"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Angliška (JAV) klaviatūra"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Ispaniška klaviatūra"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Prancūziška klaviatūra"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Prancūziška (Kanada) klaviatūra"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Prancūziška (Šveicarija) klaviatūra"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Hebrajiška klaviatūra"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Itališka klaviatūra"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Norvegiška klaviatūra"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Olandiška klaviatūra"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Lenkiška klaviatūra"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portugališka klaviatūra"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Rusiška klaviatūra"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Serbiška klaviatūra"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Švediška klaviatūra"</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index 11e768f..d455715 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Skaņa, nospiežot taustiņu"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Nospiežot taustiņu, parādīt uznirstošo izvēlni"</string>
     <string name="general_category" msgid="1859088467017573195">"Vispārīgi"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Teksta korekcija"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Teksta korekcija"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Ieteikumi, kuru pamatā ir iepriekšējie vārdi"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Citas opcijas"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Papildu iestatījumi"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Opcijas speciālistiem"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Taust. uzn. loga noraid. aizk."</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez aizkaves"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Noklusējums"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Automātiska lielo burtu lietošana"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Ātrie labojumi"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Nodrošina izplatītu drukas kļūdu labošanu."</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vienmēr rādīt"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Rādīt portreta režīmā"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vienmēr slēpt"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Izmantot valodu pārslēgšanai atstarpēšanas taustiņu"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Rādīt iestatījumu taustiņu"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automātiski"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Vienmēr rādīt"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Izslēgta"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mērena"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresīva"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigram ieteikumi"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigrammu ieteikumi"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Ieteikuma uzlabošanai izmantot iepriekšējo vārdu"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigrammu prognozes"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Izmantot iepriekšējo vārdu arī prognozēm"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: saglabāts"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Sākt"</string>
     <string name="label_next_key" msgid="362972844525672568">"Tālāk"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Vairāk"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pauze"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Gaidīt"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Dzēšanas taustiņš"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Atgriešanās taustiņš"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Iestatījumu taustiņš"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Pārslēgšanas taustiņš"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Atstarpes taustiņš"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Simbolu taustiņš"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tabulēšanas taustiņš"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Runas ievades taustiņš"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Simbolu režīms ir ieslēgts."</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Simbolu režīms ir izslēgts."</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Pārslēgšanas režīms ir ieslēgts."</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Pārslēgšanas režīms ir izslēgts."</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Balss ievade"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Balss ievade jūsu valodā pašlaik netiek atbalstīta, taču tā ir pieejama angļu valodā."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Balss ievadei tiek izmantota Google runas atpazīšanas funkcija. Uz šīs funkcijas lietošanu attiecas "<a href="http://m.google.com/privacy">"mobilo sakaru ierīču lietošanas konfidencialitātes politika"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Pieskarties ievadītajiem vārdiem, lai tos labotu (tikai tad, ja tiek rādīti ieteikumi)."</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tastatūras motīvs"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Čehu tastatūra"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arābu tastatūra"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Dāņu tastatūra"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Vācu tastatūra"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Angļu (Lielbritānija) tastatūra"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Angļu (ASV) tastatūra"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Spāņu tastatūra"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Franču tastatūra"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Franču (Kanāda) tastatūra"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Franču (Šveices) tastatūra"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Ebreju tastatūra"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Itāļu tastatūra"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Norvēģu tastatūra"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Holandiešu tastatūra"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Poļu valodas tastatūra"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portugāļu valodas tastatūra"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Krievu tastatūra"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Serbu tastatūra"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Zviedru tastatūra"</string>
diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml
new file mode 100644
index 0000000..532a895
--- /dev/null
+++ b/java/res/values-ms/strings.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Papan kekunci Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Tetapan papan kekunci Android"</string>
+    <string name="english_ime_input_options" msgid="3909945612939668554">"Pilihan input"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Getar pada tekanan kekunci"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Bunyi pada tekanan kekunci"</string>
+    <string name="popup_on_keypress" msgid="123894815723512944">"Pop timbul pada tekanan kunci"</string>
+    <string name="general_category" msgid="1859088467017573195">"Umum"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Pembetulan teks"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Cadangan berdasarkan pada perkataan sebelumnya"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Pilihan lain"</string>
+    <!-- no translation found for advanced_settings (362895144495591463) -->
+    <skip />
+    <!-- no translation found for advanced_settings_summary (5193513161106637254) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_no_delay (2096123151571458064) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
+    <string name="auto_cap" msgid="1719746674854628252">"Huruf besar auto"</string>
+    <string name="quick_fixes" msgid="5353213327680897927">"Pembaikan pantas"</string>
+    <string name="quick_fixes_summary" msgid="3405028402510332373">"Membetulkan kesalahan menaip yang biasa"</string>
+    <string name="prefs_show_suggestions" msgid="8026799663445531637">"Tunjukkan cadangan pembetulan"</string>
+    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Paparkan cadangan perkataan semasa menaip"</string>
+    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Sentiasa tunjukkan"</string>
+    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Tunjukkan pada mod potret"</string>
+    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Sentiasa sembunyikan"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Bar ruang tukar bhs"</string>
+    <string name="prefs_settings_key" msgid="4623341240804046498">"Tunjukkan kekunci tetapan"</string>
+    <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatik"</string>
+    <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Sentiasa tunjukkan"</string>
+    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Sentiasa sembunyikan"</string>
+    <string name="auto_correction" msgid="4979925752001319458">"Auto Pembetulan"</string>
+    <string name="auto_correction_summary" msgid="5625751551134658006">"Bar ruang dan tanda baca secara automatik membetulkan perkataan yang ditaip salah"</string>
+    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Matikan"</string>
+    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Sederhana"</string>
+    <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresif"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Cadangan bigram"</string>
+    <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Gunakan perkataan sebelumnya untuk memperbaik cadangan"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Ramalan bigram"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gunakan juga perkataan sebelumnya untuk ramalan"</string>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Disimpan"</string>
+    <string name="label_go_key" msgid="1635148082137219148">"Pergi"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Seterusnya"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Selesai"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Hantar"</string>
+    <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_more_key" msgid="3760239494604948502">"Lagi"</string>
+    <string name="label_pause_key" msgid="181098308428035340">"Jeda"</string>
+    <string name="label_wait_key" msgid="6402152600878093134">"Tnggu"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Input suara"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Input suara tidak disokong untuk bahasa anda pada masa ini tetapi ia berfungsi dalam bahasa Inggeris."</string>
+    <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Input suara menggunakan pengecaman pertuturan Google. "<a href="http://m.google.com/privacy">"Dasar Polisi Mudah Alih"</a>" digunakan."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="3190378129944934856">"Untuk mematikan input suara, pergi ke tetapan kaedah input."</string>
+    <string name="voice_hint_dialog_message" msgid="1420686286820661548">"Untuk menggunakan input suara, tekan butang mikrofon."</string>
+    <string name="voice_listening" msgid="467518160751321844">"Sebutkan sekarang"</string>
+    <string name="voice_working" msgid="6666937792815731889">"Berfungsi"</string>
+    <string name="voice_initializing" msgid="661962047129906646"></string>
+    <string name="voice_error" msgid="5140896300312186162">"Ralat. Sila cuba lagi."</string>
+    <string name="voice_network_error" msgid="6649556447401862563">"Tidak boleh disambungkan"</string>
+    <string name="voice_too_much_speech" msgid="5746973620134227376">"Ralat, terlalu banyak pertuturan."</string>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Masalah audio"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Ralat pelayan"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Tiada pertuturan didengari"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Tiada padanan ditemui"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Carian suara tidak dipasang"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021">"Petunjuk"<b>":"</b>" Leret merentasi papan kekunci untuk bercakap"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Petunjuk:"</b>" Lain kali, cuba ucapkan tanda baca seperti \"titik\", \"koma\" atau \"tanda soal\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Batal"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Input suara"</string>
+    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Hidpkn kekunci utama"</string>
+    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Pd ppn kekunci simbl"</string>
+    <string name="voice_input_modes_off" msgid="3745699748218082014">"Matikan"</string>
+    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mik. pd kekunci utma"</string>
+    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik. pd kekunci smbl"</string>
+    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Input suara dilmphkn"</string>
+    <string name="selectInputMethod" msgid="315076553378705821">"Pilih kaedah input"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Bahasa input"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Luncurkan jari pada spacebar untuk menukar bahasa"</string>
+    <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Sentuh sekali lagi untuk menyimpan"</string>
+    <string name="has_dictionary" msgid="6071847973466625007">"Kamus tersedia"</string>
+    <string name="prefs_enable_log" msgid="6620424505072963557">"Dayakan maklum balas pengguna"</string>
+    <string name="prefs_description_log" msgid="5827825607258246003">"Bantu memperbaik editor input ini dengan menghantar statistik penggunaan dan laporan runtuhan kepada Google."</string>
+    <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Sentuh untuk membetulkan perkataan"</string>
+    <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Sentuh perkataan yang dimasukkan untuk membetulkannya, hanya apabila cadangan boleh dilihat"</string>
+    <string name="keyboard_layout" msgid="437433231038683666">"Tema Papan Kekunci"</string>
+    <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Papan kekunci Czech"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Papan kekunci bahasa Arab"</string>
+    <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Papan kekunci Denmark"</string>
+    <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Papan kekunci Jerman"</string>
+    <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Papan kekunci Inggeris (UK)"</string>
+    <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Papan kekunci Inggeris (AS)"</string>
+    <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Papan kekunci Sepanyol"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
+    <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Papan kekunci Perancis"</string>
+    <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Papan kekunci Perancis (Kanada)"</string>
+    <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Ppan kekunci Perancis (Switzerland)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Papan kekunci bahasa Ibrani"</string>
+    <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Papan kekunci Itali"</string>
+    <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Papan kekunci Norway"</string>
+    <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Papan kekunci Belanda"</string>
+    <!-- no translation found for subtype_mode_pl_keyboard (2225816414814396047) -->
+    <skip />
+    <!-- no translation found for subtype_mode_pt_keyboard (7503997804861754840) -->
+    <skip />
+    <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Papan kekunci Rusia"</string>
+    <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Papan kekunci Serbia"</string>
+    <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Papan kekunci Sweden"</string>
+    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Suara Bahasa Afrikaans"</string>
+    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Suara Orang Czech"</string>
+    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Suara Orang Jerman"</string>
+    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Suara Bahasa Inggeris"</string>
+    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Suara Orang Sepanyol"</string>
+    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Suara Orang Perancis"</string>
+    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Suara Bahasa Itali"</string>
+    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Suara Orang Jepun"</string>
+    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Suara Orang Korea"</string>
+    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Suara Bahasa Belanda"</string>
+    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Suara Orang Poland"</string>
+    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Suara Orang Portugis"</string>
+    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Suara Orang Rusia"</string>
+    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Suara Orang Turki"</string>
+    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Cina, Suara Bahasa Yue"</string>
+    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Cina, Suara Bahasa Mandarin"</string>
+    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Suara bahasa isiZulu"</string>
+    <string name="prefs_usability_study_mode" msgid="6937813623647419810">"Mod Kajian Kebolehgunaan"</string>
+</resources>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index db163b9..af5dd3c 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Lyd ved tastetrykk"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Hurtigvindu ved tastetrykk"</string>
     <string name="general_category" msgid="1859088467017573195">"Generelt"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Tekstkorrigering"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Tekstkorrigering"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Forslag basert på tidligere ord"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Andre alternativer"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Avanserte innstillinger"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Alternativer for ekspertbrukere"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tregt tastevindu"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"U/ forsinkelse"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Stor forbokstav"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Autokorrektur"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Retter vanlige stavefeil"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vis alltid"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Vis i stående modus"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Skjul alltid"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Skift språk med mellomromstasten"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Vis innstillingsnøkkel"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatisk"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Vis alltid"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Av"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderat"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Omfattende"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigram-forslag"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigram-forslag"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Bruk forrige ord til å forbedre forslaget"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigram-prediksjon"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Bruk forrige ord også for forslag"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Lagret"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Gå"</string>
     <string name="label_next_key" msgid="362972844525672568">"Neste"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Mer"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pause"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Vent"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Delete"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Enter"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Innstillinger"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Mellomrom"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Symboler"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Taleinndata"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Symboler er slått på"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Symboler er slått av"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift på"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift av"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Stemmedata"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Stemmedata håndteres foreløpig ikke på ditt språk, men fungerer på engelsk."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Google Voice bruker Googles talegjenkjenning. "<a href="http://m.google.com/privacy">"Personvernreglene for mobil"</a>" gjelder."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Når forslag er synlige, kan du trykke på ord du har skrevet inn, for å endre dem"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tastaturtema"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Tsjekkisk tastatur"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arabisk tastatur"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Dansk tastatur"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Tysk tastatur"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Engelsk tastatur (Storbritannia)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Engelsk tastatur (USA)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Spansk tastatur"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Fransk tastatur"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Fransk tastatur (Canada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Fransk tastatur (Sveits)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Hebraisk tastatur"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Italiensk tastatur"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Norsk tastatur"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Nederlandsk tastatur"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Polsk tastatur"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portugisisk tastatur"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Russisk tastatur"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Serbisk tastatur"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Svensk tastatur"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index 6dece48..6c4cb13 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Geluid bij toetsaanslag"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up bij toetsaanslag"</string>
     <string name="general_category" msgid="1859088467017573195">"Algemeen"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Tekstcorrectie"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Tekstcorrectie"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Suggesties op basis van eerdere woorden"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Andere opties"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Geavanceerde instellingen"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Opties voor ervaren gebruikers"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Afwijz.vertr. toetspop-up"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Geen vertraging"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standaard"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Auto-hoofdlettergebruik"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Snelle oplossingen"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Hiermee worden veelvoorkomende typefouten gecorrigeerd"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Altijd weergeven"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Weergeven in staande modus"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Altijd verbergen"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Taal schakelen via spatiebalk"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Instellingscode weergeven"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatisch"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Altijd weergeven"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Uitgeschakeld"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Normaal"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressief"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Digram-suggesties"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Digram-suggesties"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Vorig woord gebruiken om suggestie te verbeteren"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Digram-voorspelling"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Het voorgaande woord ook voor voorspelling gebruiken"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: opgeslagen"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Start"</string>
     <string name="label_next_key" msgid="362972844525672568">"Volgende"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Meer"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Onderbr."</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Wacht"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Delete"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Return"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Instellingen"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Spatie"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Symbolen"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Spraakinvoer"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Symbolen aan"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Symbolen uit"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift aan"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift uit"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Spraakinvoer"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Spraakinvoer wordt momenteel niet ondersteund in uw taal, maar is wel beschikbaar in het Engels."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Spraakinvoer maakt gebruik van de spraakherkenning van Google. Het "<a href="http://m.google.com/privacy">"Privacybeleid van Google Mobile"</a>" is van toepassing."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Ingevoerde woorden aanraken om ze te verbeteren, alleen mogelijk wanneer suggesties zichtbaar zijn"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Toetsenbordthema"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Tsjechisch toetsenbord"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arabisch toetsenbord"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Deens toetsenbord"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Duits toetsenbord"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Engels toetsenbord (VK)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Engels toetsenbord (VS)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Spaans toetsenbord"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Frans toetsenbord"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Frans toetsenbord (Canada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Frans toetsenbord (Zwitserland)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Hebreeuws toetsenbord"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Italiaans toetsenbord"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Noors toetsenbord"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Nederlands toetsenbord"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Pools toetsenbord"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portugees toetsenbord"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Russisch toetsenbord"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Servisch toetsenbord"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Zweeds toetsenbord"</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index 70c4b18..2c39c53 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Dźwięk przy naciśnięciu"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Powiększ po naciśnięciu"</string>
     <string name="general_category" msgid="1859088467017573195">"Ogólne"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Korekta tekstu"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Poprawianie tekstu"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Podpowiedzi na podstawie wcześniejszych słów"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Inne opcje"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Ustawienia zaawansowane"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Opcje dla zaawansowanych użytkowników"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Opóźnienie wyłączenia wyskakującego okienka"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez opóźnienia"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Wartość domyślna"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Wstawiaj wielkie litery"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Szybkie poprawki"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Poprawia częste błędy wpisywania"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Zawsze pokazuj"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Pokaż w trybie pionowym"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Zawsze ukrywaj"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Spacja przełącza język"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Pokaż klawisz ustawień"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatycznie"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Zawsze pokazuj"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Wyłącz"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Umiarkowana"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresywna"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Sugestie dla bigramów"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Podpowiadanie dwuznaków"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Używaj poprzedniego wyrazu, aby polepszyć sugestię"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Przewidywanie dwuznaków"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Przewiduj również na podstawie poprzedniego słowa"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Zapisano"</string>
     <string name="label_go_key" msgid="1635148082137219148">"OK"</string>
     <string name="label_next_key" msgid="362972844525672568">"Dalej"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Więcej"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pauza"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Czekaj"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Delete"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Enter"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Ustawienia"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Spacja"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Symbole"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Wprowadzanie głosowe"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Symbole włączone"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Symbole wyłączone"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift włączony"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift wyłączony"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Wprowadzanie głosowe"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Wprowadzanie głosowe obecnie nie jest obsługiwane w Twoim języku, ale działa w języku angielskim."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Funkcja wprowadzania głosowego wykorzystuje mechanizm rozpoznawania mowy. Obowiązuje "<a href="http://m.google.com/privacy">"Polityka prywatności Google Mobile"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Dotykaj wprowadzonych słów, aby je poprawiać tylko wówczas, gdy widoczne są sugestie."</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Motyw klawiatury"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Klawiatura czeska"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Klawiatura arabska"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Klawiatura duńska"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Klawiatura niemiecka"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Klawiatura angielska (UK)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Klawiatura angielska (USA)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Klawiatura hiszpańska"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Klawiatura francuska"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Klawiatura francuska (Kanada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Klawiatura francuska (Szwajcaria)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Klawiatura hebrajska"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Klawiatura włoska"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Klawiatura norweska"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Klawiatura holenderska"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Klawiatura polska"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Klawiatura portugalska"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Klawiatura rosyjska"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Klawiatura serbska"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Klawiatura szwedzka"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index 5845ebc..cae6f49 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Som ao premir as teclas"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Mostrar popup ao premir tecla"</string>
     <string name="general_category" msgid="1859088467017573195">"Geral"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Correcção de texto"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Correção de texto"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Sugestões baseadas em palavras anteriores"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Outras opções"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Definições avançadas"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Opções para utilizadores experientes"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Atraso p/ ignorar pop-up"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sem atraso"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinido"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Letras maiúsculas automáticas"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Correcções rápidas"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Corrige os erros de escrita comuns"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar sempre"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostrar no modo de retrato"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ocultar sempre"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Utilizar barra esp. alt. idioma"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Mostrar tecla das definições"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automático"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mostrar sempre"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desligar"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderada"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressiva"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Sugestões Bigram"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Sugestões Bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Utilizar a palavra anterior para melhorar a sugestão"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Predição Bigram"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utilizar a palavra anterior também para predição"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
     <string name="label_next_key" msgid="362972844525672568">"Seguinte"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Mais"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Esp."</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Delete"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Enter"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Definições"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Espaço"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Símbolos"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Entrada de voz"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Símbolos ativados"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Símbolos desativados"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift ativado"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift desativado"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Entrada de voz"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Actualmente, a entrada de voz não é suportada para o seu idioma, mas funciona em inglês."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"A entrada de voz utiliza o reconhecimento de voz da Google. É aplicável a "<a href="http://m.google.com/privacy">"Política de privacidade do Google Mobile"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Tocar nas palavras introduzidas para as corrigir, apenas quando as sugestões estiverem visíveis"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tema do teclado"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Teclado checo"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Teclado árabe"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Teclado dinamarquês"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Teclado alemão"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Teclado inglês (Reino Unido)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Teclado inglês (EUA)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Teclado espanhol"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Teclado francês"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Teclado francês (Canadá)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Teclado francês (Suíça)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Teclado hebraico"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Teclado italiano"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Teclado norueguês"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Teclado holandês"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Teclado Polaco"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Teclado Português"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Teclado russo"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Teclado sérvio"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Teclado sueco"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index f1203c6..62c20eb 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Som ao tocar a tecla"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Exibir pop-up ao digitar"</string>
     <string name="general_category" msgid="1859088467017573195">"Geral"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Correção de texto"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Correção de texto"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Sugestões baseadas em palavras anteriores"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Outras opções"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Configurações avançadas"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Opções para usuários experientes"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Dispens. atraso chave princ."</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sem atraso"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Padrão"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Capitaliz. automática"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Reparos rápidos"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Corrige erros comuns de digitação"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar sempre"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostrar em modo retrato"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Sempre ocultar"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Alt. idiomas c/ a barra"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Mostrar tecla de config."</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automático"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mostrar sempre"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desativado"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderado"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressivo"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Sugestões de bigrama"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Sugestões de bigrama"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Usar palavra anterior para melhorar a sugestão"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Previsão de bigrama"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Use também a palavra anterior para prever"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Salvo"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
     <string name="label_next_key" msgid="362972844525672568">"Avançar"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Mais"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Esp."</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Excluir"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Voltar"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Configurações"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Espaço"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Símbolos"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Entrada de texto por voz"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Símbolos ativados"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Símbolos desativados"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift ativado"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift desativado"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Entrada de voz"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"A entrada de voz não é suportada no momento para o seu idioma, mas funciona em inglês."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"A entrada de texto por voz usa o reconhecimento de voz do Google. "<a href="http://m.google.com/privacy">"A política de privacidade para celulares"</a>" é aplicada."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Toque nas palavras digitadas para corrigi-las apenas quando as sugestões estiverem visíveis"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tema do teclado"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Teclado em tcheco"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Teclado árabe"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Teclado para dinamarquês"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Teclado para alemão"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Teclado para inglês (Reino Unido)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Teclado para inglês (EUA)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Teclado para espanhol"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Teclado para francês"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Teclado para francês (Canadá)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Teclado para francês (Suíça)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Teclado hebraico"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Teclado para italiano"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Teclado para norueguês"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Teclado para holandês"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Teclado polonês"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Teclado em português"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Teclado para russo"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Teclado para sérvio"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Teclado para sueco"</string>
diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml
index 9c51cad..ddb40e8 100644
--- a/java/res/values-rm/strings.xml
+++ b/java/res/values-rm/strings.xml
@@ -29,7 +29,26 @@
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up cun smatgar ina tasta"</string>
     <!-- no translation found for general_category (1859088467017573195) -->
     <skip />
-    <!-- outdated translation 7027100625580696660 -->     <string name="prediction_category" msgid="6361242011806282176">"Parameters da las propostas per pleds"</string>
+    <!-- no translation found for correction_category (2236750915056607613) -->
+    <skip />
+    <!-- no translation found for ngram_category (5337109164339320257) -->
+    <skip />
+    <!-- no translation found for misc_category (6894192814868233453) -->
+    <skip />
+    <!-- no translation found for advanced_settings (362895144495591463) -->
+    <skip />
+    <!-- no translation found for advanced_settings_summary (5193513161106637254) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_no_delay (2096123151571458064) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Maiusclas automaticas"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Correcturas sveltas"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Curregia sbagls da tippar currents"</string>
@@ -43,6 +62,8 @@
     <skip />
     <!-- no translation found for prefs_suggestion_visibility_hide_name (6309143926422234673) -->
     <skip />
+    <!-- no translation found for prefs_use_spacebar_language_switch (8828538114550634449) -->
+    <skip />
     <!-- no translation found for prefs_settings_key (4623341240804046498) -->
     <skip />
     <!-- no translation found for settings_key_mode_auto_name (2993460277873684680) -->
@@ -59,8 +80,12 @@
     <skip />
     <!-- no translation found for auto_correction_threshold_mode_aggeressive (3524029103734923819) -->
     <skip />
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Propostas da tip bigram"</string>
+    <!-- outdated translation 1323347224043514969 -->     <string name="bigram_suggestion" msgid="2636414079905220518">"Propostas da tip bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Meglierar la proposta cun agid dal pled precedent"</string>
+    <!-- no translation found for bigram_prediction (8914273444762259739) -->
+    <skip />
+    <!-- no translation found for bigram_prediction_summary (1747261921174300098) -->
+    <skip />
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Memorisà"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Dai"</string>
     <string name="label_next_key" msgid="362972844525672568">"Vinavant"</string>
@@ -74,30 +99,6 @@
     <skip />
     <!-- no translation found for label_wait_key (6402152600878093134) -->
     <skip />
-    <!-- no translation found for description_delete_key (5586406298531883960) -->
-    <skip />
-    <!-- no translation found for description_return_key (8750044000806461678) -->
-    <skip />
-    <!-- no translation found for description_settings_key (7484527796782969219) -->
-    <skip />
-    <!-- no translation found for description_shift_key (346906866277787836) -->
-    <skip />
-    <!-- no translation found for description_space_key (8512130111575878517) -->
-    <skip />
-    <!-- no translation found for description_switch_alpha_symbol_key (4537975384274405537) -->
-    <skip />
-    <!-- no translation found for description_tab_key (828186583738307137) -->
-    <skip />
-    <!-- no translation found for description_voice_key (3057731675774652754) -->
-    <skip />
-    <!-- no translation found for description_symbols_on (2994366855822840559) -->
-    <skip />
-    <!-- no translation found for description_symbols_off (3209578267079515136) -->
-    <skip />
-    <!-- no translation found for description_shift_on (6983188949895971587) -->
-    <skip />
-    <!-- no translation found for description_shift_off (8553265474523069034) -->
-    <skip />
     <string name="voice_warning_title" msgid="4419354150908395008">"Cumonds vocals"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"\"Cumonds vocals en Vossa lingua na vegnan actualmain betg sustegnids, ma la funcziun è disponibla per englais.\""</string>
     <!-- outdated translation 4611518823070986445 -->     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Ils cumonds vocals èn ina funcziunalitad experimentala che utilisescha la renconuschientscha vocala da rait da Google."</string>
@@ -146,6 +147,8 @@
     <string name="keyboard_layout" msgid="437433231038683666">"Design da la tastatura"</string>
     <!-- no translation found for subtype_mode_cs_keyboard (1141718931112377586) -->
     <skip />
+    <!-- no translation found for subtype_mode_ar_keyboard (2655338636329774995) -->
+    <skip />
     <!-- no translation found for subtype_mode_da_keyboard (1243570804427922104) -->
     <skip />
     <!-- no translation found for subtype_mode_de_keyboard (1990979135959462145) -->
@@ -156,18 +159,30 @@
     <skip />
     <!-- no translation found for subtype_mode_es_keyboard (1775125478866113148) -->
     <skip />
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <!-- no translation found for subtype_mode_fr_keyboard (8016515336759761014) -->
     <skip />
     <!-- no translation found for subtype_mode_fr_CA_keyboard (2628517247158376263) -->
     <skip />
     <!-- no translation found for subtype_mode_fr_CH_keyboard (6742806653181621228) -->
     <skip />
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <!-- no translation found for subtype_mode_iw_keyboard (1787536828253289950) -->
+    <skip />
     <!-- no translation found for subtype_mode_it_keyboard (4934199655425394484) -->
     <skip />
     <!-- no translation found for subtype_mode_nb_keyboard (1175783216100212360) -->
     <skip />
     <!-- no translation found for subtype_mode_nl_keyboard (5090278083256037936) -->
     <skip />
+    <!-- no translation found for subtype_mode_pl_keyboard (2225816414814396047) -->
+    <skip />
+    <!-- no translation found for subtype_mode_pt_keyboard (7503997804861754840) -->
+    <skip />
     <!-- no translation found for subtype_mode_ru_keyboard (1383995915064277943) -->
     <skip />
     <!-- no translation found for subtype_mode_sr_keyboard (5019440799612208168) -->
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index 0a80911..960ced5 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Sunet la apăsarea tastei"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Fereastră pop-up la apăsarea tastei"</string>
     <string name="general_category" msgid="1859088467017573195">"General"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Corectare text"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Corectare text"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Sugestii bazate pe cuvinte anterioare"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Alte opţiuni"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Setări avansate"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Opţiuni pt. utiliz. experţi"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Înt. înch. pop-up esenţ."</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Fără întârziere"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Prestabilit"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalizare"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Remedieri rapide"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Corectează greşelile introduse frecvent"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Afişaţi întotdeauna"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Afişaţi în modul Portret"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ascundeţi întotdeauna"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Spacebar – selector limbă"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Afişaţi tasta setări"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automat"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Afişaţi întotdeauna"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Dezactivată"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderată"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivă"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Sugestii pentru cuvinte de două litere"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Sugestii de tip bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Utilizaţi cuvântul anterior pentru a îmbunătăţi sugestia"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Predicţii de tip bigram"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Se utilizează şi cuvântul precedent pentru predicţii"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: salvat"</string>
     <string name="label_go_key" msgid="1635148082137219148">"OK"</string>
     <string name="label_next_key" msgid="362972844525672568">"Înainte"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Mai multe"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pauză"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Aşt."</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Ştergeţi"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Tasta Enter"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Setări"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Tasta Space"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Simboluri"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tasta Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Intrare vocală"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Simbolurile sunt activate"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Simbolurile sunt dezactivate"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Tasta Shift este activată"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Tasta Shift este dezactivată"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Intrare voce"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Intrarea vocală nu este acceptată în prezent pentru limba dvs., însă funcţionează în limba engleză."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Intrarea vocală utilizează funcţia Google de recunoaştere vocală. Se aplică "<a href="http://m.google.com/privacy">"Politica de confidenţialitate Google Mobil"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Atingeţi cuvintele introduse pentru a le corecta, numai când pot fi văzute sugestii"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Temă pentru tastatură"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Tastatură cehă"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Tastatură arabă"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Tastatură daneză"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Tastatură germană"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Tastatură engleză (Marea Britanie)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Tastatură engleză (S.U.A.)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Tastatură spaniolă"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Tastatură franceză"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Tastatură franceză (Canada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Tastatură franceză (Elveţia)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Tastatură ebraică"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Tastatură italiană"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Tastatură norvegiană"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Tastatură olandeză"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Tastatură poloneză"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Tastatură portugheză"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Tastatură rusă"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Tastatură sârbă"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Tastatură suedeză"</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index 6dab43c..68ec650 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук клавиш"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Увеличение нажатых"</string>
     <string name="general_category" msgid="1859088467017573195">"Общие"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Коррекция текста"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Исправление текста"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Подсказки, основанные на предыдущих словах"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Другие варианты"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Расширенные настройки"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Для опытных пользователей"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Задержка закрытия"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без задержки"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По умолчанию"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Заглавные автоматически"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Быстрое исправление"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Исправлять распространенные опечатки"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Всегда показывать"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Показать вертикально"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Всегда скрывать"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Пробел меняет язык"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Кнопка настроек"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Автоматически"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Всегда показывать"</string>
@@ -45,8 +57,10 @@
     <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>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Биграммные подсказки"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Биграммные подсказки"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Используйте предыдущее слово, чтобы исправить подсказку"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Биграммный прогноз"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Использовать предыдущее слово для прогнозирования"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: сохранено"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Поиск"</string>
     <string name="label_next_key" msgid="362972844525672568">"Далее"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Ещё"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Приостановить"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Подождите"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Клавиша удаления"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Клавиша \"Ввод\""</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Клавиша настроек"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Клавиша верхнего регистра"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Клавиша \"Пробел\""</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Клавиша символов"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Клавиша табуляции"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Клавиша голосового ввода"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Клавиши символов выключены"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Клавиши символов включены"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Верхний регистр включен"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Верхний регистр выключен"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Голосовой ввод"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"В настоящее время функция голосового ввода не поддерживает ваш язык, но вы можете пользоваться ей на английском."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Голосовой ввод использует алгоритмы распознавания речи Google. Действует "<a href="http://m.google.com/privacy">"политика конфиденциальности для мобильных устройств"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Нажмите на слово, чтобы исправить его (при наличии подсказок)"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Вид клавиатуры"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Клавиатура: чешская"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Арабская клавиатура"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Клавиатура: датская"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Клавиатура: немецкая"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Клавиатура: английская (Великобритания)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Клавиатура: английская (США)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Клавиатура: испанская"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Клавиатура: французская"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Клавиатура: французская"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Клавиатура: французская (Швейцария)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Клавиатура на иврите"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Клавиатура: итальянская"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Клавиатура: норвежская"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Клавиатура: голландская"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Польская клавиатура"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Португальская раскладка"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Клавиатура: русская"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Клавиатура: сербская"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Голос: шведский"</string>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index e7a7cd1..3d5dd58 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Zvuk pri stlačení klávesu"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Zobraziť znaky pri stlačení klávesu"</string>
     <string name="general_category" msgid="1859088467017573195">"Všeobecné"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Oprava textu"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Oprava textu"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Návrhy na základe predchádzajúcich slov"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Ďalšie možnosti"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Rozšírené nastavenia"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Možnosti pre skúsených používateľov"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Onesk. zrušenia kľúč. kon. okna"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez oneskorenia"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predvolená"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Veľké písmená automaticky"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Rýchle opravy"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Opravuje najčastejšie chyby pri písaní"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vždy zobrazovať"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Zobraziť v režime na výšku"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vždy skrývať"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Použite medzerník na prepínanie medzi jazykmi"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Zobraziť kláves Nastavenia"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automaticky"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Vždy zobrazovať"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Vypnuté"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mierne"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresívne"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Návrh Bigram"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Návrhy Bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Na zlepšenie návrhu použiť predchádzajúce slovo"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Odhady Bigram"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Použiť predchádzajúce slovo aj pre predpoveď"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Uložené"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Hľadať"</string>
     <string name="label_next_key" msgid="362972844525672568">"Ďalej"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Viac"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pozastaviť"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Čakajte"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Delete"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Return"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Nastavenia"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Medzera"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Symboly"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Hlasový vstup"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Symboly zapnuté"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Symboly vypnuté"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift zapnutý"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift vypnutý"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Hlasový vstup"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Pre váš jazyk aktuálne nie je hlasový vstup podporovaný, ale funguje v angličtine."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Hlasový vstup používa rozpoznávanie hlasu Google. Na používanie hlasového vstupu sa vzťahujú "<a href="http://m.google.com/privacy">"Pravidlá ochrany osobných údajov pre mobilné služby"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Dotykom zadaných slov tieto slová opravíte, musia však byť viditeľné návrhy"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Motív klávesnice"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"klávesnica – čeština"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"klávesnica – arabčina"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"klávesnica – dánčina"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"klávesnica – nemčina"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"klávesnica – angličtina (br.)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"klávesnica – angličtina (am.)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"klávesnica – španielčina"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"klávesnica – francúzština"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"klávesnica – francúzština (Kanada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"klávesnica – francúzština (Švajč.)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"klávesnica – hebrejčina"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"klávesnica – taliančina"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"klávesnica – nórčina"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"klávesnica – holandčina"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Poľská klávesnica"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portugalská klávesnica"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"klávesnica – ruština"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"hlas – srbčina"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"klávesnica – švédčina"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 6bb0b36..a747f4c 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Zvok ob pritisku tipke"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pojavno okno ob pritisku tipke"</string>
     <string name="general_category" msgid="1859088467017573195">"Splošno"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Popravek besedila"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Popravek besedila"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Predlogi, ki temeljijo na prejšnjih besedah"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Druge možnosti"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Dodatne nastavitve"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Možnosti za izkušene uporabnike"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Zakas. okna za zavrnitev"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Brez zamude"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Privzeto"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Samodejne velike začetnice"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Hitri popravki"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Popravi pogoste tipkarske napake"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vedno pokaži"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Pokaži v pokončnem načinu"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vedno skrij"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Preklopite med jeziki s preslednico"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Pokaži tipko za nastavitve"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Samodejno"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Vedno pokaži"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Izklopljeno"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Zmerno"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivno"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigramni predlogi"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigramni predlogi"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Predlog izboljšaj s prejšnjo besedo"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigramsko predvidevanje"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Uporabi prejšnjo besedo tudi za predvidevanje"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: shranjeno"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Pojdi"</string>
     <string name="label_next_key" msgid="362972844525672568">"Naprej"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Več"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Premor"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Čakaj"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Izbriši"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Vračalka"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Nastavitve"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Dvigalka"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Preslednica"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Znaki"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tabulatorka"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Glasovni vnos"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Znaki vklopljeni"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Znaki izklopljeni"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Dvigalka vklopljena"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Dvigalka izklopljena"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Glasovni vnos"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Glasovni vnos trenutno ni podprt v vašem jeziku, deluje pa v angleščini."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Glasovni vnos uporablja Googlovo prepoznavanje govora. Zanj velja "<a href="http://m.google.com/privacy">"pravilnik o zasebnosti za mobilne naprave"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Dotaknite se vnesenih besed in jih popravite, samo ko so predlogi vidni"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tema tipkovnice"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Češka tipkovnica"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arabska tipkovnica"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Danska tipkovnica"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Nemška tipkovnica"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Tipkovnica za britansko angleščino"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Tipkovnica za ameriško angleščino"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Španska tipkovnica"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Francoska tipkovnica"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Tipkovnica za kanadsko francoščino"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Tipkovnica za švicarsko francoščino"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Hebrejska tipkovnica"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Italijanska tipkovnica"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Norveška tipkovnica"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Nizozemska tipkovnica"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Poljska tipkovnica"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portugalska tipkovnica"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Ruska tipkovnica"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Srbska tipkovnica"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Švedska tipkovnica"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 02e7593..025ce91 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук на притисак тастера"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Искачући прозор приликом притиска тастера"</string>
     <string name="general_category" msgid="1859088467017573195">"Опште"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Исправљање текста"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Исправљање текста"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Предлози на основу претходних речи"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Друге опције"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Напредна подешавања"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Опције за искусне кориснике"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Одложи одбац. иск. прозора тастера"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без одлагања"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Подразумевано"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Аутоматски унос великих слова"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Брзе исправке"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Исправља честе грешке у куцању"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Увек прикажи"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Прикажи у усправном режиму"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Увек сакриј"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Користи размак за избор језика"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Прикажи тастер за подешавања"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Аутоматски"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Увек прикажи"</string>
@@ -45,8 +57,10 @@
     <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>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigram предлози"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigram предлози"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Користи претходну реч за побољшање предлога"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigram предвиђање"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Користи претходну реч и за предвиђање"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Сачувано"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Иди"</string>
     <string name="label_next_key" msgid="362972844525672568">"Следеће"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Још"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Паузирај"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Сачекајте"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Delete"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Return"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Подешавања"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Размак"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Симболи"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Гласовни унос"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Симболи су укључени"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Симболи су искључени"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift је укључен"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift је искључен"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Гласовни унос"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Гласовни унос тренутно није подржан за ваш језик, али функционише на енглеском."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Гласовни унос користи Google-ову функцију за препознавање гласа. Примењује се "<a href="http://m.google.com/privacy">"политика приватности за мобилне уређаје"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Додирните унете речи да бисте их исправили само када су предлози видљиви"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Тема тастатуре"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Језик тастатуре: чешки"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Језик тастатуре: арапски"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Језик тастатуре: дански"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Језик тастатуре: немачки"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Језик тастатуре: енглески (УК)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Језик тастатуре: енглески (САД)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Језик тастатуре: шпански"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Језик тастатуре: француски"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Језик тастатуре: француски (Канада)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Језик тастатуре: француски (Швајц.)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Језик тастатуре: хебрејски"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Језик тастатуре: италијански"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Језик тастатуре: норвешки"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Језик тастатуре: холандски"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Језик тастатуре: пољски"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Језик тастатуре: португалски"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Језик тастатуре: руски"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Језик тастатуре: српски"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Језик тастатуре: шведски"</string>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index eaf3505..cf0fcf8 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Knappljud"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Popup vid knapptryck"</string>
     <string name="general_category" msgid="1859088467017573195">"Allmänt"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Textkorrigering"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Textkorrigering"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Förslag baserade på tidigare ord"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Andra alternativ"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Avancerade inställningar"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Alternativ för expertanvändare"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ta bort popup-fördröjning"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Fördröj inte"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Automatiska versaler"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Snabba lösningar"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Åtgärdar automatiskt vanliga misstag"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Visa alltid"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Visa stående"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Dölj alltid"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Byt språk m. mellanslag"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Visa inställningsknapp"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatiskt"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Visa alltid"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Av"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Måttlig"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressiv"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigramförslag"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigramförslag"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Förbättra förslaget med föregående ord"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigramförslag"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Använd även föregående ord för att ge förslag"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: sparat"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Kör"</string>
     <string name="label_next_key" msgid="362972844525672568">"Nästa"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Mer"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Vänta"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Ta bort"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Retur"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Inställningar"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Skift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Blanksteg"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Symboler"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tabb"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Röstinmatning"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Aktivera symboler"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Inaktivera symboler"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Aktivera Skift"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Inaktivera Skift"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Röstindata"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Röstindata stöds inte på ditt språk än, men tjänsten fungerar på engelska."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Röstinmatning använder sig av Googles tjänst för taligenkänning. "<a href="http://m.google.com/privacy">"Sekretesspolicyn för mobila enheter"</a>" gäller."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Tryck på skrivna ord om du vill korrigera dem, endast när förslag visas"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tangentbordstema"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Tjeckiskt tangentbord"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arabiskt tangentbord"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Danskt tangentbord"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Tyskt tangentbord"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Engelskt tangentbord (Storbrit.)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Engelskt tangentbord (USA)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Spanskt tangentbord"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Franskt tangentbord"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Franskt tangentbord (Kanada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Franskt tangentbord (Schweiz)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Hebreiskt tangentbord"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Italienskt tangentbord"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Norskt tangentbord"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Holländskt tangentbord"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Polskt tangentbord"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portugisiskt tangentbord"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Ryskt tangentbord"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Serbiskt tangentbord"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Svenskt tangentbord"</string>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
new file mode 100644
index 0000000..44f0722
--- /dev/null
+++ b/java/res/values-sw/strings.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Kibodi ya Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Mipangilio ya kibodi ya Android"</string>
+    <string name="english_ime_input_options" msgid="3909945612939668554">"Chaguo za uingizaji"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Tetema unabofya kitufe"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Toa sauti unapobofya kitufe"</string>
+    <string name="popup_on_keypress" msgid="123894815723512944">"Ibuka kitufe kinapobonyezwa"</string>
+    <!-- no translation found for general_category (1859088467017573195) -->
+    <skip />
+    <!-- no translation found for correction_category (2236750915056607613) -->
+    <skip />
+    <!-- no translation found for ngram_category (5337109164339320257) -->
+    <skip />
+    <!-- no translation found for misc_category (6894192814868233453) -->
+    <skip />
+    <!-- no translation found for advanced_settings (362895144495591463) -->
+    <skip />
+    <!-- no translation found for advanced_settings_summary (5193513161106637254) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_no_delay (2096123151571458064) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
+    <string name="auto_cap" msgid="1719746674854628252">"Uwekaji wa herufi kubwa kiotomatiki"</string>
+    <string name="quick_fixes" msgid="5353213327680897927">"Utatuzi wa haraka"</string>
+    <string name="quick_fixes_summary" msgid="3405028402510332373">"Husahihisha makosa ya kawaida yaliyoandikwa"</string>
+    <!-- no translation found for prefs_show_suggestions (8026799663445531637) -->
+    <skip />
+    <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>
+    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Onyesha kwenye hali wima"</string>
+    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ficha kila wakati"</string>
+    <!-- no translation found for prefs_use_spacebar_language_switch (8828538114550634449) -->
+    <skip />
+    <string name="prefs_settings_key" msgid="4623341240804046498">"Onyesha kitufe cha mipangilio"</string>
+    <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Kiotomatiki"</string>
+    <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Onyesha kila wakati"</string>
+    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">" Ficha kila mara"</string>
+    <string name="auto_correction" msgid="4979925752001319458">"Usahihishaji Kioto"</string>
+    <!-- outdated translation 6881047311475758267 -->     <string name="auto_correction_summary" msgid="5625751551134658006">"Sahihisha neno lililotangulia kiotomatiki"</string>
+    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Zima"</string>
+    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Ya wastani"</string>
+    <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Ya hima"</string>
+    <!-- outdated translation 1323347224043514969 -->     <string name="bigram_suggestion" msgid="2636414079905220518">"Mapendekezo ya Bigramu"</string>
+    <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Tumia neno la hapo awali ili kuboresha pendekezo"</string>
+    <!-- no translation found for bigram_prediction (8914273444762259739) -->
+    <skip />
+    <!-- no translation found for bigram_prediction_summary (1747261921174300098) -->
+    <skip />
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Imehifadhiwa"</string>
+    <string name="label_go_key" msgid="1635148082137219148">"Nenda"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Ifuatayo"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Kwisha"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Tuma"</string>
+    <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_more_key" msgid="3760239494604948502">"Zaidi"</string>
+    <string name="label_pause_key" msgid="181098308428035340">"Pumzisha"</string>
+    <string name="label_wait_key" msgid="6402152600878093134">"Subiri"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Uingizaji wa sauti"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Uingizaji wa sauti hauhimiliwi kwa lugha yako kwa sasa, lakini inafanya kazi kwa Kiingereza."</string>
+    <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Uingizaji wa sauti hutumia utambuaji wa usemi wa Google. "<a href="http://m.google.com/privacy">"Sera ya Faragha ya Simu za mkononi "</a>" hutumika."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="3190378129944934856">"Ili kuzima uingizaji sauti, nenda kwa mipangilio ya mbinu ya uingizaji."</string>
+    <string name="voice_hint_dialog_message" msgid="1420686286820661548">"Ili kutumia uingizaji wa sauti, bonyeza kitufe cha maikrofoni."</string>
+    <string name="voice_listening" msgid="467518160751321844">"Ongea sasa"</string>
+    <string name="voice_working" msgid="6666937792815731889">"Inafanya kazi"</string>
+    <string name="voice_initializing" msgid="661962047129906646"></string>
+    <string name="voice_error" msgid="5140896300312186162">"Hitilafu. Tafadhali jaribu tena."</string>
+    <string name="voice_network_error" msgid="6649556447401862563">"Haiwezi kuunganisha"</string>
+    <string name="voice_too_much_speech" msgid="5746973620134227376">"Hitilafu, usemi ni zaidi."</string>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Tatizo la sauti"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Hitilafu ya Seva"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Hakuna matamshi yaliyosikizwa"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Hakuna zinazolingana zilizopatikana."</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Utafutaji wa sauti haujawekwa"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Kidokezo:"</b>" Telezesha kidole kwenye kibodi ili utamke"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Kidokezo:"</b>" Wakati mwingine, jaribu kutamka uakifishaji kama vile \"kituo\", \"koma\", au \"kiulizio cha swali\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Ghairi"</string>
+    <string name="ok" msgid="7898366843681727667">"Sawa"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Uingizaji wa sauti"</string>
+    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Kwenye kibodi kuu"</string>
+    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Kwenye kibodi ya ishara"</string>
+    <string name="voice_input_modes_off" msgid="3745699748218082014">"Zima"</string>
+    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Maikrofoni kwenye kibodi kuu"</string>
+    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Maikrofoni kwenye kibodi ya ishara"</string>
+    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Uingizaji sauti umelemazwa"</string>
+    <string name="selectInputMethod" msgid="315076553378705821">"Chagua mtindo wa uingizaji"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Lugha za uingizaji"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Telezesha kidole kwenye kitufe cha nafasi ili kubadilisha lugha"</string>
+    <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Gusa tena ili kuhifadhi"</string>
+    <string name="has_dictionary" msgid="6071847973466625007">"Kamusi inapatikana"</string>
+    <string name="prefs_enable_log" msgid="6620424505072963557">"Wezesha maoni ya watumiaji"</string>
+    <string name="prefs_description_log" msgid="5827825607258246003">"Saidia kuimarisha mbinu ya uingizaji wa kihariri, kwa kutuma takwimu za matumizi na ripoti za kuvurugika kwa Google kiotomatiki."</string>
+    <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Gusa ili kurekebisha maneno"</string>
+    <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Gusa maneno yaliyoingizwa ili kuyasahihisha, wakati mapendekezo yanaonekana tu"</string>
+    <string name="keyboard_layout" msgid="437433231038683666">"Maandhari ya Kibodi"</string>
+    <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Kibodi ya Kicheki"</string>
+    <!-- no translation found for subtype_mode_ar_keyboard (2655338636329774995) -->
+    <skip />
+    <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Kibodi ya Kidenmaki"</string>
+    <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Kibodi ya Kijerumani"</string>
+    <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Kibodi ya Kiingereza (Uingereza)"</string>
+    <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Kibodi ya Kiingereza (Marekani)"</string>
+    <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Kibodi ya Kihispania"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
+    <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Kibodi ya Kifaransa"</string>
+    <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Kibodi ya Kifaransa (Kanada)"</string>
+    <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Kibodi ya Kifaransa (Uswizi)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <!-- no translation found for subtype_mode_iw_keyboard (1787536828253289950) -->
+    <skip />
+    <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Kibodi ya Kiitaliano"</string>
+    <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Kibodi ya Kinorwe"</string>
+    <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Kibodi ya Kiholanzi"</string>
+    <!-- no translation found for subtype_mode_pl_keyboard (2225816414814396047) -->
+    <skip />
+    <!-- no translation found for subtype_mode_pt_keyboard (7503997804861754840) -->
+    <skip />
+    <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Kibodi ya Kirusi"</string>
+    <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Kibodi ya Kiserbia"</string>
+    <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Kibodi ya Kiswidi"</string>
+    <!-- no translation found for subtype_mode_af_voice (7542487489657902699) -->
+    <skip />
+    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Sauti ya Kicheki"</string>
+    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Sauti ya Kijerumani"</string>
+    <!-- no translation found for subtype_mode_en_voice (6643420989651848728) -->
+    <skip />
+    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Sauti ya Kihispania"</string>
+    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Sauti ya Kifaransa"</string>
+    <!-- no translation found for subtype_mode_it_voice (5077373057157441323) -->
+    <skip />
+    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Sauti ya Kijapani"</string>
+    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Sauti ya Kikorea"</string>
+    <!-- no translation found for subtype_mode_nl_voice (2603552312869575021) -->
+    <skip />
+    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Sauti ya Kipolandi"</string>
+    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Sauti ya Kireno"</string>
+    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Sauti ya Kirusi"</string>
+    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Sauti ya Kituruki"</string>
+    <!-- no translation found for subtype_mode_yue_voice (1576887891614624263) -->
+    <skip />
+    <!-- no translation found for subtype_mode_zh_voice (4360533229467271152) -->
+    <skip />
+    <!-- no translation found for subtype_mode_zu_voice (1146122571698884636) -->
+    <skip />
+    <string name="prefs_usability_study_mode" msgid="6937813623647419810">"Hali ya Uchunguzi wa Utumizi"</string>
+</resources>
diff --git a/java/res/values-sw600dp-land/dimens.xml b/java/res/values-sw600dp-land/dimens.xml
index 618b7f4..9da8f89 100644
--- a/java/res/values-sw600dp-land/dimens.xml
+++ b/java/res/values-sw600dp-land/dimens.xml
@@ -21,18 +21,25 @@
 <resources>
     <!-- keyboardHeight = key_height*4 + key_bottom_gap*3 -->
     <dimen name="keyboardHeight">45.0mm</dimen>
+    <fraction name="minKeyboardHeight">45%p</fraction>
     <!-- key_height + key_bottom_gap = popup_key_height -->
     <!-- <dimen name="key_height">14.5mm</dimen> -->
     <dimen name="key_bottom_gap">1.3mm</dimen>
     <dimen name="key_horizontal_gap">1.3mm</dimen>
-    <dimen name="popup_key_height">13.0mm</dimen>
     <dimen name="keyboard_top_padding">1.1mm</dimen>
     <dimen name="keyboard_bottom_padding">0.0mm</dimen>
+    <dimen name="key_bottom_gap_ics">1.3mm</dimen>
+    <dimen name="key_horizontal_gap_ics">1.3mm</dimen>
+    <dimen name="keyboard_top_padding_ics">1.1mm</dimen>
+    <dimen name="keyboard_bottom_padding_ics">0.0mm</dimen>
+    <dimen name="popup_key_height">13.0mm</dimen>
     <!-- key_height x 1.0 -->
     <dimen name="key_preview_height">13.0mm</dimen>
 
-    <dimen name="key_letter_size">25dip</dimen>
-    <dimen name="key_label_text_size">18dip</dimen>
+    <fraction name="key_letter_ratio">45%</fraction>
+    <fraction name="key_label_ratio">32%</fraction>
+    <fraction name="key_hint_letter_ratio">23%</fraction>
+    <fraction name="key_uppercase_letter_ratio">29%</fraction>
     <!-- left or right padding of label alignment -->
     <dimen name="key_label_horizontal_alignment_padding">18dip</dimen>
     <dimen name="candidate_strip_padding">40.0mm</dimen>
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml
index c3d3445..c2c0c25 100644
--- a/java/res/values-sw600dp/config.xml
+++ b/java/res/values-sw600dp/config.xml
@@ -26,13 +26,13 @@
     <bool name="config_enable_show_recorrection_option">false</bool>
     <bool name="config_enable_quick_fixes_option">false</bool>
     <bool name="config_enable_bigram_suggestions_option">false</bool>
-    <bool name="config_candidate_highlight_font_color_enabled">false</bool>
     <bool name="config_swipe_down_dismiss_keyboard_enabled">false</bool>
     <bool name="config_sliding_key_input_enabled">false</bool>
     <bool name="config_digit_popup_characters_enabled">false</bool>
     <!-- Whether or not Popup on key press is enabled by default -->
     <bool name="config_default_popup_preview">false</bool>
     <bool name="config_default_sound_enabled">true</bool>
+    <bool name="config_auto_correction_spacebar_led_enabled">false</bool>
     <bool name="config_use_spacebar_language_switcher">false</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">true</bool>
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml
index 27d577c..6c67746 100644
--- a/java/res/values-sw600dp/dimens.xml
+++ b/java/res/values-sw600dp/dimens.xml
@@ -21,6 +21,8 @@
 <resources>
     <!-- keyboardHeight = key_height*4 + key_bottom_gap*3 -->
     <dimen name="keyboardHeight">48.0mm</dimen>
+    <fraction name="maxKeyboardHeight">50%p</fraction>
+    <fraction name="minKeyboardHeight">-35.0%p</fraction>
     <!-- key_height + key_bottom_gap = popup_key_height -->
     <!-- <dimen name="key_height">14.5mm</dimen> -->
     <dimen name="key_bottom_gap">1.0mm</dimen>
@@ -28,6 +30,10 @@
     <dimen name="popup_key_height">10.0mm</dimen>
     <dimen name="keyboard_top_padding">1.1mm</dimen>
     <dimen name="keyboard_bottom_padding">0.0mm</dimen>
+    <dimen name="key_bottom_gap_ics">1.0mm</dimen>
+    <dimen name="key_horizontal_gap_ics">1.0mm</dimen>
+    <dimen name="keyboard_top_padding_ics">1.1mm</dimen>
+    <dimen name="keyboard_bottom_padding_ics">0.0mm</dimen>
     <!-- key_height x 1.0 -->
     <dimen name="key_preview_height">13.0mm</dimen>
     <dimen name="mini_keyboard_key_horizontal_padding">12dip</dimen>
@@ -37,9 +43,11 @@
     <!-- popup_key_height x -1.0 -->
     <dimen name="mini_keyboard_vertical_correction">-13.0mm</dimen>
 
-    <dimen name="key_letter_size">24dip</dimen>
-    <dimen name="key_label_text_size">14dip</dimen>
-    <dimen name="key_preview_text_size_large">24dip</dimen>
+    <fraction name="key_letter_ratio">37%</fraction>
+    <fraction name="key_label_ratio">22%</fraction>
+    <fraction name="key_hint_letter_ratio">23%</fraction>
+    <fraction name="key_uppercase_letter_ratio">25%</fraction>
+    <fraction name="key_preview_text_ratio">82%</fraction>
     <!-- left or right padding of label alignment -->
     <dimen name="key_label_horizontal_alignment_padding">6dip</dimen>
 
diff --git a/java/res/values-sw768dp-land/dimens.xml b/java/res/values-sw768dp-land/dimens.xml
index c406710..a6cd85b 100644
--- a/java/res/values-sw768dp-land/dimens.xml
+++ b/java/res/values-sw768dp-land/dimens.xml
@@ -19,21 +19,30 @@
 -->
 
 <resources>
-    <!-- keyboardHeight = key_height*4 + key_bottom_gap*3 -->
+    <!-- keyboardHeight = key_height*4 + key_bottom_gap*3, key_height=14.5mm -->
     <dimen name="keyboardHeight">58.0mm</dimen>
+    <fraction name="minKeyboardHeight">45%p</fraction>
     <!-- key_height + key_bottom_gap = popup_key_height -->
     <!-- <dimen name="key_height">14.5mm</dimen> -->
     <dimen name="key_bottom_gap">1.6mm</dimen>
     <dimen name="key_horizontal_gap">1.6mm</dimen>
-    <dimen name="popup_key_height">13.0mm</dimen>
     <dimen name="keyboard_top_padding">1.1mm</dimen>
     <dimen name="keyboard_bottom_padding">0.0mm</dimen>
-    <!-- key_height x 1.0 -->
-    <dimen name="key_preview_height">13.0mm</dimen>
+    <dimen name="key_bottom_gap_ics">1.6mm</dimen>
+    <dimen name="key_horizontal_gap_ics">1.6mm</dimen>
+    <dimen name="keyboard_top_padding_ics">1.1mm</dimen>
+    <dimen name="keyboard_bottom_padding_ics">0.0mm</dimen>
+    <dimen name="popup_key_height">13.0mm</dimen>
+    <dimen name="keyboard_horizontal_edges_padding">0.0mm</dimen>
 
-    <dimen name="key_letter_size">28dip</dimen>
-    <dimen name="key_label_text_size">20dip</dimen>
+    <fraction name="key_letter_ratio">38%</fraction>
+    <fraction name="key_label_ratio">26%</fraction>
+    <fraction name="key_hint_letter_ratio">23%</fraction>
+    <fraction name="key_uppercase_letter_ratio">24%</fraction>
     <!-- left or right padding of label alignment -->
     <dimen name="key_label_horizontal_alignment_padding">18dip</dimen>
+    <dimen name="key_preview_height_holo">26.5mm</dimen>
+    <dimen name="key_preview_offset_holo">7.5mm</dimen>
+
     <dimen name="candidate_strip_padding">40.0mm</dimen>
 </resources>
diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml
index f075b1b..dc4c287 100644
--- a/java/res/values-sw768dp/config.xml
+++ b/java/res/values-sw768dp/config.xml
@@ -20,26 +20,27 @@
 
 <resources>
     <bool name="config_enable_show_settings_key_option">false</bool>
-    <bool name="config_enable_show_subtype_settings">false</bool>
     <bool name="config_enable_show_voice_key_option">false</bool>
-    <bool name="config_enable_show_popup_on_keypress_option">false</bool>
+    <!-- TODO: This configuration value is temporary set true to check popup preview behavior. -->
+    <bool name="config_enable_show_popup_on_keypress_option">true</bool>
     <bool name="config_enable_show_recorrection_option">false</bool>
     <bool name="config_enable_quick_fixes_option">false</bool>
     <bool name="config_enable_bigram_suggestions_option">false</bool>
-    <bool name="config_candidate_highlight_font_color_enabled">false</bool>
     <bool name="config_swipe_down_dismiss_keyboard_enabled">false</bool>
     <bool name="config_sliding_key_input_enabled">false</bool>
     <bool name="config_digit_popup_characters_enabled">false</bool>
     <!-- Whether or not Popup on key press is enabled by default -->
     <bool name="config_default_popup_preview">false</bool>
     <bool name="config_default_sound_enabled">true</bool>
+    <bool name="config_auto_correction_spacebar_led_enabled">true</bool>
     <bool name="config_use_spacebar_language_switcher">false</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">true</bool>
-    <!-- The language is never displayed if == 0, always displayed if < 0 -->
-    <integer name="config_delay_before_fadeout_language_on_spacebar">1200</integer>
+    <integer name="config_delay_update_suggestions">180</integer>
     <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
     <string name="config_default_keyboard_theme_id" translatable="false">5</string>
     <string name="config_text_size_of_language_on_spacebar" translatable="false">medium</string>
     <integer name="config_max_popup_keyboard_column">5</integer>
+    <!--  Screen metrics for logging. 0 = "mdpi", 1 = "hdpi", 2 = "xlarge" -->
+    <integer name="log_screen_metrics">2</integer>
 </resources>
diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml
index 9ea5258..8152673 100644
--- a/java/res/values-sw768dp/dimens.xml
+++ b/java/res/values-sw768dp/dimens.xml
@@ -19,17 +19,23 @@
 -->
 
 <resources>
-    <!-- keyboardHeight = key_height*4 + key_bottom_gap*3 -->
+    <!-- keyboardHeight = key_height*4 + key_bottom_gap*3, key_height=12mm -->
     <dimen name="keyboardHeight">48.0mm</dimen>
+    <fraction name="maxKeyboardHeight">50%p</fraction>
+    <fraction name="minKeyboardHeight">-35.0%p</fraction>
     <!-- key_height + key_bottom_gap = popup_key_height -->
     <!-- <dimen name="key_height">14.5mm</dimen> -->
     <dimen name="key_bottom_gap">1.1mm</dimen>
     <dimen name="key_horizontal_gap">1.1mm</dimen>
-    <dimen name="popup_key_height">10.0mm</dimen>
     <dimen name="keyboard_top_padding">1.1mm</dimen>
     <dimen name="keyboard_bottom_padding">0.0mm</dimen>
-    <!-- key_height x 1.0 -->
-    <dimen name="key_preview_height">13.0mm</dimen>
+    <dimen name="key_bottom_gap_ics">1.1mm</dimen>
+    <dimen name="key_horizontal_gap_ics">1.1mm</dimen>
+    <dimen name="keyboard_top_padding_ics">1.1mm</dimen>
+    <dimen name="keyboard_bottom_padding_ics">0.0mm</dimen>
+    <dimen name="popup_key_height">10.0mm</dimen>
+    <dimen name="keyboard_horizontal_edges_padding">0.0mm</dimen>
+    <dimen name="mini_keyboard_horizontal_padding_holo">40dip</dimen>
     <dimen name="mini_keyboard_key_horizontal_padding">12dip</dimen>
     <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. -->
     <!-- popup_key_height x 1.2 -->
@@ -37,15 +43,22 @@
     <!-- popup_key_height x -1.0 -->
     <dimen name="mini_keyboard_vertical_correction">-13.0mm</dimen>
 
-    <dimen name="key_letter_size">26dip</dimen>
-    <dimen name="key_label_text_size">16dip</dimen>
-    <dimen name="key_preview_text_size_large">24dip</dimen>
+    <fraction name="key_letter_ratio">38%</fraction>
+    <fraction name="key_label_ratio">26%</fraction>
+    <fraction name="key_hint_letter_ratio">23%</fraction>
+    <fraction name="key_uppercase_letter_ratio">25%</fraction>
     <!-- left or right padding of label alignment -->
     <dimen name="key_label_horizontal_alignment_padding">6dip</dimen>
+    <fraction name="key_preview_text_ratio">26.3%</fraction>
+    <dimen name="key_preview_height_holo">23.0mm</dimen>
+    <dimen name="key_preview_offset_holo">8.0mm</dimen>
 
     <dimen name="candidate_strip_height">46dip</dimen>
+    <!-- candidate_strip_minimum_height =
+         key_preview_height_holo - key_preview_offset_holo + alpha -->
+    <dimen name="candidate_strip_minimum_height">18mm</dimen>
     <dimen name="candidate_strip_padding">15.0mm</dimen>
-    <dimen name="candidate_min_width">0.3in</dimen>
-    <dimen name="candidate_padding">12dip</dimen>
+    <dimen name="candidate_min_width">46dip</dimen>
+    <dimen name="candidate_padding">8dip</dimen>
     <dimen name="candidate_text_size">22dip</dimen>
 </resources>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index 6dc813b..1cbc42e 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"ส่งเสียงเมื่อกดปุ่ม"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"ป๊อปอัปเมื่อกดแป้น"</string>
     <string name="general_category" msgid="1859088467017573195">"ทั่วไป"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"การแก้ไขข้อความ"</string>
+    <string name="correction_category" msgid="2236750915056607613">"การแก้ไขข้อความ"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"ข้อเสนอแนะตามคำก่อนหน้านี้"</string>
+    <string name="misc_category" msgid="6894192814868233453">"ตัวเลือกอื่นๆ"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"การตั้งค่าขั้นสูง"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"ตัวเลือกสำหรับผู้ใช้ที่มีความเชี่ยวชาญ"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"การหน่วงเวลาก่อนปิดป๊อปอัพหลัก"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ไม่มีการหน่วงเวลา"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ค่าเริ่มต้น"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"ปรับเป็นตัวพิมพ์ใหญ่อัตโนมัติ"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"แก้ไขด่วน"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"แก้ไขข้อผิดพลาดในการพิมพ์ที่พบบ่อย"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"แสดงทุกครั้ง"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"แสดงในโหมดแนวตั้ง"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"ซ่อนทุกครั้ง"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"ใช้แป้น Spacebar เพื่อสลับภาษา"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"แสดงแป้นการตั้งค่า"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"อัตโนมัติ"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"แสดงตลอดเวลา"</string>
@@ -45,8 +57,10 @@
     <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>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"คำแนะนำ Bigram"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"คำแนะนำ Bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"ใช้คำก่อนหน้านี้เพื่อปรับปรุงคำแนะนำ"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"การคาดคะเน Bigram"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"ใช้คำก่อนหน้านี้สำหรับการคาดคะเน"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : บันทึกแล้ว"</string>
     <string name="label_go_key" msgid="1635148082137219148">"ไป"</string>
     <string name="label_next_key" msgid="362972844525672568">"ถัดไป"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"เพิ่มเติม"</string>
     <string name="label_pause_key" msgid="181098308428035340">"หยุดชั่วคราว"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"รอ"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"ลบ"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Return"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"การตั้งค่า"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Space"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"สัญลักษณ์"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"ป้อนข้อมูลด้วยเสียง"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"สัญลักษณ์เปิดอยู่"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"สัญลักษณ์ปิดอยู่"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift เปิดอยู่"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift ปิดอยู่"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"การป้อนข้อมูลด้วยเสียง"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"ขณะนี้การป้อนข้อมูลด้วยเสียงยังไม่ได้รับการสนับสนุนในภาษาของคุณ แต่ใช้ได้ในภาษาอังกฤษ"</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"ป้อนข้อมูลด้วยเสียงใช้การจดจำคำพูดของ Google "<a href="http://m.google.com/privacy">" นโยบายส่วนบุคคลของมือถือ"</a>"มีผลบังคับใช้"</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"แตะคำที่ป้อนไว้เพื่อแก้ไข เฉพาะเมื่อเห็นข้อเสนอแนะเท่านั้น"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"ชุดรูปแบบแป้นพิมพ์"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"แป้นพิมพ์ภาษาเช็ก"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"แป้นพิมพ์ภาษาอาหรับ"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"แป้นพิมพ์ภาษาเดนมาร์ก"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"แป้นพิมพ์ภาษาเยอรมัน"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"แป้นพิมพ์ภาษาอังกฤษ (สหราชอาณาจักร)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"แป้นพิมพ์ภาษาอังกฤษ (สหรัฐอเมริกา)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"แปันพิมพ์ภาษาสเปน"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"แป้นพิมพ์ภาษาฝรั่งเศส"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"แป้นพิมพ์ภาษาฝรั่งเศส (แคนาดา)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"แป้นพิมพ์ภาษาฝรั่งเศส (สวิตเซอร์แลนด์)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"แป้นพิมพ์ภาษาฮิบรู"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"แป้นพิมพ์ภาษาอิตาลี"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"แป้นพิมพ์ภาษานอร์เวย์"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"แป้นพิมพ์ภาษาดัตช์"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"แป้นพิมพ์ภาษาโปแลนด์"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"แป้นพิมพ์ภาษาโปรตุเกส"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"แป้นพิมพ์ภาษารัสเซีย"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"แป้นพิมพ์ภาษาเซอร์เบีย"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"แป้นพิมพ์ภาษาสวีเดน"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index bdd700b..e5d9687 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Tunog sa keypress"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Popup sa keypress"</string>
     <string name="general_category" msgid="1859088467017573195">"Pangkalahatan"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Pagwawasto ng teksto"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Pagwawasto ng teksto"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Mga suhestiyon batay sa mga nakaraang salita"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Iba pang mga pagpipilian"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Mga advanced na setting"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Mga pagpipilian para sa mga ekspertong user"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Balewala antala key popup"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Walang antala"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalization"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Mga mabilisang pagsasaayos"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Itinatama ang mga karaniwang na-type na mali"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Palaging ipakita"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Ipakita sa portrait mode"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Palaging itago"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Gamitin ang panglipat ng wika sa spacebar"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Ipakita ang key ng mga setting"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Awtomatiko"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Palaging ipakita"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Naka-off"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Modest"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresibo"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Mga Suhestiyon na Bigram"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Mga bigram na suhestiyon"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Gamitin ang nakaraang salita upang pahusayin ang suhestiyon"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigram na hula"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gamitin ang nakaraang salita para din sa hula"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Na-save"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Punta"</string>
     <string name="label_next_key" msgid="362972844525672568">"Susunod"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Higit pa"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Pause"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Intay"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Tanggalin"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Bumalik"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Mga Setting"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Puwang"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Mga Simbolo"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Input ng Boses"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Naka-on ang mga simbolo"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Naka-off ang mga simbolo"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Naka-on ang shift"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Naka-off ang shift"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Pag-input ng boses"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Hindi kasalukuyang suportado ang pag-input ng boses para sa iyong wika, ngunit gumagana sa Ingles."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Gumagamit ang pag-input ng boses ng speech recognition ng Google. Nalalapat "<a href="http://m.google.com/privacy">"Ang Patakaran sa Privacy ng Mobile"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Pindutin ang mga inilagay na salita upang iwasto ang mga ito, kapag nakikita lang ang mga suhestiyon"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Tema ng Keyboard"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Czech na Keyboard"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arabic na Keyboard"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Danish na Keyboard"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"German na Keyboard"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Ingles (UK) na Keyboard"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Ingles (US) na Keyboard"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Spanish na Keyboard"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"French na Keyboard"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"French (Canada) na Keyboard"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"French (Switzerland) na Keyboard"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Hebrew na Keyboard"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Italian na Keyboard"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Norwegian na Keyboard"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Dutch na Keyboard"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Polish na Keyboard"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portuguese na Keyboard"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Russian na Keyboard"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Serbian na Keyboard"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Swedish na Keyboard"</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index a88b509..8be9f7a 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Tuşa basıldığında ses çıkar"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Tuşa basıldığında pop-up aç"</string>
     <string name="general_category" msgid="1859088467017573195">"Genel"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Metin düzeltme"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Metin düzeltme"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Önceki kelimelere dayalı öneriler"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Diğer seçenekler"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Gelişmiş ayarlar"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Uzman kullanıcılar için seçenekler"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tuş popup içn kaptm ertlm"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Gecikme yok"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Varsayılan"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Otomatik olarak büyük harf yap"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Hızlı onarımlar"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Yaygın olarak yapılan yazım hatalarını düzeltir"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Her zaman göster"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Dikey modda göster"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Her zaman gizle"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Dil geçişi &gt; boşluk çubuğuyla"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Ayarları göster tuşu"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Otomatik"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Her zaman göster"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Kapalı"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Ölçülü"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresif"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Bigram Önerileri"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Bigram önerileri"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Öneriyi geliştirmek için önceki kelimeyi kullanın"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Bigram tahmini"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Önceki kelimeyi de tahmin için kullan"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Kaydedildi"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Git"</string>
     <string name="label_next_key" msgid="362972844525672568">"İleri"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Diğer"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Durkl"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Bekle"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Sil"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Return"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Ayarlar"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Üst Karakter"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Boşluk"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Simgeler"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Sekme"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Ses Girişi"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Simgeler açık"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Simgeler kapalı"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Üst Karakter açık"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Üst Karakter kapalı"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Ses girişi"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Ses girişi, şu anda sizin diliniz için desteklenmiyor ama İngilizce dilinde kullanılabilir."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Ses girişi Google\'ın konuşma tanıma işlevini kullanır. "<a href="http://m.google.com/privacy">" Mobil Gizlilik Politikası"</a>" geçerlidir."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Yalnızca öneriler görünür olduğunda, düzeltmek için girilen kelimelere dokunun"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Klavye Teması"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Çekçe Klavye"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Arapça Klavye"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Danca Klavye"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Almanca Klavye"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"İngilizce (İngiltere) Klavye"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"İngilizce (ABD) Klavye"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"İspanyolca Klavye"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Fransızca Klavye"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Fransızca (Kanada) Klavye"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Fransızca (İsviçre) Klavye"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"İbranice Klavye"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"İtalyanca Klavye"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Norveççe Klavye"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Felemenkçe Klavye"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Lehçe Klavye"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Portekizce Klavye"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Rusça Klavye"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Sırpça Klavye"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"İsveççe Klavye"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index 5010ee9..e5d2b31 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук при натиску клав."</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Сплив. при нат.клав."</string>
     <string name="general_category" msgid="1859088467017573195">"Загальні"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Виправлення тексту"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Виправлення тексту"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Пропозиції на основі попередніх слів"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Інші опції"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Розширені налаштування"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Налаштування для досвідчених користувачів"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Затримка клавіши закриття"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без затримки"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"За умовчанням"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Авто викор. вел. літер"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Шв. виправлення"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Виправляє поширені помилки"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Завжди показувати"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Показувати в книжковому режимі"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Завжди ховати"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Використ. зміну мови пробілом"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Показ. клав. налашт."</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Автоматично"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Завжди показ."</string>
@@ -45,8 +57,10 @@
     <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>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Двобуквені пропозиції"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Пропозиції з двох слів"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Викор. попер. слово для покращ. пропозиції"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Передбачений запит  із двох слів"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Використовувати попереднє слово також як передбачений запит"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : збережено"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Іти"</string>
     <string name="label_next_key" msgid="362972844525672568">"Далі"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Більше"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Пауза"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Чек."</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Клавіша Delete"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Клавіша Return"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Клавіша Settings"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Клавіша Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Клавіша Space"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Клавіша Symbols"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Клавіша Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Клавіша Voice Input"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Символи ввімкнено"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Символи вимкнено"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift увімкнено"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift вимкнено"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Голос. ввід"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Голос. ввід наразі не підтрим. для вашої мови, але можна користуватися англійською."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Голосовий ввід використовує розпізнавання мовлення Google. Застосовується "<a href="http://m.google.com/privacy">"Політика конфіденційності для мобільних пристроїв"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Торкніться введених слів, щоб виправити їх, лише коли ввімкнено пропозиції"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Тема клавіатури"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Чеська розкладка"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Розкладка для арабської мови"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Данська розкладка"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Німецька розкладка"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Англ. розкладка (Великобританія)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Англійська розкладка (США)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Іспанська розкладка"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Французька розкладка"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Французька розкладка (Канада)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Французька розкладка (Швейцарія)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Розкладка для івриту"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Італійська розкладка"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Норвезька розкладка"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Голланд. розклад."</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Польська розкладка"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Португальська розкладка"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Російська розкладка"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Сербська розкладка"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Шведська розкладка"</string>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 8dac2f9..a75f7e8 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Âm thanh khi nhấn phím"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Cửa sổ bật lên khi nhấn phím"</string>
     <string name="general_category" msgid="1859088467017573195">"Chung"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"Sửa văn bản"</string>
+    <string name="correction_category" msgid="2236750915056607613">"Sửa văn bản"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"Đề xuất dựa trên các từ trước đó"</string>
+    <string name="misc_category" msgid="6894192814868233453">"Tùy chọn khác"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"Cài đặt nâng cao"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Tùy chọn cho người dùng chuyên gia"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Hlại việc l.bỏ csổ b.lên chính"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Không có tgian trễ"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Mặc định"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"Tự động viết hoa"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"Sửa nhanh"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"Sửa lỗi nhập thông thường"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Luôn hiển thị"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Hiển thị trên chế độ khổ đứng"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Luôn ẩn"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"Sử dụng trình chuyển đổi ngôn ngữ thanh cách"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"Hiển thị phím cài đặt"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Tự động"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Luôn hiển thị"</string>
@@ -45,8 +57,10 @@
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Tắt"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Đơn giản"</string>
     <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Linh hoạt"</string>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"Đề xuất Bigram"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"Đề xuất Bigram"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Sử dụng từ trước đó để cải tiến đề xuất"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"Dự đoán Bigram"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"Cũng sử dụng từ trước đó để dự đoán"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Đã lưu"</string>
     <string name="label_go_key" msgid="1635148082137219148">"Đến"</string>
     <string name="label_next_key" msgid="362972844525672568">"Tiếp theo"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"Khác"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Tạm dừng"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Đợi"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"Xóa"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"Quay lại"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"Cài đặt"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"Dấu cách"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"Biểu tượng"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"Nhập liệu bằng giọng nói"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"Bật biểu tượng"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"Tắt biểu tượng"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Bật Shift"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Tắt Shift"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"Nhập liệu bằng giọng nói"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Nhập liệu bằng giọng nói hiện không được hỗ trợ cho ngôn ngữ của bạn nhưng hoạt động với ngôn ngữ tiếng Anh."</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Nhập liệu bằng giọng nói sử dụng nhận dạng giọng nói của Google. Áp dụng "<a href="http://m.google.com/privacy">"Chính sách bảo mật dành cho điện thoại di động"</a>"."</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Chạm các từ đã nhập để sửa, chỉ khi các đề xuất hiển thị"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"Chủ đề bàn phím"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Bàn phím tiếng Séc"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"Bàn phím tiếng Ả Rập"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Bàn phím tiếng Đan Mạch"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Bàn phím tiếng Đức"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Bàn phím tiếng Anh (Anh)"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Bàn phím tiếng Anh (Mỹ)"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Bàn phím tiếng Tây Ban Nha"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Bàn phím tiếng Pháp"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Bàn phím tiếng Pháp (Canada)"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Bàn phím tiếng Pháp (Thụy Sĩ)"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"Bàn phím tiếng Do Thái"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Bàn phím tiếng Ý"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Bàn phím tiếng Na Uy"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Bàn phím tiếng Hà Lan"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"Bàn phím tiếng Ba Lan"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"Bàn phím tiếng Bồ Đào Nha"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Bàn phím tiếng Nga"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Bàn phím tiếng Serbia"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Bàn phím tiếng Thụy Điển"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index 9d76891..2db21f4 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"按键时播放音效"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"按键时显示弹出窗口"</string>
     <string name="general_category" msgid="1859088467017573195">"常规"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"文本更正"</string>
+    <string name="correction_category" msgid="2236750915056607613">"文本更正"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"根据前面的字词提供建议"</string>
+    <string name="misc_category" msgid="6894192814868233453">"其他选项"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"高级设置"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"适合专家级用户的选项"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"关闭弹出式键盘的延迟"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"无延迟"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"默认"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"自动大写"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"快速纠正"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"纠正常见的输入错误"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"始终显示"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"以纵向模式显示"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"始终隐藏"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"使用空格键切换语言"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"显示设置键"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"自动"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"始终显示"</string>
@@ -45,8 +57,10 @@
     <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>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"双连词建议"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"双连词建议"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"使用以前的字词改进建议"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"双连词预测"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"结合前一个字词进行预测"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已保存"</string>
     <string name="label_go_key" msgid="1635148082137219148">"开始"</string>
     <string name="label_next_key" msgid="362972844525672568">"下一步"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"更多"</string>
     <string name="label_pause_key" msgid="181098308428035340">"暂停"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"等待"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"删除"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"回车"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"设置"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"空格"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"符号"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"语音输入"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"符号模式已打开"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"符号模式已关闭"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"Shift 模式已打开"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"Shift 模式已关闭"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"语音输入"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"语音输入功能当前还不支持您的语言,您只能输入英语语音。"</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"语音输入采用了 Google 的语音识别技术,因此请遵守"<a href="http://m.google.com/privacy">"“Google 移动”隐私权政策"</a>"。"</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"仅在系统显示建议后,才触摸输入的字词进行更正"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"键盘主题"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"捷克语键盘"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"阿拉伯语键盘"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"丹麦语键盘"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"德语键盘"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"英语(英国)键盘"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"英语(美国)键盘"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"西班牙语键盘"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"法语键盘"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"法语(加拿大)键盘"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"法语(瑞士)键盘"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"希伯来语键盘"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"意大利语键盘"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"挪威语键盘"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"荷兰语键盘"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"波兰语键盘"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"葡萄牙语键盘"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"俄语键盘"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"塞尔维亚语键盘"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"瑞典语键盘"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 9ba7284..43a1f37 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -27,7 +27,18 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"按鍵時播放音效"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"按鍵時顯示彈出式視窗"</string>
     <string name="general_category" msgid="1859088467017573195">"一般設定"</string>
-    <string name="prediction_category" msgid="6361242011806282176">"文字修正"</string>
+    <string name="correction_category" msgid="2236750915056607613">"文字修正"</string>
+    <string name="ngram_category" msgid="5337109164339320257">"根據先前字詞產生的建議"</string>
+    <string name="misc_category" msgid="6894192814868233453">"其他選項"</string>
+    <string name="advanced_settings" msgid="362895144495591463">"進階設定"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"提供給專業使用者的選項"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"關閉彈出式鍵盤的延遲時間"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"不延遲"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"預設"</string>
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
     <string name="auto_cap" msgid="1719746674854628252">"自動大寫"</string>
     <string name="quick_fixes" msgid="5353213327680897927">"快速修正"</string>
     <string name="quick_fixes_summary" msgid="3405028402510332373">"修正一般打字錯誤"</string>
@@ -36,6 +47,7 @@
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"一律顯示"</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"以垂直模式顯示"</string>
     <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"永遠隱藏"</string>
+    <string name="prefs_use_spacebar_language_switch" msgid="8828538114550634449">"使用空白鍵切換語言"</string>
     <string name="prefs_settings_key" msgid="4623341240804046498">"顯示設定金鑰"</string>
     <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"自動"</string>
     <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"一律顯示"</string>
@@ -45,8 +57,10 @@
     <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>
-    <string name="bigram_suggestion" msgid="1323347224043514969">"雙連詞建議"</string>
+    <string name="bigram_suggestion" msgid="2636414079905220518">"雙連詞建議"</string>
     <string name="bigram_suggestion_summary" msgid="4383845146070101531">"根據前一個字詞自動找出更適合的建議"</string>
+    <string name="bigram_prediction" msgid="8914273444762259739">"雙連詞預測"</string>
+    <string name="bigram_prediction_summary" msgid="1747261921174300098">"同樣使用先前的字詞進行預測"</string>
     <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已儲存"</string>
     <string name="label_go_key" msgid="1635148082137219148">"開始"</string>
     <string name="label_next_key" msgid="362972844525672568">"繼續"</string>
@@ -56,18 +70,6 @@
     <string name="label_more_key" msgid="3760239494604948502">"更多"</string>
     <string name="label_pause_key" msgid="181098308428035340">"暫停"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"等候"</string>
-    <string name="description_delete_key" msgid="5586406298531883960">"刪除"</string>
-    <string name="description_return_key" msgid="8750044000806461678">"返回"</string>
-    <string name="description_settings_key" msgid="7484527796782969219">"設定"</string>
-    <string name="description_shift_key" msgid="346906866277787836">"Shift 鍵"</string>
-    <string name="description_space_key" msgid="8512130111575878517">"空白鍵"</string>
-    <string name="description_switch_alpha_symbol_key" msgid="4537975384274405537">"符號"</string>
-    <string name="description_tab_key" msgid="828186583738307137">"Tab 鍵"</string>
-    <string name="description_voice_key" msgid="3057731675774652754">"語音輸入"</string>
-    <string name="description_symbols_on" msgid="2994366855822840559">"開啟符號"</string>
-    <string name="description_symbols_off" msgid="3209578267079515136">"關閉符號"</string>
-    <string name="description_shift_on" msgid="6983188949895971587">"開啟位移"</string>
-    <string name="description_shift_off" msgid="8553265474523069034">"關閉位移"</string>
     <string name="voice_warning_title" msgid="4419354150908395008">"語音輸入"</string>
     <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"語音輸入目前不支援您的語言,但是可以辨識英文。"</string>
     <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"語音輸入使用 Google 的語音辨識功能,並遵循《"<a href="http://m.google.com/privacy">"行動服務隱私權政策"</a>"》。"</string>
@@ -106,17 +108,27 @@
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"輕觸輸入的字詞即可加以修正 (出現建議時才適用)"</string>
     <string name="keyboard_layout" msgid="437433231038683666">"鍵盤主題"</string>
     <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"捷克文鍵盤"</string>
+    <string name="subtype_mode_ar_keyboard" msgid="2655338636329774995">"阿拉伯文鍵盤"</string>
     <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"丹麥文鍵盤"</string>
     <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"德文鍵盤"</string>
     <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"英文 (英國) 鍵盤"</string>
     <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"英文 (美國) 鍵盤"</string>
     <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"西班牙文鍵盤"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
     <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"法文鍵盤"</string>
     <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"法文 (加拿大) 鍵盤"</string>
     <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"法文 (瑞士) 鍵盤"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <string name="subtype_mode_iw_keyboard" msgid="1787536828253289950">"希伯來文鍵盤"</string>
     <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"義大利文鍵盤"</string>
     <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"挪威文鍵盤"</string>
     <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"荷蘭文鍵盤"</string>
+    <string name="subtype_mode_pl_keyboard" msgid="2225816414814396047">"波蘭文鍵盤"</string>
+    <string name="subtype_mode_pt_keyboard" msgid="7503997804861754840">"葡萄牙文鍵盤"</string>
     <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"俄文鍵盤"</string>
     <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"塞爾維亞文鍵盤"</string>
     <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"瑞典文語音"</string>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
new file mode 100644
index 0000000..3c6f2ba
--- /dev/null
+++ b/java/res/values-zu/strings.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Ikhibhodi ye-Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Izilungiselelo zekhibhodi ye-Android"</string>
+    <string name="english_ime_input_options" msgid="3909945612939668554">"Okukhethwa kukho kokungenayo"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Dlidlizelisa ngokucindezela inkinobho"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Umsindo wokucindezela ukhiye"</string>
+    <string name="popup_on_keypress" msgid="123894815723512944">"Ugaxekile ngokucindezela ukhiye"</string>
+    <!-- no translation found for general_category (1859088467017573195) -->
+    <skip />
+    <!-- no translation found for correction_category (2236750915056607613) -->
+    <skip />
+    <!-- no translation found for ngram_category (5337109164339320257) -->
+    <skip />
+    <!-- no translation found for misc_category (6894192814868233453) -->
+    <skip />
+    <!-- no translation found for advanced_settings (362895144495591463) -->
+    <skip />
+    <!-- no translation found for advanced_settings_summary (5193513161106637254) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_no_delay (2096123151571458064) -->
+    <skip />
+    <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+    <skip />
+    <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+    <skip />
+    <string name="auto_cap" msgid="1719746674854628252">"Ukwenza ofeleba okuzenzakalelayo"</string>
+    <string name="quick_fixes" msgid="5353213327680897927">"Ukulungisa okusheshayo"</string>
+    <string name="quick_fixes_summary" msgid="3405028402510332373">"Ilungisa amaphutha athayiphwa ngokuvamile"</string>
+    <!-- no translation found for prefs_show_suggestions (8026799663445531637) -->
+    <skip />
+    <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>
+    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Bonisa kwimodi emile"</string>
+    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Fihla njalo"</string>
+    <!-- no translation found for prefs_use_spacebar_language_switch (8828538114550634449) -->
+    <skip />
+    <string name="prefs_settings_key" msgid="4623341240804046498">"Bonisa ukhiye wezilungiselelo"</string>
+    <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Okuzenzakalelayo"</string>
+    <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Bonisa njalo"</string>
+    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Fihla njalo"</string>
+    <string name="auto_correction" msgid="4979925752001319458">"Ukulungisa okuzenzakalelayo"</string>
+    <!-- outdated translation 6881047311475758267 -->     <string name="auto_correction_summary" msgid="5625751551134658006">"Ngokuzenzakalelayo ilungisa igama elandulele"</string>
+    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Valiwe"</string>
+    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Thobekile"</string>
+    <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Bukhali"</string>
+    <!-- outdated translation 1323347224043514969 -->     <string name="bigram_suggestion" msgid="2636414079905220518">"Iziphakamiso ze-Biagram"</string>
+    <string name="bigram_suggestion_summary" msgid="4383845146070101531">"Sebenzisa igama elandulele ukuthuthukisa okusikiselwayo"</string>
+    <!-- no translation found for bigram_prediction (8914273444762259739) -->
+    <skip />
+    <!-- no translation found for bigram_prediction_summary (1747261921174300098) -->
+    <skip />
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Kulondoloziwe"</string>
+    <string name="label_go_key" msgid="1635148082137219148">"Iya"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Okulandelayo"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Kwenziwe"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Thumela"</string>
+    <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_more_key" msgid="3760239494604948502">"Okungaphezulu"</string>
+    <string name="label_pause_key" msgid="181098308428035340">"Misa okwesikhashana"</string>
+    <string name="label_wait_key" msgid="6402152600878093134">"Linda"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Okungenayo kwezwi"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Okungenayo kwezwi akusekelwa kolimi lwakho, kodwa kuyasebenza Ngesingisi."</string>
+    <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Okufakwayo kwezwi kusebenzisa ukuqaphela izwi le-Google. "<a href="http://m.google.com/privacy">"Inqubomgomo Yobumfihlo Yefoni"</a>" iyasebenza."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="3190378129944934856">"Ukuvala okufakwayo kwezwi, iya kuzilungiselelo zendlela yokufakwayo"</string>
+    <string name="voice_hint_dialog_message" msgid="1420686286820661548">"Ukusebenzisa okufakwayo kwezwi, cindezela inkinobho yemakrofoni."</string>
+    <string name="voice_listening" msgid="467518160751321844">"Khuluma manje"</string>
+    <string name="voice_working" msgid="6666937792815731889">"Kuyasebenza"</string>
+    <string name="voice_initializing" msgid="661962047129906646"></string>
+    <string name="voice_error" msgid="5140896300312186162">"Iphutha. Sicela uzame futhi."</string>
+    <string name="voice_network_error" msgid="6649556447401862563">"Ayikwazanga ukuxhuma"</string>
+    <string name="voice_too_much_speech" msgid="5746973620134227376">"Iphutha, kunamagama amaningi."</string>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Inkinga yomsindo"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Iphutha leseva"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Awekho amagama azwakele"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Akukho okufanayo okutholiwe"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Ukusesha ngezwi akufakiwe"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Isexwayiso:"</b>"Shintshela kwikhibhodi ukuze ukhulume"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Isixwayiso: "</b>"Esikhathini esilandelayo, zama ukukhuluma izimpimiselo ezinjengo \"isikhathi, \"ikhefu\" noma \"uphawu lombuzo\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Khansela"</string>
+    <string name="ok" msgid="7898366843681727667">"KULUNGILE"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Okungenayo kwezwi"</string>
+    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Kwikhibhodi eyisisekelo"</string>
+    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Ikhibhodi yezimpawu"</string>
+    <string name="voice_input_modes_off" msgid="3745699748218082014">"VALIWE"</string>
+    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"I-mic kwikhibhodi eyisisekelo"</string>
+    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Ikhibhodi yezimpawu ze-mic"</string>
+    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Okufakwayo kwezwi kuvimbelwe"</string>
+    <string name="selectInputMethod" msgid="315076553378705821">"Khetha indlela yokungenayo"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Izilimi zokufakwayo"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Slayida umunwe kwibha yesikhala ukushintsha ulimi"</string>
+    <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Thinta futhi ukulondoloza"</string>
+    <string name="has_dictionary" msgid="6071847973466625007">"Isichazamazwi siyatholakala"</string>
+    <string name="prefs_enable_log" msgid="6620424505072963557">"Vumela impendulo yomsebenzisi"</string>
+    <string name="prefs_description_log" msgid="5827825607258246003">"Siza ukuthuthukisa umhleli wendlela yokungenayo ngokuthumela izibalo zokubala nokuphahlaza imibiko e-Google."</string>
+    <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Thinta ukulungisa amagama"</string>
+    <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Thinta amagama afakiwe ukuwalungisa, kuphela lapho okusikiselwayo kubonakala"</string>
+    <string name="keyboard_layout" msgid="437433231038683666">"Indikimba Yekhibhodi"</string>
+    <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Ikhibhodi yesi-Czech"</string>
+    <!-- no translation found for subtype_mode_ar_keyboard (2655338636329774995) -->
+    <skip />
+    <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Ikhibhodi yesi-Danish"</string>
+    <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Ikhobhodi yei-German"</string>
+    <string name="subtype_mode_en_GB_keyboard" msgid="7945856548410373708">"Ikhibhodi ye-English (UK)"</string>
+    <string name="subtype_mode_en_US_keyboard" msgid="3708655163769735410">"Ikhobhodi ye-English (US)"</string>
+    <string name="subtype_mode_es_keyboard" msgid="1775125478866113148">"Ikhobhodi yesi-Spanish"</string>
+    <!-- no translation found for subtype_mode_fi_keyboard (3198596464082614532) -->
+    <skip />
+    <string name="subtype_mode_fr_keyboard" msgid="8016515336759761014">"Ikhibhodi yesi-French"</string>
+    <string name="subtype_mode_fr_CA_keyboard" msgid="2628517247158376263">"Ikhobhodi yesi-French (i-Canada)"</string>
+    <string name="subtype_mode_fr_CH_keyboard" msgid="6742806653181621228">"Ikhibhodi yesi-French (i-Switzerland"</string>
+    <!-- no translation found for subtype_mode_hr_keyboard (7177182405440070112) -->
+    <skip />
+    <!-- no translation found for subtype_mode_hu_keyboard (8843338355732633647) -->
+    <skip />
+    <!-- no translation found for subtype_mode_iw_keyboard (1787536828253289950) -->
+    <skip />
+    <string name="subtype_mode_it_keyboard" msgid="4934199655425394484">"Ikhibhodi yesi-Italian"</string>
+    <string name="subtype_mode_nb_keyboard" msgid="1175783216100212360">"Ikhibhodi yesi-Norwegian"</string>
+    <string name="subtype_mode_nl_keyboard" msgid="5090278083256037936">"Ikhibhodi yesi-Dutch"</string>
+    <!-- no translation found for subtype_mode_pl_keyboard (2225816414814396047) -->
+    <skip />
+    <!-- no translation found for subtype_mode_pt_keyboard (7503997804861754840) -->
+    <skip />
+    <string name="subtype_mode_ru_keyboard" msgid="1383995915064277943">"Ikhobhodi yesi-Russian"</string>
+    <string name="subtype_mode_sr_keyboard" msgid="5019440799612208168">"Ikhibhodi yesi-Serbian"</string>
+    <string name="subtype_mode_sv_keyboard" msgid="4933838139861753401">"Ikhobhodi yesi-Swedish"</string>
+    <!-- no translation found for subtype_mode_af_voice (7542487489657902699) -->
+    <skip />
+    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Izwi le-Czech"</string>
+    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Izwi lesi-German"</string>
+    <!-- no translation found for subtype_mode_en_voice (6643420989651848728) -->
+    <skip />
+    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Izwi lesi-Spanish"</string>
+    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Izwi lesi-French"</string>
+    <!-- no translation found for subtype_mode_it_voice (5077373057157441323) -->
+    <skip />
+    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Izwi lesi-Japanese"</string>
+    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Izwi lesi-Korean"</string>
+    <!-- no translation found for subtype_mode_nl_voice (2603552312869575021) -->
+    <skip />
+    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Izwi lesi-Polish"</string>
+    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Izwi lesi-Portuguese"</string>
+    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Izwi lesi-Russian"</string>
+    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Izwi lesi-Turkish"</string>
+    <!-- no translation found for subtype_mode_yue_voice (1576887891614624263) -->
+    <skip />
+    <!-- no translation found for subtype_mode_zh_voice (4360533229467271152) -->
+    <skip />
+    <!-- no translation found for subtype_mode_zu_voice (1146122571698884636) -->
+    <skip />
+    <string name="prefs_usability_study_mode" msgid="6937813623647419810">"Imodi Yesitadi Yokusebenziseka"</string>
+</resources>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index a015d6b..5c00510 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -15,34 +15,56 @@
 -->
 
 <resources>
+    <declare-styleable name="KeyboardTheme">
+        <!-- Keyboard style -->
+        <attr name="keyboardStyle" format="reference" />
+        <!-- LatinKeyboard style -->
+        <attr name="latinKeyboardStyle" format="reference" />
+        <!-- KeyboardView style -->
+        <attr name="keyboardViewStyle" format="reference" />
+        <attr name="keyPreviewStyle" format="reference" />
+        <!-- PopupMiniKeyboardView style -->
+        <attr name="popupMiniKeyboardViewStyle" format="reference" />
+        <attr name="popupMiniKeyboardPanelStyle" format="reference" />
+        <!-- Suggestions strip style -->
+        <attr name="suggestionsStripBackgroundStyle" format="reference" />
+        <attr name="suggestionBackgroundStyle" format="reference" />
+        <attr name="suggestionPreviewBackgroundStyle" format="reference" />
+        <attr name="candidateViewStyle" format="reference" />
+    </declare-styleable>
 
     <declare-styleable name="KeyboardView">
-        <!-- Default KeyboardView style. -->
-        <attr name="keyboardViewStyle" format="reference" />
-
         <!-- Image for the key. This image needs to be a StateListDrawable, with the following
              possible states: normal, pressed, checkable, checkable+pressed, checkable+checked,
              checkable+checked+pressed. -->
         <attr name="keyBackground" format="reference" />
 
-        <!-- Size of the text for one letter character keys. -->
-        <attr name="keyLetterSize" format="dimension" />
-
-        <!-- Size of the text for custom keys with some text and no icon. -->
-        <attr name="labelTextSize" format="dimension" />
+        <!-- Size of the text for one letter keys, in the proportion of key height. -->
+        <attr name="keyLetterRatio" format="float" />
+        <!-- Size of the text for keys with some text, in the proportion of key height. -->
+        <attr name="keyLabelRatio" format="float" />
+        <!-- Size of the text for hint letter, in the proportion of key height. -->
+        <attr name="keyHintLetterRatio" format="float" />
+        <!-- Size of the text for upper case letter, in the proportion of key height. -->
+        <attr name="keyUppercaseLetterRatio" format="float" />
 
         <!-- Color to use for the label in a key. -->
         <attr name="keyTextColor" format="color" />
+        <!-- Color to use for the label in a key when in inactivated state. -->
+        <attr name="keyTextInactivatedColor" format="color" />
+        <!-- Key hint letter color -->
+        <attr name="keyHintLetterColor" format="color" />
+        <!-- Upper case letter colors -->
+        <attr name="keyUppercaseLetterInactivatedColor" format="color" />
+        <attr name="keyUppercaseLetterActivatedColor" format="color" />
 
-        <!-- Color to use for the label in a key when in disabled state. -->
-        <attr name="keyTextColorDisabled" format="color" />
+        <!-- Popup hint icon "..." -->
+        <attr name="keyPopupHintIcon" format="reference" />
 
         <!-- Layout resource for key press feedback.-->
         <attr name="keyPreviewLayout" format="reference" />
-
         <!-- Vertical offset of the key press feedback from the key. -->
         <attr name="keyPreviewOffset" format="dimension" />
-
         <!-- Height of the key press feedback popup. -->
         <attr name="keyPreviewHeight" format="dimension" />
 
@@ -59,20 +81,26 @@
         <attr name="shadowRadius" format="float" />
         <attr name="backgroundDimAmount" format="float" />
 
-        <attr name="keyLetterStyle">
+        <attr name="keyTextStyle" format="enum">
             <!-- This should be aligned with Typeface.NORMAL etc. -->
             <enum name="normal" value="0" />
             <enum name="bold" value="1" />
             <enum name="italic" value="2" />
             <enum name="boldItalic" value="3" />
         </attr>
+    </declare-styleable>
 
-        <attr name="colorScheme">
-            <!-- This should be aligned with KeyboardView.COLOR_SCHEME_* -->
-            <enum name="white" value="0" />
-            <enum name="black" value="1" />
+    <declare-styleable name="CandidateView">
+        <attr name="autoCorrectHighlight" format="integer">
+            <flag name="autoCorrectBold" value="0x01" />
+            <flag name="autoCorrectUnderline" value="0x02" />
+            <flag name="autoCorrectInvert" value="0x04" />
         </attr>
-
+        <attr name="colorTypedWord" format="color" />
+        <attr name="colorAutoCorrect" format="color" />
+        <attr name="colorSuggested" format="color" />
+        <attr name="iconExpandPane" format="reference" />
+        <attr name="iconClosePane" format="reference" />
     </declare-styleable>
 
     <declare-styleable name="Keyboard">
@@ -80,15 +108,24 @@
         <attr name="keyboardHeight" format="dimension" />
         <!-- Maximum keyboard height, in pixels or percentage of display height -->
         <attr name="maxKeyboardHeight" format="dimension|fraction" />
-        <!-- Default width of a key, in pixels or percentage of display width. -->
-        <attr name="keyWidth" format="dimension|fraction" />
-        <!-- Default height of a row (key height + vertical gap), in pixels or percentage of
-             keyboard height.
+        <!-- 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. -->
+        <attr name="keyboardTopPadding" format="dimension" />
+        <attr name="keyboardBottomPadding" format="dimension" />
+        <!-- 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="0" />
+            <enum name="fillBoth" value="-1" />
+        </attr>
+        <!-- Default height of a row (key height + vertical gap), in pixels or percentage of
+             keyboard height. -->
         <attr name="rowHeight" format="dimension|fraction" />
         <!-- Default horizontal gap between keys. -->
         <attr name="horizontalGap" format="dimension|fraction" />
@@ -96,6 +133,34 @@
         <attr name="verticalGap" format="dimension|fraction" />
         <!-- Popup keyboard layout template -->
         <attr name="popupKeyboardTemplate" format="reference" />
+        <!-- Locale of the keyboard layout -->
+        <attr name="keyboardLocale" format="string" />
+        <!-- Icon set for key top and key preview. -->
+        <attr name="iconShiftKey" format="reference" />
+        <attr name="iconToSymbolKey" format="reference" />
+        <attr name="iconToSymbolKeyWithShortcut" format="reference" />
+        <attr name="iconDeleteKey" format="reference" />
+        <attr name="iconSettingsKey" format="reference" />
+        <attr name="iconShortcutKey" format="reference" />
+        <attr name="iconSpaceKey" format="reference" />
+        <attr name="iconReturnKey" format="reference" />
+        <attr name="iconSearchKey" format="reference" />
+        <attr name="iconTabKey" format="reference" />
+        <attr name="iconNum1Key" format="reference" />
+        <attr name="iconNum2Key" format="reference" />
+        <attr name="iconNum3Key" format="reference" />
+        <attr name="iconNum4Key" format="reference" />
+        <attr name="iconNum5Key" format="reference" />
+        <attr name="iconNum6Key" format="reference" />
+        <attr name="iconNum7Key" format="reference" />
+        <attr name="iconNum8Key" format="reference" />
+        <attr name="iconNum9Key" format="reference" />
+        <attr name="iconNum0Key" format="reference" />
+        <attr name="iconShiftedShiftKey" format="reference" />
+        <attr name="iconPreviewSpaceKey" format="reference" />
+        <attr name="iconPreviewTabKey" format="reference" />
+        <attr name="iconPreviewSettingsKey" format="reference" />
+        <attr name="iconPreviewShortcutKey" format="reference" />
     </declare-styleable>
 
     <declare-styleable name="Keyboard_Key">
@@ -106,47 +171,80 @@
         <!-- Maximum column of popup keyboard -->
         <attr name="maxPopupKeyboardColumn" format="integer" />
         <!-- Key edge flags. -->
-        <attr name="keyEdgeFlags">
+        <attr name="keyEdgeFlags" format="integer">
             <!-- Key is anchored to the left of the keyboard. -->
             <flag name="left" value="1" />
             <!-- Key is anchored to the right of the keyboard. -->
             <flag name="right" value="2" />
         </attr>
-        <!-- Whether this is a modifier key such as Alt or Shift. -->
-        <attr name="isModifier" format="boolean" />
+        <!-- Whether this is a functional key which has different key top than normal key. -->
+        <attr name="isFunctional" format="boolean" />
         <!-- Whether this is a toggle key. -->
         <attr name="isSticky" format="boolean" />
         <!-- Whether long-pressing on this key will make it repeat. -->
         <attr name="isRepeatable" format="boolean" />
-        <!-- The icon to show in the popup preview. -->
-        <attr name="iconPreview" format="reference" />
         <!-- The string of characters to output when this key is pressed. -->
         <attr name="keyOutputText" format="string" />
         <!-- The label to display on the key. -->
         <attr name="keyLabel" format="string" />
-        <!-- The key label option -->
-        <attr name="keyLabelOption">
-            <!-- This should be aligned with KeyboardView.KEY_LABEL_OPTION_* -->
-            <flag name="alignLeft" value="1" />
-            <flag name="alignRight" value="2" />
-            <flag name="alignBottom" value="8" />
-            <flag name="fontNormal" value="16" />
+        <!-- The hint letter to display on the key in conjunction with the label. -->
+        <attr name="keyHintLetter" format="string" />
+        <!-- The key label option. -->
+        <attr name="keyLabelOption" format="integer">
+            <!-- This should be aligned with Key.LABEL_OPTION_* -->
+            <flag name="alignLeft" value="0x01" />
+            <flag name="alignRight" value="0x02" />
+            <flag name="alignBottom" value="0x08" />
+            <flag name="fontNormal" value="0x10" />
+            <flag name="fontFixedWidth" value="0x20" />
+            <flag name="followKeyLetterRatio" value="0x40" />
+            <flag name="popupHint" value="0x80" />
+            <flag name="hasUppercaseLetter" value="0x100" />
         </attr>
-        <!-- The unicode that this key generates in manual temporary upper case mode. -->
-        <attr name="manualTemporaryUpperCaseCode" format="integer" />
         <!-- The icon to display on the key instead of the label. -->
-        <attr name="keyIcon" format="reference" />
-        <!-- The hint icon to display on the key in conjunction with the label -->
-        <attr name="keyHintIcon" format="reference" />
-        <!-- The hint icon to display on the key when keyboard is in manual temporary upper case
-             mode. -->
-        <attr name="manualTemporaryUpperCaseHintIcon" format="reference" />
+        <attr name="keyIcon" format="enum">
+            <!-- This should be aligned with KeyboardIcons.ICON_* -->
+            <enum name="iconShiftKey" value="1" />
+            <enum name="iconToSymbolKey" value="2" />
+            <enum name="iconToSymbolKeyWithShortcut" value="3" />
+            <enum name="iconDeleteKey" value="4" />
+            <enum name="iconSettingsKey" value="5" />
+            <enum name="iconShortcutKey" value="6" />
+            <enum name="iconSpaceKey" value="7" />
+            <enum name="iconReturnKey" value="8" />
+            <enum name="iconSearchKey" value="9" />
+            <enum name="iconTabKey" value="10" />
+            <enum name="iconNum1Key" value="11" />
+            <enum name="iconNum2Key" value="12" />
+            <enum name="iconNum3Key" value="13" />
+            <enum name="iconNum4Key" value="14" />
+            <enum name="iconNum5Key" value="15" />
+            <enum name="iconNum6Key" value="16" />
+            <enum name="iconNum7Key" value="17" />
+            <enum name="iconNum8Key" value="18" />
+            <enum name="iconNum9Key" value="19" />
+            <enum name="iconNum0Key" value="20" />
+        </attr>
+        <!-- Shift key icon for shifted state -->
+        <attr name="keyIconShifted" format="enum">
+            <!-- This should be aligned with KeyboardIcons.ICON_SHIFTED_* -->
+            <enum name="iconShiftedShiftKey" value="21" />
+        </attr>
+        <!-- The icon to show in the popup preview. -->
+        <attr name="keyIconPreview" format="enum">
+            <!-- This should be aligned with KeyboardIcons.ICON_PREVIEW_* -->
+            <enum name="iconPreviewSpaceKey" value="22" />
+            <enum name="iconPreviewTabKey" value="23" />
+            <enum name="iconPreviewSettingsKey" value="24" />
+            <enum name="iconPreviewShortcutKey" value="25" />
+        </attr>
         <!-- The key style to specify a set of key attributes defined by <key_style/> -->
         <attr name="keyStyle" format="string" />
-        <!-- Shift key icon for shifted state -->
-        <attr name="shiftedIcon" format="reference" />
         <!-- The key is enabled and responds on press. -->
         <attr name="enabled" format="boolean" />
+        <!-- Visual insets -->
+        <attr name="visualInsetsLeft" format="dimension|fraction" />
+        <attr name="visualInsetsRight" format="dimension|fraction" />
         <!-- 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" />
@@ -154,7 +252,7 @@
 
     <declare-styleable name="Keyboard_Row">
         <!-- Row edge flags. -->
-        <attr name="rowEdgeFlags">
+        <attr name="rowEdgeFlags" format="integer">
             <!-- Row is anchored to the top of the keyboard. -->
             <flag name="top" value="4" />
             <!-- Row is anchored to the bottom of the keyboard. -->
@@ -168,7 +266,7 @@
 
     <declare-styleable name="Keyboard_Case">
         <!-- This should be aligned with KeyboardId.MODE_* -->
-        <attr name="mode">
+        <attr name="mode" format="enum|string">
             <enum name="text" value="0" />
             <enum name="url" value="1" />
             <enum name="email" value="2" />
@@ -176,30 +274,23 @@
             <enum name="phone" value="4" />
             <enum name="number" value="5" />
         </attr>
-        <attr name="webInput" format="boolean" />
+        <attr name="navigateAction" format="boolean" />
         <attr name="passwordInput" format="boolean" />
         <attr name="hasSettingsKey" format="boolean" />
-        <!-- This should be aligned with KeyboardID.F2KEY_MODE_* -->
-        <attr name="f2KeyMode" format="enum">
-            <enum name="none" value="0" />
-            <enum name="settings" value="1" />
-            <enum name="shortcutIme" value="2" />
-            <enum name="shortcutImeOrSettings" value="3" />
-        </attr>
-        <attr name="clobberSettingsKey" format="boolean" />
         <attr name="voiceKeyEnabled" format="boolean" />
         <attr name="hasVoiceKey" format="boolean" />
-        <attr name="imeAction">
+        <attr name="imeAction" format="enum">
             <!-- This should be aligned with EditorInfo.IME_ACTION_* -->
-            <flag name="actionUnspecified" value="0" />
-            <flag name="actionNone" value="1" />
-            <flag name="actionGo" value="2" />
-            <flag name="actionSearch" value="3" />
-            <flag name="actionSend" value="4" />
-            <flag name="actionNext" value="5" />
-            <flag name="actionDone" value="6" />
-            <flag name="actionPrevious" value="7" />
+            <enum name="actionUnspecified" value="0" />
+            <enum name="actionNone" value="1" />
+            <enum name="actionGo" value="2" />
+            <enum name="actionSearch" value="3" />
+            <enum name="actionSend" value="4" />
+            <enum name="actionNext" value="5" />
+            <enum name="actionDone" value="6" />
+            <enum name="actionPrevious" value="7" />
         </attr>
+        <attr name="localeCode" format="string" />
         <attr name="languageCode" format="string" />
         <attr name="countryCode" format="string" />
     </declare-styleable>
@@ -208,4 +299,16 @@
         <attr name="styleName" format="string" />
         <attr name="parentStyle" format="string" />
     </declare-styleable>
+
+    <declare-styleable name="LatinKeyboard">
+        <attr name="autoCorrectionSpacebarLedEnabled" format="boolean" />
+        <attr name="autoCorrectionSpacebarLedIcon" format="reference" />
+        <attr name="disabledShortcutIcon" format="reference" />
+        <attr name="spacebarTextColor" format="color" />
+        <attr name="spacebarTextShadowColor" format="color" />
+        <attr name="spacebarArrowLeftIcon" format="reference" />
+        <attr name="spacebarArrowRightIcon" format="reference" />
+        <attr name="spacebarArrowPreviewLeftIcon" format="reference" />
+        <attr name="spacebarArrowPreviewRightIcon" format="reference" />
+    </declare-styleable>
 </resources>
diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml
deleted file mode 100644
index 741171f..0000000
--- a/java/res/values/colors.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* 
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
--->
-<resources>
-    <color name="candidate_normal">#FFFFFFFF</color>
-    <color name="candidate_recommended">#FFFCAE00</color>
-    <color name="candidate_other">#FFFCAE00</color>
-    <color name="latinkeyboard_transparent">#00000000</color>
-    <color name="latinkeyboard_bar_language_shadow_white">#80000000</color>
-    <color name="latinkeyboard_bar_language_shadow_black">#80FFFFFF</color>
-    <color name="latinkeyboard_bar_language_text">#FFC0C0C0</color>
-    <color name="latinkeyboard_feedback_language_text">#FFFFFFFF</color>
-    <color name="latinkeyboard_key_color_white">#FFFFFFFF</color>
-    <color name="latinkeyboard_key_color_black">#FF000000</color>
-</resources>
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 7c100c3..ad580cc 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -21,14 +21,12 @@
 <resources>
     <bool name="config_swipeDisambiguation">true</bool>
     <bool name="config_enable_show_settings_key_option">true</bool>
-    <bool name="config_enable_show_subtype_settings">true</bool>
     <bool name="config_enable_show_voice_key_option">true</bool>
     <bool name="config_enable_show_popup_on_keypress_option">true</bool>
     <bool name="config_enable_show_recorrection_option">true</bool>
     <bool name="config_enable_quick_fixes_option">true</bool>
     <bool name="config_enable_bigram_suggestions_option">true</bool>
     <bool name="config_enable_usability_study_mode_option">false</bool>
-    <bool name="config_candidate_highlight_font_color_enabled">true</bool>
     <bool name="config_swipe_down_dismiss_keyboard_enabled">true</bool>
     <bool name="config_sliding_key_input_enabled">true</bool>
     <bool name="config_digit_popup_characters_enabled">true</bool>
@@ -36,20 +34,29 @@
     <bool name="config_default_popup_preview">true</bool>
     <!-- Default values for whether quick fixes and bigram suggestions are activated -->
     <bool name="config_default_quick_fixes">true</bool>
+    <!-- Default value for bigram suggestion: while showing candidates for a word should we weigh
+         in the previous word? -->
     <bool name="config_default_bigram_suggestions">true</bool>
+    <!-- Default value for bigram prediction: after entering a word and a space only, should we look
+         at input history to suggest a hopefully helpful candidate for the next word? -->
+    <bool name="config_default_bigram_prediction">false</bool>
     <bool name="config_default_recorrection_enabled">true</bool>
     <bool name="config_default_sound_enabled">false</bool>
+    <bool name="config_auto_correction_spacebar_led_enabled">true</bool>
     <bool name="config_use_spacebar_language_switcher">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>
     <!-- The language is never displayed if == 0, always displayed if < 0 -->
-    <integer name="config_delay_before_fadeout_language_on_spacebar">-1</integer>
+    <integer name="config_delay_before_fadeout_language_on_spacebar">1200</integer>
+    <integer name="config_delay_update_suggestions">100</integer>
+    <integer name="config_delay_update_old_suggestions">300</integer>
+    <integer name="config_delay_update_shift_state">100</integer>
     <integer name="config_duration_of_fadeout_language_on_spacebar">50</integer>
-    <integer name="config_final_fadeout_percentage_of_language_on_spacebar">15</integer>
+    <integer name="config_final_fadeout_percentage_of_language_on_spacebar">50</integer>
     <integer name="config_delay_before_preview">0</integer>
-    <integer name="config_delay_after_preview">10</integer>
-    <integer name="config_preview_fadein_anim_time">0</integer>
-    <integer name="config_preview_fadeout_anim_time">70</integer>
+    <integer name="config_delay_after_preview">70</integer>
+    <integer name="config_mini_keyboard_fadein_anim_time">0</integer>
+    <integer name="config_mini_keyboard_fadeout_anim_time">100</integer>
     <integer name="config_delay_before_key_repeat_start">400</integer>
     <integer name="config_key_repeat_interval">50</integer>
     <integer name="config_keyboard_grid_width">32</integer>
@@ -60,7 +67,7 @@
     <integer name="config_double_spaces_turn_into_period_timeout">1100</integer>
     <dimen name="config_touch_noise_threshold_distance">2.0mm</dimen>
     <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
-    <string name="config_default_keyboard_theme_id" translatable="false">4</string>
+    <string name="config_default_keyboard_theme_id" translatable="false">5</string>
     <string name="config_text_size_of_language_on_spacebar" translatable="false">small</string>
     <integer name="config_max_popup_keyboard_column">5</integer>
     <string-array name="auto_correction_threshold_values" translatable="false">
@@ -73,4 +80,7 @@
              will be subject to auto-correction. -->
         <item>0</item>
     </string-array>
+    <!--  Screen metrics for logging. 0 = "mdpi", 1 = "hdpi", 2 = "xlarge" -->
+    <integer name="log_screen_metrics">0</integer>
+    <bool name="config_require_umlaut_processing">false</bool>
 </resources>
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index d28b923..665245c 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -19,41 +19,56 @@
 -->
 
 <resources>
-    <!-- keyboardHeight = key_height*4 + key_bottom_gap*3 -->
-    <dimen name="keyboardHeight">1.265in</dimen>
+    <!-- keyboardHeight = key_height*4 + key_bottom_gap*3, key_height=0.295in -->
+    <dimen name="keyboardHeight">1.285in</dimen>
+    <fraction name="maxKeyboardHeight">50%p</fraction>
+    <fraction name="minKeyboardHeight">-61.8%p</fraction>
     <!-- key_height + key_bottom_gap = popup_key_height -->
-    <!-- <dimen name="key_height">0.290in</dimen> -->
+    <!-- <dimen name="key_height">0.295in</dimen> -->
     <dimen name="key_bottom_gap">0.035in</dimen>
     <dimen name="key_horizontal_gap">0.000in</dimen>
-    <dimen name="popup_key_height">0.325in</dimen>
+    <dimen name="popup_key_height">0.330in</dimen>
     <dimen name="keyboard_top_padding">0.00in</dimen>
     <dimen name="keyboard_bottom_padding">0.06in</dimen>
-    <!-- key_preview_text_size_large x 2 -->
-    <dimen name="key_preview_height">80sp</dimen>
+    <dimen name="key_bottom_gap_stone">0.00in</dimen>
+    <dimen name="key_horizontal_gap_stone">0.00in</dimen>
+    <dimen name="key_bottom_gap_ics">0.06in</dimen>
+    <dimen name="key_horizontal_gap_ics">0.01in</dimen>
+    <dimen name="keyboard_top_padding_ics">0.03in</dimen>
+    <dimen name="keyboard_bottom_padding_ics">0.06in</dimen>
+    <dimen name="keyboard_horizontal_edges_padding">0.0in</dimen>
+    <dimen name="mini_keyboard_horizontal_padding">16dip</dimen>
+    <dimen name="mini_keyboard_horizontal_padding_holo">38dip</dimen>
     <dimen name="mini_keyboard_key_horizontal_padding">8dip</dimen>
     <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. -->
     <!-- popup_key_height x 1.2 -->
-    <dimen name="mini_keyboard_slide_allowance">0.390in</dimen>
+    <dimen name="mini_keyboard_slide_allowance">0.396in</dimen>
     <!-- popup_key_height x -1.0 -->
-    <dimen name="mini_keyboard_vertical_correction">-0.325in</dimen>
-
-    <dimen name="key_letter_size">0.13in</dimen>
-    <dimen name="key_label_text_size">0.083in</dimen>
-    <dimen name="key_preview_text_size_large">40sp</dimen>
-    <!-- left or right padding of label alignment -->
-    <dimen name="key_label_horizontal_alignment_padding">0.13in</dimen>
-    <dimen name="key_preview_offset">0.000in</dimen>
+    <dimen name="mini_keyboard_vertical_correction">-0.330in</dimen>
     <!-- We use "inch", not "dip" because this value tries dealing with physical distance related
          to user's finger. -->
     <dimen name="keyboard_vertical_correction">-0.05in</dimen>
 
+    <fraction name="key_letter_ratio">45%</fraction>
+    <fraction name="key_label_ratio">29%</fraction>
+    <fraction name="key_hint_letter_ratio">23%</fraction>
+    <fraction name="key_uppercase_letter_ratio">35%</fraction>
+    <!-- left or right padding of label alignment -->
+    <dimen name="key_label_horizontal_alignment_padding">21dip</dimen>
+    <dimen name="key_preview_height">80sp</dimen>
+    <dimen name="key_preview_offset">0.1in</dimen>
+    <fraction name="key_preview_text_ratio">82%</fraction>
+    <dimen name="key_preview_offset_ics">0.05in</dimen>
+
     <dimen name="candidate_strip_height">42dip</dimen>
+    <!-- candidate_strip_minimum_height =
+         key_preview_height_holo - key_preview_offset_holo + alpha -->
+    <dimen name="candidate_strip_minimum_height">100sp</dimen>
     <dimen name="candidate_strip_fading_edge_length">63dip</dimen>
     <dimen name="candidate_strip_padding">0dip</dimen>
-    <dimen name="candidate_min_width">0.3in</dimen>
+    <dimen name="candidate_min_width">32dip</dimen>
     <dimen name="candidate_padding">6dip</dimen>
     <dimen name="candidate_text_size">18dip</dimen>
-    <dimen name="spacebar_vertical_correction">4dip</dimen>
     <!-- If the screen height in landscape is larger than the below value, then the keyboard
          will not go into extract (fullscreen) mode. -->
     <dimen name="max_height_for_fullscreen">2.5in</dimen>
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index 6a1069e..1c198d9 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -18,12 +18,19 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Symbols that are commonly considered word separators in this language -->
-    <string name="word_separators">.\u0009\u0020,;:!?\n()[]*&amp;@{}/&lt;&gt;_+=|\u0022</string>
-    <!-- Symbols that are sentence separators, for purposes of making it hug the last sentence. -->
-    <string name="sentence_separators">.,!?)</string>
     <!-- Symbols that are suggested between words -->
     <string name="suggested_punctuations">!?,\u0022\u0027:();-/@_</string>
+    <!-- Symbols that should be swapped with a magic space -->
+    <string name="magic_space_swapping_symbols">.,;:!?)]}\u0022</string>
+    <!-- Symbols that should strip a magic space -->
+    <string name="magic_space_stripping_symbols">\u0009\u0020\n/_\u0027-</string>
+    <!-- Symbols that should convert magic spaces into real space -->
+    <string name="magic_space_promoting_symbols">([*&amp;@{&lt;&gt;+=|</string>
+    <!-- Symbols that do NOT separate words -->
+    <string name="non_word_separator_symbols">\u0027-</string>
+    <!-- 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 \ non_word_separator_symbols -->
 
     <!-- Label for ALT modifier key.  Must be short to fit on key! -->
     <string name="label_alt_key">ALT</string>
@@ -31,8 +38,10 @@
     <string name="label_tab_key">Tab</string>
     <!-- Label for "switch to symbols" key.  Must be short to fit on key! -->
     <string name="label_to_symbol_key">\?123</string>
-    <!-- Label for "switch to numeric" key.  Must be short to fit on key! -->
-    <string name="label_to_numeric_key">123</string>
+    <!-- Label for "switch to phone numeric" key.  Must be short to fit on key! -->
+    <string name="label_to_phone_numeric_key">123</string>
+    <!-- Label for "switch to phone symbols" key.  Must be short to fit on key! -->
+    <string name="label_to_phone_symbols_key">\uff0a\uff03\uff08</string>
 
     <!-- Option values to show/hide the settings key in onscreen keyboard -->
     <!-- Automatically decide to show or hide the settings key -->
@@ -119,7 +128,7 @@
     <string name="layout_stone_bold">Stone (bold)</string>
     <string name="layout_stone_normal">Stone (normal)</string>
     <string name="layout_gingerbread">Gingerbread</string>
-    <string name="layout_honeycomb">Honeycomb</string>
+    <string name="layout_ics">IceCreamSandwich</string>
 
     <!-- For keyboard theme switcher dialog -->
     <string-array name="keyboard_layout_modes">
@@ -128,7 +137,7 @@
         <item>@string/layout_stone_normal</item>
         <item>@string/layout_stone_bold</item>
         <item>@string/layout_gingerbread</item>
-        <item>@string/layout_honeycomb</item>
+        <item>@string/layout_ics</item>
     </string-array>
     <string-array name="keyboard_layout_modes_values">
         <item>0</item>
@@ -143,9 +152,11 @@
     <string-array name="subtype_locale_exception_keys">
         <item>en_US</item>
         <item>en_GB</item>
+        <item>de_ZZ</item>
     </string-array>
     <string-array name="subtype_locale_exception_values">
         <item>English (US)</item>
         <item>English (UK)</item>
+        <item>Deutsch (QWERTY)</item>
     </string-array>
 </resources>
diff --git a/java/res/values/keyboard-icons-black.xml b/java/res/values/keyboard-icons-black.xml
new file mode 100644
index 0000000..3ba3455
--- /dev/null
+++ b/java/res/values/keyboard-icons-black.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <style name="KeyboardIcons.Black" parent="android:Theme.Light">
+        <!-- Keyboard icons -->
+        <item name="iconShiftKey">@drawable/sym_bkeyboard_shift</item>
+        <item name="iconToSymbolKeyWithShortcut">@drawable/sym_bkeyboard_123_mic</item>
+        <item name="iconDeleteKey">@drawable/sym_bkeyboard_delete</item>
+        <item name="iconSettingsKey">@drawable/sym_bkeyboard_settings</item>
+        <item name="iconShortcutKey">@drawable/sym_bkeyboard_mic</item>
+        <item name="iconSpaceKey">@drawable/sym_bkeyboard_space</item>
+        <item name="iconReturnKey">@drawable/sym_bkeyboard_return</item>
+        <item name="iconSearchKey">@drawable/sym_bkeyboard_search</item>
+        <item name="iconTabKey">@drawable/sym_bkeyboard_tab</item>
+        <item name="iconNum1Key">@drawable/sym_bkeyboard_num1</item>
+        <item name="iconNum2Key">@drawable/sym_bkeyboard_num2</item>
+        <item name="iconNum3Key">@drawable/sym_bkeyboard_num3</item>
+        <item name="iconNum4Key">@drawable/sym_bkeyboard_num4</item>
+        <item name="iconNum5Key">@drawable/sym_bkeyboard_num5</item>
+        <item name="iconNum6Key">@drawable/sym_bkeyboard_num6</item>
+        <item name="iconNum7Key">@drawable/sym_bkeyboard_num7</item>
+        <item name="iconNum8Key">@drawable/sym_bkeyboard_num8</item>
+        <item name="iconNum9Key">@drawable/sym_bkeyboard_num9</item>
+        <item name="iconNum0Key">@drawable/sym_bkeyboard_num0</item>
+        <item name="iconShiftedShiftKey">@drawable/sym_bkeyboard_shift_locked</item>
+        <item name="iconPreviewSpaceKey">@drawable/sym_keyboard_feedback_space</item>
+        <item name="iconPreviewTabKey">@drawable/sym_keyboard_feedback_tab</item>
+        <item name="iconPreviewSettingsKey">@drawable/sym_keyboard_feedback_settings</item>
+        <item name="iconPreviewShortcutKey">@drawable/sym_keyboard_feedback_mic</item>
+        <!-- LatinKeyboard icons -->
+        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item>
+        <item name="disabledShortcutIcon">@drawable/sym_keyboard_voice_off_holo</item>
+        <item name="spacebarArrowLeftIcon">@drawable/sym_keyboard_language_arrows_left</item>
+        <item name="spacebarArrowRightIcon">@drawable/sym_keyboard_language_arrows_right</item>
+        <item name="spacebarArrowPreviewLeftIcon">@drawable/sym_keyboard_feedback_language_arrows_left</item>
+        <item name="spacebarArrowPreviewRightIcon">@drawable/sym_keyboard_feedback_language_arrows_right</item>
+    </style>
+</resources>
diff --git a/java/res/values/keyboard-icons-ics.xml b/java/res/values/keyboard-icons-ics.xml
new file mode 100644
index 0000000..3029695
--- /dev/null
+++ b/java/res/values/keyboard-icons-ics.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <style name="KeyboardIcons.IceCreamSandwich" parent="android:Theme.Holo">
+        <!-- Keyboard icons -->
+<!--         <item name="iconShiftKey">@drawable/sym_keyboard_shift_holo</item> -->
+        <item name="iconShiftKey">@drawable/sym_keyboard_shift</item>
+<!--         <item name="iconToSymbolKeyWithShortcut">@drawable/sym_keyboard_123_voice_holo</item> -->
+        <item name="iconToSymbolKeyWithShortcut">@drawable/sym_keyboard_123_mic</item>
+<!--         <item name="iconDeleteKey">@drawable/sym_keyboard_delete_holo</item> -->
+        <item name="iconDeleteKey">@drawable/sym_keyboard_delete</item>
+<!--         <item name="iconSettingsKey">@drawable/sym_keyboard_settings_holo</item> -->
+        <item name="iconSettingsKey">@drawable/sym_keyboard_settings</item>
+<!--         <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo</item> -->
+        <item name="iconShortcutKey">@drawable/sym_keyboard_mic</item>
+<!--         <item name="iconSpaceKey">@drawable/sym_keyboard_space_holo</item> -->
+        <item name="iconSpaceKey">@drawable/sym_keyboard_space</item>
+<!--         <item name="iconReturnKey">@drawable/sym_keyboard_return_holo</item> -->
+        <item name="iconReturnKey">@drawable/sym_keyboard_return</item>
+<!--         <item name="iconSearchKey">@drawable/sym_keyboard_search_holo</item> -->
+        <item name="iconSearchKey">@drawable/sym_keyboard_search</item>
+<!--         <item name="iconTabKey">@drawable/sym_keyboard_tab_holo</item> -->
+        <item name="iconTabKey">@drawable/sym_keyboard_tab</item>
+<!--         <item name="iconNum1Key">@drawable/sym_keyboard_num1_holo</item> -->
+<!--         <item name="iconNum2Key">@drawable/sym_keyboard_num2_holo</item> -->
+<!--         <item name="iconNum3Key">@drawable/sym_keyboard_num3_holo</item> -->
+<!--         <item name="iconNum4Key">@drawable/sym_keyboard_num4_holo</item> -->
+<!--         <item name="iconNum5Key">@drawable/sym_keyboard_num5_holo</item> -->
+<!--         <item name="iconNum6Key">@drawable/sym_keyboard_num6_holo</item> -->
+<!--         <item name="iconNum7Key">@drawable/sym_keyboard_num7_holo</item> -->
+<!--         <item name="iconNum8Key">@drawable/sym_keyboard_num8_holo</item> -->
+<!--         <item name="iconNum9Key">@drawable/sym_keyboard_num9_holo</item> -->
+<!--         <item name="iconNum0Key">@drawable/sym_keyboard_num0_holo</item> -->
+<!--         <item name="iconNumStarKey">@drawable/sym_keyboard_numbstar_holo</item> -->
+<!--         <item name="iconNumPoundKey">@drawable/sym_keyboard_numbpound_holo</item> -->
+<!--         <item name="iconNumAltKey">@drawable/sym_keyboard_numalt_holo</item> -->
+        <item name="iconNum1Key">@drawable/sym_keyboard_num1</item>
+        <item name="iconNum2Key">@drawable/sym_keyboard_num2</item>
+        <item name="iconNum3Key">@drawable/sym_keyboard_num3</item>
+        <item name="iconNum4Key">@drawable/sym_keyboard_num4</item>
+        <item name="iconNum5Key">@drawable/sym_keyboard_num5</item>
+        <item name="iconNum6Key">@drawable/sym_keyboard_num6</item>
+        <item name="iconNum7Key">@drawable/sym_keyboard_num7</item>
+        <item name="iconNum8Key">@drawable/sym_keyboard_num8</item>
+        <item name="iconNum9Key">@drawable/sym_keyboard_num9</item>
+        <item name="iconNum0Key">@drawable/sym_keyboard_num0</item>
+<!--         <item name="iconShiftedShiftKey">@drawable/sym_keyboard_shift_locked_holo</item> -->
+        <item name="iconShiftedShiftKey">@drawable/sym_keyboard_shift_locked</item>
+        <item name="iconPreviewSpaceKey">@drawable/sym_keyboard_space_holo</item>
+<!--         <item name="iconPreviewTabKey">@drawable/sym_keyboard_tab_holo</item> -->
+        <item name="iconPreviewTabKey">@drawable/sym_keyboard_feedback_tab</item>
+<!--         <item name="iconPreviewSettingsKey">@drawable/sym_keyboard_settings_holo</item> -->
+        <item name="iconPreviewSettingsKey">@drawable/sym_keyboard_settings</item>
+<!--         <item name="iconPreviewShortcutKey">@drawable/sym_keyboard_feedback_voice_holo</item> -->
+        <item name="iconPreviewShortcutKey">@drawable/sym_keyboard_feedback_mic</item>
+        <!-- LatinKeyboard icons -->
+        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item>
+        <item name="disabledShortcutIcon">@drawable/sym_keyboard_voice_off_holo</item>
+        <item name="spacebarArrowLeftIcon">@drawable/sym_keyboard_language_arrows_left</item>
+        <item name="spacebarArrowRightIcon">@drawable/sym_keyboard_language_arrows_right</item>
+        <item name="spacebarArrowPreviewLeftIcon">@drawable/sym_keyboard_feedback_language_arrows_left</item>
+        <item name="spacebarArrowPreviewRightIcon">@drawable/sym_keyboard_feedback_language_arrows_right</item>
+    </style>
+</resources>
diff --git a/java/res/values/keyboard-icons-white.xml b/java/res/values/keyboard-icons-white.xml
new file mode 100644
index 0000000..9f5fb5a
--- /dev/null
+++ b/java/res/values/keyboard-icons-white.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <style name="KeyboardIcons" parent="android:Theme">
+        <!-- Keyboard icons -->
+        <item name="iconShiftKey">@drawable/sym_keyboard_shift</item>
+        <item name="iconToSymbolKeyWithShortcut">@drawable/sym_keyboard_123_mic</item>
+        <item name="iconDeleteKey">@drawable/sym_keyboard_delete</item>
+        <item name="iconSettingsKey">@drawable/sym_keyboard_settings</item>
+        <item name="iconShortcutKey">@drawable/sym_keyboard_mic</item>
+        <item name="iconSpaceKey">@drawable/sym_keyboard_space</item>
+        <item name="iconReturnKey">@drawable/sym_keyboard_return</item>
+        <item name="iconSearchKey">@drawable/sym_keyboard_search</item>
+        <item name="iconTabKey">@drawable/sym_keyboard_tab</item>
+        <item name="iconNum1Key">@drawable/sym_keyboard_num1</item>
+        <item name="iconNum2Key">@drawable/sym_keyboard_num2</item>
+        <item name="iconNum3Key">@drawable/sym_keyboard_num3</item>
+        <item name="iconNum4Key">@drawable/sym_keyboard_num4</item>
+        <item name="iconNum5Key">@drawable/sym_keyboard_num5</item>
+        <item name="iconNum6Key">@drawable/sym_keyboard_num6</item>
+        <item name="iconNum7Key">@drawable/sym_keyboard_num7</item>
+        <item name="iconNum8Key">@drawable/sym_keyboard_num8</item>
+        <item name="iconNum9Key">@drawable/sym_keyboard_num9</item>
+        <item name="iconNum0Key">@drawable/sym_keyboard_num0</item>
+        <item name="iconShiftedShiftKey">@drawable/sym_keyboard_shift_locked</item>
+        <item name="iconPreviewSpaceKey">@drawable/sym_keyboard_feedback_space</item>
+        <item name="iconPreviewTabKey">@drawable/sym_keyboard_feedback_tab</item>
+        <item name="iconPreviewSettingsKey">@drawable/sym_keyboard_feedback_settings</item>
+        <item name="iconPreviewShortcutKey">@drawable/sym_keyboard_feedback_mic</item>
+        <!-- LatinKeyboard icons -->
+        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item>
+        <item name="disabledShortcutIcon">@drawable/sym_keyboard_voice_off_holo</item>
+        <item name="spacebarArrowLeftIcon">@drawable/sym_keyboard_language_arrows_left</item>
+        <item name="spacebarArrowRightIcon">@drawable/sym_keyboard_language_arrows_right</item>
+        <item name="spacebarArrowPreviewLeftIcon">@drawable/sym_keyboard_feedback_language_arrows_left</item>
+        <item name="spacebarArrowPreviewRightIcon">@drawable/sym_keyboard_feedback_language_arrows_right</item>
+    </style>
+</resources>
diff --git a/java/res/values/keycodes.xml b/java/res/values/keycodes.xml
index d6f9bfc..ee34529 100644
--- a/java/res/values/keycodes.xml
+++ b/java/res/values/keycodes.xml
@@ -26,26 +26,6 @@
     <integer name="key_shift">-1</integer>
     <integer name="key_switch_alpha_symbol">-2</integer>
     <integer name="key_delete">-5</integer>
-    <integer name="key_settings">-100</integer>
-    <integer name="key_voice">-102</integer>
-
-    <!-- Array used for mapping key codes to description strings. -->
-    <array name="key_descriptions">
-        <item>@integer/key_tab</item>
-        <item>@string/description_tab_key</item>
-        <item>@integer/key_return</item>
-        <item>@string/description_return_key</item>
-        <item>@integer/key_space</item>
-        <item>@string/description_space_key</item>
-        <item>@integer/key_shift</item>
-        <item>@string/description_shift_key</item>
-        <item>@integer/key_switch_alpha_symbol</item>
-        <item>@string/description_switch_alpha_symbol_key</item>
-        <item>@integer/key_delete</item>
-        <item>@string/description_delete_key</item>
-        <item>@integer/key_settings</item>
-        <item>@string/description_settings_key</item>
-        <item>@integer/key_voice</item>
-        <item>@string/description_voice_key</item>
-    </array>
+    <integer name="key_settings">-6</integer>
+    <integer name="key_shortcut">-8</integer>
 </resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index ac14a20..5d7a1f9 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -38,7 +38,30 @@
     <string name="general_category">General</string>
 
     <!-- Category title for text prediction -->
-    <string name="prediction_category">Text correction</string>
+    <string name="correction_category">Text correction</string>
+
+    <!-- Category title for ngrams  -->
+    <string name="ngram_category">Suggestions based on previous words</string>
+
+    <!-- Category title for misc options  -->
+    <string name="misc_category">Other options</string>
+
+    <!-- Option name for advanced settings screen [CHAR LIMIT=25] -->
+    <string name="advanced_settings">Advanced settings</string>
+    <!-- Option summary for advanced settings screen [CHAR LIMIT=65 (two lines) or 30 (fits on one line, preferable)] -->
+    <string name="advanced_settings_summary">Options for expert users</string>
+
+    <!-- Option for the dismiss delay of the key popup [CHAR LIMIT=25] -->
+    <string name="key_preview_popup_dismiss_delay">Key popup dismiss delay</string>
+    <!-- Description for delay for dismissing a popup on keypress: no delay [CHAR LIMIT=15] -->
+    <string name="key_preview_popup_dismiss_no_delay">No delay</string>
+    <!-- Description for delay for dismissing a popup on screen: default value of the delay [CHAR LIMIT=15] -->
+    <string name="key_preview_popup_dismiss_default_delay">Default</string>
+
+    <!-- Option name for enabling or disabling the use of names of people in Contacts for suggestion and correction [CHAR LIMIT=25] -->
+    <string name="use_contacts_dict">Suggest Contact names</string>
+    <!-- Description for option enabling or disabling the use of names of people in Contacts for suggestion and correction [CHAR LIMIT=65] -->
+    <string name="use_contacts_dict_summary">Use names from Contacts for suggestions and corrections</string>
 
     <!-- Option to enable auto capitalization of sentences -->
     <string name="auto_cap">Auto-capitalization</string>
@@ -55,6 +78,8 @@
     <string name="prefs_suggestion_visibility_show_name">Always show</string>
     <string name="prefs_suggestion_visibility_show_only_portrait_name">Show on portrait mode</string>
     <string name="prefs_suggestion_visibility_hide_name">Always hide</string>
+    <!-- Option to enable spacebar language switcher [CHAR LIMIT=20]-->
+    <string name="prefs_use_spacebar_language_switch">Use the spacebar language switcher</string>
 
     <!-- Option to show/hide the settings key -->
     <string name="prefs_settings_key">Show settings key</string>
@@ -78,9 +103,13 @@
     <string name="auto_correction_threshold_mode_aggeressive">Aggressive</string>
 
     <!-- Option to enable bigram correction -->
-    <string name="bigram_suggestion">Bigram Suggestions</string>
+    <string name="bigram_suggestion">Bigram suggestions</string>
     <!-- Description for auto correction -->
     <string name="bigram_suggestion_summary">Use previous word to improve suggestion</string>
+    <!-- Option to enable using user-history bigram when no input -->
+    <string name="bigram_prediction">Bigram prediction</string>
+    <!-- Description for auto correction -->
+    <string name="bigram_prediction_summary">Use previous word also for prediction</string>
 
     <!-- Indicates that a word has been added to the dictionary -->
     <string name="added_word"><xliff:g id="word">%s</xliff:g> : Saved</string>
@@ -102,30 +131,80 @@
     <!-- Label for "Wait" key of phone number keyboard.  Must be short to fit on key! [CHAR LIMIT=5]-->
     <string name="label_wait_key">Wait</string>
 
-    <!-- Spoken text description for delete key. -->
-    <string name="description_delete_key">Delete</string>
-    <!-- Spoken text description for return key. -->
-    <string name="description_return_key">Return</string>
-    <!-- Spoken text description for settings key. -->
-    <string name="description_settings_key">Settings</string>
-    <!-- Spoken text description for shift key. -->
-    <string name="description_shift_key">Shift</string>
-    <!-- Spoken text description for space key. -->
-    <string name="description_space_key">Space</string>
-    <!-- Spoken text description for symbols key. -->
-    <string name="description_switch_alpha_symbol_key">Symbols</string>
-    <!-- Spoken text description for tab key. -->
-    <string name="description_tab_key">Tab</string>
-    <!-- Spoken text description for voice input key. -->
-    <string name="description_voice_key">Voice Input</string>
-    <!-- Spoken text description for symbols mode on. -->
-    <string name="description_symbols_on">Symbols on</string>
-    <!-- Spoken text description for symbols mode off. -->
-    <string name="description_symbols_off">Symbols off</string>
-    <!-- Spoken text description for shift mode on. -->
-    <string name="description_shift_on">Shift on</string>
-    <!-- Spoken text description for shift mode off. -->
-    <string name="description_shift_off">Shift off</string>
+    <!-- Spoken description for the currently entered text -->
+    <string name="spoken_current_text_is">Current text is "%s"</string>
+    <!-- Spoken description when there is no text entered -->
+    <string name="spoken_no_text_entered">No text entered</string>
+
+    <!-- Spoken description for unknown keyboard keys. -->
+    <string name="spoken_description_unknown">Key code %d</string>
+    <!-- Spoken description for the "Shift" keyboard key. -->
+    <string name="spoken_description_shift">Shift</string>
+    <!-- Spoken description for the "Shift" keyboard key's pressed state. -->
+    <string name="spoken_description_shift_shifted">Shift enabled</string>
+    <!-- Spoken description for the "Shift" keyboard key's pressed state. -->
+    <string name="spoken_description_caps_lock">Caps lock enabled</string>
+    <!-- Spoken description for the "Delete" keyboard key. -->
+    <string name="spoken_description_delete">Delete</string>
+    <!-- Spoken description for the "To Symbol" keyboard key. -->
+    <string name="spoken_description_to_symbol">Symbols</string>
+    <!-- Spoken description for the "To Alpha" keyboard key. -->
+    <string name="spoken_description_to_alpha">Letters</string>
+    <!-- Spoken description for the "To Numbers" keyboard key. -->
+    <string name="spoken_description_to_numeric">Numbers</string>
+    <!-- Spoken description for the "Settings" keyboard key. -->
+    <string name="spoken_description_settings">Settings</string>
+    <!-- Spoken description for the "Tab" keyboard key. -->
+    <string name="spoken_description_tab">Tab</string>
+    <!-- Spoken description for the "Space" keyboard key. -->
+    <string name="spoken_description_space">Space</string>
+    <!-- Spoken description for the "Mic" keyboard key. -->
+    <string name="spoken_description_mic">Voice input</string>
+    <!-- Spoken description for the "Smiley" keyboard key. -->
+    <string name="spoken_description_smiley">Smiley face</string>
+    <!-- Spoken description for the "Return" keyboard key. -->
+    <string name="spoken_description_return">Return</string>
+
+    <!-- Spoken description for the "," keyboard key. -->
+    <string name="spoken_description_comma">Comma</string>
+    <!-- Spoken description for the "." keyboard key. -->
+    <string name="spoken_description_period">Period</string>
+    <!-- Spoken description for the "(" keyboard key. -->
+    <string name="spoken_description_left_parenthesis">Left parenthesis</string>
+    <!-- Spoken description for the ")" keyboard key. -->
+    <string name="spoken_description_right_parenthesis">Right parenthesis</string>
+    <!-- Spoken description for the ":" keyboard key. -->
+    <string name="spoken_description_colon">Colon</string>
+    <!-- Spoken description for the ";" keyboard key. -->
+    <string name="spoken_description_semicolon">Semicolon</string>
+    <!-- Spoken description for the "!" keyboard key. -->
+    <string name="spoken_description_exclamation_mark">Exclamation mark</string>
+    <!-- Spoken description for the "?" keyboard key. -->
+    <string name="spoken_description_question_mark">Question mark</string>
+    <!-- Spoken description for the """ keyboard key. -->
+    <string name="spoken_description_double_quote">Double quote</string>
+    <!-- Spoken description for the "'" keyboard key. -->
+    <string name="spoken_description_single_quote">Single quote</string>
+    <!-- Spoken description for the "•" keyboard key. -->
+    <string name="spoken_description_dot">Dot</string>
+    <!-- Spoken description for the "√" keyboard key. -->
+    <string name="spoken_description_square_root">Square root</string>
+    <!-- Spoken description for the "π" keyboard key. -->
+    <string name="spoken_description_pi">Pi</string>
+    <!-- Spoken description for the "Δ" keyboard key. -->
+    <string name="spoken_description_delta">Delta</string>
+    <!-- Spoken description for the "™" keyboard key. -->
+    <string name="spoken_description_trademark">Trademark</string>
+    <!-- Spoken description for the "℅" keyboard key. -->
+    <string name="spoken_description_care_of">Care of</string>
+    <!-- Spoken description for the "*" keyboard key. -->
+    <string name="spoken_description_star">Star</string>
+    <!-- Spoken description for the "#" keyboard key. -->
+    <string name="spoken_description_pound">Pound</string>
+    <!-- Spoken description for the "…" keyboard key. -->
+    <string name="spoken_description_ellipsis">Ellipsis</string>
+    <!-- Spoken description for the "„" keyboard key. -->
+    <string name="spoken_description_low_double_quote">Low double quote</string>
 
     <!-- Voice related labels -->
 
@@ -242,34 +321,53 @@
 
     <!-- Description for Czech keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_cs_keyboard">Czech Keyboard</string>
+    <!-- Description for Arabic keyboard subtype [CHAR LIMIT=35] -->
+    <string name="subtype_mode_ar_keyboard">Arabic Keyboard</string>
     <!-- Description for Danish keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_da_keyboard">Danish Keyboard</string>
     <!-- Description for German keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_de_keyboard">German Keyboard</string>
+    <!-- Description for German QWERTY keyboard subtype [CHAR LIMIT=35] -->
+    <string name="subtype_mode_de_qwerty_keyboard">German QWERTY Keyboard</string>
     <!-- Description for English (United Kingdom) keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_en_GB_keyboard">English (UK) Keyboard</string>
     <!-- Description for English (United States) keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_en_US_keyboard">English (US) Keyboard</string>
     <!-- Description for Spanish keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_es_keyboard">Spanish Keyboard</string>
+    <!-- Description for Finnish keyboard subtype [CHAR LIMIT=35] -->
+    <string name="subtype_mode_fi_keyboard">Finnish Keyboard</string>
     <!-- Description for French keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_fr_keyboard">French Keyboard</string>
     <!-- Description for French (Canada) keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_fr_CA_keyboard">French (Canada) Keyboard</string>
     <!-- Description for French (Switzerland) keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_fr_CH_keyboard">French (Switzerland) Keyboard</string>
+    <!-- Description for Croatian keyboard subtype [CHAR LIMIT=35] -->
+    <string name="subtype_mode_hr_keyboard">Croatian Keyboard</string>
+    <!-- Description for Hungarian keyboard subtype [CHAR LIMIT=35] -->
+    <string name="subtype_mode_hu_keyboard">Hungarian Keyboard</string>
+    <!-- Description for Hebrew keyboard subtype [CHAR LIMIT=35] -->
+    <!-- Java uses the deprecated "iw" code instead of the standard "he" code -->
+    <string name="subtype_mode_iw_keyboard">Hebrew Keyboard</string>
     <!-- Description for Italian keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_it_keyboard">Italian Keyboard</string>
     <!-- Description for Norwegian keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_nb_keyboard">Norwegian Keyboard</string>
     <!-- Description for Dutch keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_nl_keyboard">Dutch Keyboard</string>
+    <!-- Description for Polish keyboard subtype [CHAR LIMIT=35] -->
+    <string name="subtype_mode_pl_keyboard">Polish Keyboard</string>
+    <!-- Description for Portuguese keyboard subtype [CHAR LIMIT=35] -->
+    <string name="subtype_mode_pt_keyboard">Portuguese Keyboard</string>
     <!-- Description for Russian keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_ru_keyboard">Russian Keyboard</string>
     <!-- Description for Serbian keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_sr_keyboard">Serbian Keyboard</string>
     <!-- Description for Swedish keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_sv_keyboard">Swedish Keyboard</string>
+    <!-- Description for Turkish keyboard subtype [CHAR LIMIT=35] -->
+    <string name="subtype_mode_tr_keyboard">Turkish Keyboard</string>
     <!-- Description for Afrikaans voice input subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_af_voice">Afrikaans Voice</string>
     <!-- Description for Czech voice input subtype [CHAR LIMIT=35] -->
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 130714f..05defcb 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -15,31 +15,171 @@
 -->
 
 <resources>
+    <!-- Theme "Basic" -->
+    <style name="Keyboard">
+        <item name="rowHeight">25%p</item>
+        <item name="keyboardHeight">@dimen/keyboardHeight</item>
+        <item name="maxKeyboardHeight">@fraction/maxKeyboardHeight</item>
+        <item name="minKeyboardHeight">@fraction/minKeyboardHeight</item>
+        <item name="popupKeyboardTemplate">@xml/kbd_popup_template</item>
+        <item name="keyboardTopPadding">@dimen/keyboard_top_padding</item>
+        <item name="keyboardBottomPadding">@dimen/keyboard_bottom_padding</item>
+        <item name="horizontalGap">@dimen/key_horizontal_gap</item>
+        <item name="verticalGap">@dimen/key_bottom_gap</item>
+        <item name="maxPopupKeyboardColumn">@integer/config_max_popup_keyboard_column</item>
+    </style>
+    <style name="LatinKeyboard">
+        <item name="autoCorrectionSpacebarLedEnabled">@bool/config_auto_correction_spacebar_led_enabled</item>
+        <item name="spacebarTextColor">#FFC0C0C0</item>
+        <item name="spacebarTextShadowColor">#80000000</item>
+    </style>
     <style name="KeyboardView">
         <item name="android:background">@drawable/keyboard_background</item>
-
         <item name="keyBackground">@drawable/btn_keyboard_key</item>
-        <item name="keyLetterSize">@dimen/key_letter_size</item>
-        <item name="keyLetterStyle">normal</item>
+        <item name="keyLetterRatio">@fraction/key_letter_ratio</item>
+        <item name="keyLabelRatio">@fraction/key_label_ratio</item>
+        <item name="keyHintLetterRatio">@fraction/key_hint_letter_ratio</item>
+        <item name="keyUppercaseLetterRatio">@fraction/key_uppercase_letter_ratio</item>
+        <item name="keyTextStyle">normal</item>
         <item name="keyTextColor">#FFFFFFFF</item>
-        <item name="keyTextColorDisabled">#FFFFFFFF</item>
+        <item name="keyTextInactivatedColor">#FFFFFFFF</item>
+        <item name="keyHintLetterColor">#80000000</item>
+        <item name="keyUppercaseLetterInactivatedColor">#66E0E4E5</item>
+        <item name="keyUppercaseLetterActivatedColor">#CCE0E4E5</item>
+        <item name="keyPopupHintIcon">@drawable/hint_popup</item>
         <item name="keyPreviewLayout">@layout/key_preview</item>
         <item name="keyPreviewOffset">@dimen/key_preview_offset</item>
         <item name="keyPreviewHeight">@dimen/key_preview_height</item>
-        <item name="labelTextSize">@dimen/key_label_text_size</item>
         <item name="popupLayout">@layout/keyboard_popup</item>
         <item name="keyHysteresisDistance">@dimen/key_hysteresis_distance</item>
         <item name="verticalCorrection">@dimen/keyboard_vertical_correction</item>
         <item name="shadowColor">#BB000000</item>
         <item name="shadowRadius">2.75</item>
         <item name="backgroundDimAmount">0.5</item>
-        <item name="colorScheme">white</item>
     </style>
-    <style name="KeyPreviewAnimation">
-        <item name="android:windowEnterAnimation">@anim/key_preview_fadein</item>
-        <item name="android:windowExitAnimation">@anim/key_preview_fadeout</item>
+    <style name="KeyPreviewStyle">
+        <item name="android:background">@drawable/keyboard_key_feedback</item>
+        <item name="android:textColor">#FFFFFFFF</item>
     </style>
-    <style name="MiniKeyboardAnimation">
+    <style name="PopupMiniKeyboardView" parent="KeyboardView">
+        <item name="keyBackground">@drawable/btn_keyboard_key_popup</item>
+        <item name="keyHysteresisDistance">0dip</item>
+        <item name="verticalCorrection">@dimen/mini_keyboard_vertical_correction</item>
+    </style>
+    <style name="PopupMiniKeyboardPanelStyle">
+        <item name="android:background">@drawable/keyboard_popup_panel_background</item>
+        <item name="android:paddingLeft">@dimen/mini_keyboard_horizontal_padding</item>
+        <item name="android:paddingRight">@dimen/mini_keyboard_horizontal_padding</item>
+    </style>
+    <style name="SuggestionsStripBackgroundStyle">
+        <item name="android:background">@drawable/keyboard_suggest_strip</item>
+    </style>
+    <style name="SuggestionBackgroundStyle">
+        <item name="android:background">@drawable/btn_candidate</item>
+    </style>
+    <style name="SuggestionPreviewBackgroundStyle">
+        <item name="android:background">@drawable/candidate_feedback_background</item>
+    </style>
+    <style name="CandidateViewStyle" parent="SuggestionsStripBackgroundStyle">
+        <item name="autoCorrectHighlight">autoCorrectBold</item>
+        <item name="colorTypedWord">#FFFFFFFF</item>
+        <item name="colorAutoCorrect">#FFFCAE00</item>
+        <item name="colorSuggested">#FFFCAE00</item>
+        <item name="iconExpandPane">@drawable/btn_expand_candidates_pane</item>
+        <item name="iconClosePane">@drawable/btn_close_candidates_pane</item>"
+    </style>
+    <!-- Theme "Basic high contrast" -->
+    <style name="KeyboardView.HighContrast" parent="KeyboardView">
+        <item name="android:background">@android:color/black</item>
+        <item name="keyBackground">@drawable/btn_keyboard_key3</item>
+    </style>
+    <!-- Theme "Stone" -->
+    <style name="Keyboard.Stone" parent="Keyboard">
+        <item name="horizontalGap">@dimen/key_horizontal_gap_stone</item>
+        <item name="verticalGap">@dimen/key_bottom_gap_stone</item>
+    </style>
+    <style name="LatinKeyboard.Stone" parent="LatinKeyboard">
+        <item name="spacebarTextColor">#FF444444</item>
+        <item name="spacebarTextShadowColor">#80FFFFFF</item>
+    </style>
+    <style name="KeyboardView.Stone" parent="KeyboardView">
+        <item name="keyBackground">@drawable/btn_keyboard_key_stone</item>
+        <item name="keyTextColor">#FF000000</item>
+        <item name="keyTextInactivatedColor">#FF808080</item>
+        <item name="shadowColor">#FFFFFFFF</item>
+    </style>
+    <style name="PopupMiniKeyboardView.Stone" parent="PopupMiniKeyboardView">
+        <item name="keyBackground">@drawable/btn_keyboard_key_stone</item>
+        <item name="keyTextColor">#FF000000</item>
+        <item name="shadowColor">#FFFFFFFF</item>
+    </style>
+    <!-- Theme "Stone bold" -->
+    <style name="KeyboardView.Stone.Bold" parent="KeyboardView.Stone">
+        <item name="keyTextStyle">bold</item>
+    </style>
+    <!-- Theme "Gingerbread" -->
+    <style name="KeyboardView.Gingerbread" parent="KeyboardView">
+        <item name="android:background">@drawable/keyboard_dark_background</item>
+        <item name="keyBackground">@drawable/btn_keyboard_key_gingerbread</item>
+        <item name="keyTextStyle">bold</item>
+    </style>
+    <style name="PopupMiniKeyboardView.Gingerbread" parent="PopupMiniKeyboardView">
+        <item name="android:background">@null</item>
+    </style>
+    <!-- Theme "IceCreamSandwich" -->
+    <style name="Keyboard.IceCreamSandwich" parent="Keyboard" >
+        <item name="keyboardTopPadding">@dimen/keyboard_top_padding_ics</item>
+        <item name="keyboardBottomPadding">@dimen/keyboard_bottom_padding_ics</item>
+        <item name="horizontalGap">@dimen/key_horizontal_gap_ics</item>
+        <item name="verticalGap">@dimen/key_bottom_gap_ics</item>
+    </style>
+    <style name="LatinKeyboard.IceCreamSandwich" parent="LatinKeyboard">
+        <item name="disabledShortcutIcon">@drawable/sym_keyboard_voice_off_holo</item>
+    </style>
+    <style name="KeyboardView.IceCreamSandwich" parent="KeyboardView">
+        <item name="android:background">@drawable/keyboard_background_holo</item>
+        <item name="keyBackground">@drawable/btn_keyboard_key_ics</item>
+        <item name="keyTextStyle">bold</item>
+        <item name="keyTextInactivatedColor">#66E0E4E5</item>
+        <item name="keyHintLetterColor">#80000000</item>
+        <item name="keyUppercaseLetterInactivatedColor">#66E0E4E5</item>
+        <item name="keyUppercaseLetterActivatedColor">#FFFFFFFF</item>
+        <item name="keyPopupHintIcon">@drawable/hint_popup</item>
+        <item name="keyPreviewHeight">@dimen/key_preview_height</item>
+        <item name="keyPreviewOffset">@dimen/key_preview_offset_ics</item>
+        <item name="shadowColor">#00000000</item>
+        <item name="shadowRadius">0.0</item>
+    </style>
+    <style name="KeyPreviewStyle.IceCreamSandwich">
+        <item name="android:background">@drawable/keyboard_key_feedback_ics</item>
+    </style>
+    <style name="PopupMiniKeyboardView.IceCreamSandwich" parent="PopupMiniKeyboardView">
+        <item name="android:background">@null</item>
+        <item name="keyBackground">@drawable/btn_keyboard_key_popup_ics</item>
+    </style>
+    <style name="PopupMiniKeyboardPanelStyle.IceCreamSandwich">
+        <item name="android:background">@drawable/keyboard_popup_panel_background_holo</item>
+        <item name="android:paddingLeft">@dimen/mini_keyboard_horizontal_padding_holo</item>
+        <item name="android:paddingRight">@dimen/mini_keyboard_horizontal_padding_holo</item>
+    </style>
+    <style name="SuggestionsStripBackgroundStyle.IceCreamSandwich">
+        <item name="android:background">@drawable/keyboard_suggest_strip_holo</item>
+    </style>
+    <style name="SuggestionBackgroundStyle.IceCreamSandwich">
+        <item name="android:background">@drawable/btn_candidate_ics</item>
+    </style>
+    <style name="SuggestionPreviewBackgroundStyle.IceCreamSandwich">
+        <item name="android:background">@drawable/keyboard_popup_panel_background_holo</item>
+    </style>
+    <style name="CandidateViewStyle.IceCreamSandwich" parent="SuggestionsStripBackgroundStyle.IceCreamSandwich">
+        <item name="autoCorrectHighlight">autoCorrectUnderline|autoCorrectInvert</item>
+        <item name="colorTypedWord">#FFFFFFFF</item>
+        <item name="colorAutoCorrect">#FFFFFFFF</item>
+        <item name="colorSuggested">#FFFFFFFF</item>
+        <item name="iconExpandPane">@drawable/btn_expand_candidates_pane</item>
+        <item name="iconClosePane">@drawable/btn_close_candidates_pane</item>"
+    </style>
+    <style name="PopupMiniKeyboardAnimation">
         <item name="android:windowEnterAnimation">@anim/mini_keyboard_fadein</item>
         <item name="android:windowExitAnimation">@anim/mini_keyboard_fadeout</item>
     </style>
diff --git a/java/res/values/themes-basic-highcontrast.xml b/java/res/values/themes-basic-highcontrast.xml
new file mode 100644
index 0000000..8122e4e
--- /dev/null
+++ b/java/res/values/themes-basic-highcontrast.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <style name="KeyboardTheme.HighContrast" parent="KeyboardIcons">
+        <item name="keyboardStyle">@style/Keyboard</item>
+        <item name="latinKeyboardStyle">@style/LatinKeyboard</item>
+        <item name="keyboardViewStyle">@style/KeyboardView.HighContrast</item>
+        <item name="keyPreviewStyle">@style/KeyPreviewStyle</item>
+        <item name="popupMiniKeyboardViewStyle">@style/PopupMiniKeyboardView</item>
+        <item name="popupMiniKeyboardPanelStyle">@style/PopupMiniKeyboardPanelStyle</item>
+        <item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
+        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
+        <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
+        <item name="candidateViewStyle">@style/CandidateViewStyle</item>
+    </style>
+</resources>
diff --git a/java/res/values/themes-basic.xml b/java/res/values/themes-basic.xml
new file mode 100644
index 0000000..50b52de
--- /dev/null
+++ b/java/res/values/themes-basic.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <style name="KeyboardTheme" parent="KeyboardIcons">
+        <item name="keyboardStyle">@style/Keyboard</item>
+        <item name="latinKeyboardStyle">@style/LatinKeyboard</item>
+        <item name="keyboardViewStyle">@style/KeyboardView</item>
+        <item name="keyPreviewStyle">@style/KeyPreviewStyle</item>
+        <item name="popupMiniKeyboardViewStyle">@style/PopupMiniKeyboardView</item>
+        <item name="popupMiniKeyboardPanelStyle">@style/PopupMiniKeyboardPanelStyle</item>
+        <item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
+        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
+        <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
+        <item name="candidateViewStyle">@style/CandidateViewStyle</item>
+    </style>
+</resources>
diff --git a/java/res/values/themes-gingerbread.xml b/java/res/values/themes-gingerbread.xml
new file mode 100644
index 0000000..cdb10c8
--- /dev/null
+++ b/java/res/values/themes-gingerbread.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <style name="KeyboardTheme.Gingerbread" parent="KeyboardIcons">
+        <item name="keyboardStyle">@style/Keyboard</item>
+        <item name="latinKeyboardStyle">@style/LatinKeyboard</item>
+        <item name="keyboardViewStyle">@style/KeyboardView.Gingerbread</item>
+        <item name="keyPreviewStyle">@style/KeyPreviewStyle</item>
+        <item name="popupMiniKeyboardViewStyle">@style/PopupMiniKeyboardView.Gingerbread</item>
+        <item name="popupMiniKeyboardPanelStyle">@style/PopupMiniKeyboardPanelStyle</item>
+        <item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
+        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
+        <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
+        <item name="candidateViewStyle">@style/CandidateViewStyle</item>
+    </style>
+</resources>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
new file mode 100644
index 0000000..33c4081
--- /dev/null
+++ b/java/res/values/themes-ics.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <style name="KeyboardTheme.IceCreamSandwich" parent="KeyboardIcons.IceCreamSandwich">
+        <item name="keyboardStyle">@style/Keyboard.IceCreamSandwich</item>
+        <item name="latinKeyboardStyle">@style/LatinKeyboard.IceCreamSandwich</item>
+        <item name="keyboardViewStyle">@style/KeyboardView.IceCreamSandwich</item>
+        <item name="keyPreviewStyle">@style/KeyPreviewStyle.IceCreamSandwich</item>
+        <item name="popupMiniKeyboardViewStyle">@style/PopupMiniKeyboardView.IceCreamSandwich</item>
+        <item name="popupMiniKeyboardPanelStyle">@style/PopupMiniKeyboardPanelStyle.IceCreamSandwich</item>
+        <item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle.IceCreamSandwich</item>
+        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle.IceCreamSandwich</item>
+        <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle.IceCreamSandwich</item>
+        <item name="candidateViewStyle">@style/CandidateViewStyle.IceCreamSandwich</item>
+    </style>
+</resources>
diff --git a/java/res/values/themes-stone-bold.xml b/java/res/values/themes-stone-bold.xml
new file mode 100644
index 0000000..6589807
--- /dev/null
+++ b/java/res/values/themes-stone-bold.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <style name="KeyboardTheme.Stone.Bold" parent="KeyboardIcons.Black">
+        <item name="keyboardStyle">@style/Keyboard.Stone</item>
+        <item name="latinKeyboardStyle">@style/LatinKeyboard.Stone</item>
+        <item name="keyboardViewStyle">@style/KeyboardView.Stone.Bold</item>
+        <item name="keyPreviewStyle">@style/KeyPreviewStyle</item>
+        <item name="popupMiniKeyboardViewStyle">@style/PopupMiniKeyboardView.Stone</item>
+        <item name="popupMiniKeyboardPanelStyle">@style/PopupMiniKeyboardPanelStyle</item>
+        <item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
+        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
+        <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
+        <item name="candidateViewStyle">@style/CandidateViewStyle</item>
+    </style>
+</resources>
diff --git a/java/res/values/themes-stone.xml b/java/res/values/themes-stone.xml
new file mode 100644
index 0000000..a8608f6
--- /dev/null
+++ b/java/res/values/themes-stone.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <style name="KeyboardTheme.Stone" parent="KeyboardIcons.Black">
+        <item name="keyboardStyle">@style/Keyboard.Stone</item>
+        <item name="latinKeyboardStyle">@style/LatinKeyboard.Stone</item>
+        <item name="keyboardViewStyle">@style/KeyboardView.Stone</item>
+        <item name="keyPreviewStyle">@style/KeyPreviewStyle</item>
+        <item name="popupMiniKeyboardViewStyle">@style/PopupMiniKeyboardView.Stone</item>
+        <item name="popupMiniKeyboardPanelStyle">@style/PopupMiniKeyboardPanelStyle</item>
+        <item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
+        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
+        <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
+        <item name="candidateViewStyle">@style/CandidateViewStyle</item>
+    </style>
+</resources>
diff --git a/java/res/values/whitelist.xml b/java/res/values/whitelist.xml
index ced52e7..d4ecbfa 100644
--- a/java/res/values/whitelist.xml
+++ b/java/res/values/whitelist.xml
@@ -25,14 +25,5 @@
         3. (String)after
      -->
     <string-array name="wordlist_whitelist">
-
-        <item>255</item>
-        <item>ill</item>
-        <item>I\'ll</item>
-
-        <item>255</item>
-        <item>thisd</item>
-        <item>this\'d</item>
-
     </string-array>
 </resources>
diff --git a/java/res/drawable/btn_candidate_holo.xml b/java/res/xml-ar/kbd_qwerty.xml
similarity index 69%
copy from java/res/drawable/btn_candidate_holo.xml
copy to java/res/xml-ar/kbd_qwerty.xml
index 66cd246..57a6d2c 100644
--- a/java/res/drawable/btn_candidate_holo.xml
+++ b/java/res/xml-ar/kbd_qwerty.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** 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.
@@ -18,10 +18,10 @@
 */
 -->
 
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyboardLocale="ar"
 >
-    <item
-        android:state_pressed="true"
-        android:drawable="@drawable/btn_keyboard_key_popup_selected_holo" />
-</selector>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_arabic" />
+</Keyboard>
diff --git a/java/res/xml-cs/kbd_qwerty.xml b/java/res/xml-cs/kbd_qwerty.xml
index 010bdb3..9991ea2 100644
--- a/java/res/xml-cs/kbd_qwerty.xml
+++ b/java/res/xml-cs/kbd_qwerty.xml
@@ -20,15 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:keyWidth="10%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="cs"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_qwertz_rows" />
+        latin:keyboardLayout="@xml/kbd_rows_qwertz" />
 </Keyboard>
diff --git a/java/res/xml-da/kbd_qwerty.xml b/java/res/xml-da/kbd_qwerty.xml
index 441b7cb..37a50fd 100644
--- a/java/res/xml-da/kbd_qwerty.xml
+++ b/java/res/xml-da/kbd_qwerty.xml
@@ -20,14 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="da"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_qwerty_rows_scandinavia" />
+        latin:keyboardLayout="@xml/kbd_rows_scandinavian" />
 </Keyboard>
diff --git a/java/res/drawable/btn_candidate_holo.xml b/java/res/xml-de-rZZ/kbd_qwerty.xml
similarity index 69%
copy from java/res/drawable/btn_candidate_holo.xml
copy to java/res/xml-de-rZZ/kbd_qwerty.xml
index 66cd246..d5fd8ef 100644
--- a/java/res/drawable/btn_candidate_holo.xml
+++ b/java/res/xml-de-rZZ/kbd_qwerty.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** 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.
@@ -18,10 +18,10 @@
 */
 -->
 
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyboardLocale="de"
 >
-    <item
-        android:state_pressed="true"
-        android:drawable="@drawable/btn_keyboard_key_popup_selected_holo" />
-</selector>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_qwerty" />
+</Keyboard>
diff --git a/java/res/xml-de/kbd_qwerty.xml b/java/res/xml-de/kbd_qwerty.xml
index a23e4fb..89e10b2 100644
--- a/java/res/xml-de/kbd_qwerty.xml
+++ b/java/res/xml-de/kbd_qwerty.xml
@@ -20,15 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:keyWidth="10%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="de"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_qwertz_rows" />
+        latin:keyboardLayout="@xml/kbd_rows_qwertz" />
 </Keyboard>
diff --git a/java/res/drawable/btn_candidate_holo.xml b/java/res/xml-es/kbd_qwerty.xml
similarity index 69%
copy from java/res/drawable/btn_candidate_holo.xml
copy to java/res/xml-es/kbd_qwerty.xml
index 66cd246..568f4d6 100644
--- a/java/res/drawable/btn_candidate_holo.xml
+++ b/java/res/xml-es/kbd_qwerty.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** 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.
@@ -18,10 +18,10 @@
 */
 -->
 
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyboardLocale="es,es_US"
 >
-    <item
-        android:state_pressed="true"
-        android:drawable="@drawable/btn_keyboard_key_popup_selected_holo" />
-</selector>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_spanish" />
+</Keyboard>
diff --git a/java/res/xml-fi/kbd_qwerty.xml b/java/res/xml-fi/kbd_qwerty.xml
index b0a7b3e..75721e0 100644
--- a/java/res/xml-fi/kbd_qwerty.xml
+++ b/java/res/xml-fi/kbd_qwerty.xml
@@ -20,14 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="fi"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_qwerty_rows_scandinavia" />
+        latin:keyboardLayout="@xml/kbd_rows_scandinavian" />
 </Keyboard>
diff --git a/java/res/xml-fr-rCA/kbd_qwerty.xml b/java/res/xml-fr-rCA/kbd_qwerty.xml
index 92d92f0..7bdfbad 100644
--- a/java/res/xml-fr-rCA/kbd_qwerty.xml
+++ b/java/res/xml-fr-rCA/kbd_qwerty.xml
@@ -20,15 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:keyWidth="10%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="fr_CA"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_qwerty_rows" />
+        latin:keyboardLayout="@xml/kbd_rows_qwerty" />
 </Keyboard>
diff --git a/java/res/xml-fr-rCH/kbd_qwerty.xml b/java/res/xml-fr-rCH/kbd_qwerty.xml
index a23e4fb..41b701d 100644
--- a/java/res/xml-fr-rCH/kbd_qwerty.xml
+++ b/java/res/xml-fr-rCH/kbd_qwerty.xml
@@ -20,15 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:keyWidth="10%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="fr_CH"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_qwertz_rows" />
+        latin:keyboardLayout="@xml/kbd_rows_qwertz" />
 </Keyboard>
diff --git a/java/res/xml-fr/kbd_qwerty.xml b/java/res/xml-fr/kbd_qwerty.xml
index 2d0b42b..8c730a2 100644
--- a/java/res/xml-fr/kbd_qwerty.xml
+++ b/java/res/xml-fr/kbd_qwerty.xml
@@ -20,15 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:keyWidth="10%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="fr"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_azerty_rows" />
+        latin:keyboardLayout="@xml/kbd_rows_azerty" />
 </Keyboard>
diff --git a/java/res/drawable/btn_candidate_holo.xml b/java/res/xml-hr/kbd_qwerty.xml
similarity index 65%
copy from java/res/drawable/btn_candidate_holo.xml
copy to java/res/xml-hr/kbd_qwerty.xml
index 66cd246..ca92e86 100644
--- a/java/res/drawable/btn_candidate_holo.xml
+++ b/java/res/xml-hr/kbd_qwerty.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** 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.
@@ -18,10 +18,11 @@
 */
 -->
 
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyboardLocale="hr"
 >
-    <item
-        android:state_pressed="true"
-        android:drawable="@drawable/btn_keyboard_key_popup_selected_holo" />
-</selector>
+    <!-- TODO: Dedicated Croatian layout especially for tablet. -->
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_qwertz" />
+</Keyboard>
diff --git a/java/res/xml-hu/kbd_qwerty.xml b/java/res/xml-hu/kbd_qwerty.xml
index 010bdb3..3195d5b 100644
--- a/java/res/xml-hu/kbd_qwerty.xml
+++ b/java/res/xml-hu/kbd_qwerty.xml
@@ -20,15 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:keyWidth="10%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="hu"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_qwertz_rows" />
+        latin:keyboardLayout="@xml/kbd_rows_qwertz" />
 </Keyboard>
diff --git a/java/res/xml-iw/kbd_qwerty.xml b/java/res/xml-iw/kbd_qwerty.xml
index 6c2504e..dc2c5d3 100644
--- a/java/res/xml-iw/kbd_qwerty.xml
+++ b/java/res/xml-iw/kbd_qwerty.xml
@@ -1,112 +1,27 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* 
+/*
 **
 ** Copyright 2010, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** 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.
 */
 -->
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:keyWidth="10%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="iw"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_key_styles" />
-    <Row
-        latin:rowEdgeFlags="top"
-    >
-        <Spacer
-            latin:keyWidth="5%p" />
-        <Key
-            latin:keyLabel="ק"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="ר" />
-        <Key
-            latin:keyLabel="א" />
-        <Key
-            latin:keyLabel="ט" />
-        <Key
-            latin:keyLabel="ו" />
-        <Key
-            latin:keyLabel="ן" />
-        <Key
-            latin:keyLabel="ם" />
-        <Key
-            latin:keyLabel="פ" />
-        <Spacer
-            latin:keyWidth="1.25%p" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="13.75%p"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyLabel="ש"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="ד" />
-        <Key
-            latin:keyLabel="ג" />
-        <Key
-            latin:keyLabel="כ" />
-        <Key
-            latin:keyLabel="ע" />
-        <Key
-            latin:keyLabel="י" />
-        <Key
-            latin:keyLabel="ח" />
-        <Key
-            latin:keyLabel="ל" />
-        <Key
-            latin:keyLabel="ך" />
-        <Key
-            latin:keyLabel="ף"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row>
-        <Spacer
-            latin:keyWidth="5%p" />
-        <Key
-            latin:keyLabel="ז"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="ס" />
-        <Key
-            latin:keyLabel="ב" />
-        <Key
-            latin:keyLabel="ה" />
-        <Key
-            latin:keyLabel="נ" />
-        <Key
-            latin:keyLabel="מ" />
-        <Key
-            latin:keyLabel="צ" />
-        <Key
-            latin:keyLabel="ת" />
-        <Key
-            latin:keyLabel="ץ"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <include latin:keyboardLayout="@xml/kbd_qwerty_row4" />
+        latin:keyboardLayout="@xml/kbd_rows_hebrew" />
 </Keyboard>
diff --git a/java/res/xml-nb/kbd_qwerty.xml b/java/res/xml-nb/kbd_qwerty.xml
index 441b7cb..1f4e86e 100644
--- a/java/res/xml-nb/kbd_qwerty.xml
+++ b/java/res/xml-nb/kbd_qwerty.xml
@@ -20,14 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="nb"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_qwerty_rows_scandinavia" />
+        latin:keyboardLayout="@xml/kbd_rows_scandinavian" />
 </Keyboard>
diff --git a/java/res/drawable/btn_candidate_holo.xml b/java/res/xml-pl/kbd_qwerty.xml
similarity index 69%
copy from java/res/drawable/btn_candidate_holo.xml
copy to java/res/xml-pl/kbd_qwerty.xml
index 66cd246..44312c5 100644
--- a/java/res/drawable/btn_candidate_holo.xml
+++ b/java/res/xml-pl/kbd_qwerty.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** 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.
@@ -18,10 +18,10 @@
 */
 -->
 
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyboardLocale="pl"
 >
-    <item
-        android:state_pressed="true"
-        android:drawable="@drawable/btn_keyboard_key_popup_selected_holo" />
-</selector>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_qwerty" />
+</Keyboard>
diff --git a/java/res/drawable/btn_candidate_holo.xml b/java/res/xml-pt/kbd_qwerty.xml
similarity index 69%
copy from java/res/drawable/btn_candidate_holo.xml
copy to java/res/xml-pt/kbd_qwerty.xml
index 66cd246..f5dcbc6 100644
--- a/java/res/drawable/btn_candidate_holo.xml
+++ b/java/res/xml-pt/kbd_qwerty.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** 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.
@@ -18,10 +18,10 @@
 */
 -->
 
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyboardLocale="pt"
 >
-    <item
-        android:state_pressed="true"
-        android:drawable="@drawable/btn_keyboard_key_popup_selected_holo" />
-</selector>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_qwerty" />
+</Keyboard>
diff --git a/java/res/xml-ru/kbd_qwerty.xml b/java/res/xml-ru/kbd_qwerty.xml
index 0eb3115..aee1b1b 100644
--- a/java/res/xml-ru/kbd_qwerty.xml
+++ b/java/res/xml-ru/kbd_qwerty.xml
@@ -20,14 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="ru"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_ru_rows" />
+        latin:keyboardLayout="@xml/kbd_rows_russian" />
 </Keyboard>
diff --git a/java/res/xml-sr/kbd_qwerty.xml b/java/res/xml-sr/kbd_qwerty.xml
index 3995e4e..58fc187 100644
--- a/java/res/xml-sr/kbd_qwerty.xml
+++ b/java/res/xml-sr/kbd_qwerty.xml
@@ -20,14 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="sr"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_sr_rows" />
+        latin:keyboardLayout="@xml/kbd_rows_serbian" />
 </Keyboard>
diff --git a/java/res/xml-sv/kbd_qwerty.xml b/java/res/xml-sv/kbd_qwerty.xml
index 72bdc33..e29d9ab 100644
--- a/java/res/xml-sv/kbd_qwerty.xml
+++ b/java/res/xml-sv/kbd_qwerty.xml
@@ -20,14 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="sv"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_qwerty_rows_scandinavia" />
+        latin:keyboardLayout="@xml/kbd_rows_scandinavian" />
 </Keyboard>
diff --git a/java/res/xml-sw600dp/kbd_key_styles.xml b/java/res/xml-sw600dp/kbd_key_styles.xml
index 082c374..dbff293 100644
--- a/java/res/xml-sw600dp/kbd_key_styles.xml
+++ b/java/res/xml-sw600dp/kbd_key_styles.xml
@@ -22,150 +22,70 @@
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <!-- Base key style for the functional key -->
+    <key-style
+        latin:styleName="functionalKeyStyle"
+        latin:isFunctional="true" />
+    <!-- Functional key styles -->
+    <key-style
+        latin:styleName="shiftKeyStyle"
+        latin:code="@integer/key_shift"
+        latin:keyIcon="iconShiftKey"
+        latin:keyIconShifted="iconShiftedShiftKey"
+        latin:parentStyle="functionalKeyStyle"
+        latin:isSticky="true" />
+    <key-style
+        latin:styleName="deleteKeyStyle"
+        latin:code="@integer/key_delete"
+        latin:keyIcon="iconDeleteKey"
+        latin:parentStyle="functionalKeyStyle"
+        latin:isRepeatable="true" />
+    <key-style
+        latin:styleName="returnKeyStyle"
+        latin:code="@integer/key_return"
+        latin:keyIcon="iconReturnKey"
+        latin:parentStyle="functionalKeyStyle" />
+    <key-style
+        latin:styleName="spaceKeyStyle"
+        latin:code="@integer/key_space"
+        latin:keyIconPreview="iconPreviewSpaceKey" />
+    <key-style
+        latin:styleName="nonSpecialBackgroundSpaceKeyStyle"
+        latin:code="@integer/key_space"
+        latin:keyIconPreview="iconPreviewSpaceKey" />
+    <key-style
+        latin:styleName="smileyKeyStyle"
+        latin:keyLabel=":-)"
+        latin:keyOutputText=":-) "
+        latin:keyLabelOption="popupHint"
+        latin:popupCharacters="@string/alternates_for_smiley"
+        latin:maxPopupKeyboardColumn="5" />
     <switch>
         <case
-            latin:colorScheme="white"
+            latin:voiceKeyEnabled="true"
         >
             <key-style
-                latin:styleName="functionalKeyStyle"
-                latin:isModifier="true" />
-        </case>
-        <case
-            latin:colorScheme="black"
-        >
-            <key-style
-                latin:styleName="functionalKeyStyle" />
-        </case>
-    </switch>
-    <!-- Base key style for the key which may have settings key as popup key -->
-    <switch>
-        <case
-            latin:clobberSettingsKey="true"
-        >
-            <key-style
-                latin:styleName="settingsPopupStyle"
+                latin:styleName="shortcutOrSettingsKeyStyle"
+                latin:code="@integer/key_shortcut"
+                latin:keyIcon="iconShortcutKey"
+                latin:keyIconPreview="iconPreviewShortcutKey"
+                latin:keyLabelOption="popupHint"
+                latin:popupCharacters="\@icon/5|\@integer/key_settings"
                 latin:parentStyle="functionalKeyStyle" />
         </case>
         <default>
             <key-style
-                latin:styleName="settingsPopupStyle"
-                latin:keyHintIcon="@drawable/hint_popup_holo"
-                latin:popupCharacters="\@drawable/sym_keyboard_settings_holo|\@integer/key_settings"
+                latin:styleName="shortcutOrSettingsKeyStyle"
+                latin:code="@integer/key_settings"
+                latin:keyIcon="iconSettingsKey"
+                latin:keyIconPreview="iconPreviewSettingsKey"
                 latin:parentStyle="functionalKeyStyle" />
         </default>
     </switch>
-    <!-- Functional key styles -->
-    <switch>
-        <case
-            latin:colorScheme="white"
-        >
-            <key-style
-                latin:styleName="shiftKeyStyle"
-                latin:code="@integer/key_shift"
-                latin:keyIcon="@drawable/sym_keyboard_shift_holo"
-                latin:shiftedIcon="@drawable/sym_keyboard_shift_locked_holo"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_shift"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isSticky="true" />
-            <key-style
-                latin:styleName="deleteKeyStyle"
-                latin:code="@integer/key_delete"
-                latin:keyIcon="@drawable/sym_keyboard_delete_holo"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_delete"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isRepeatable="true" />
-            <key-style
-                latin:styleName="returnKeyStyle"
-                latin:code="@integer/key_return"
-                latin:keyIcon="@drawable/sym_keyboard_return_holo"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_return"
-                latin:parentStyle="functionalKeyStyle" />
-            <key-style
-                latin:styleName="spaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-            <key-style
-                latin:styleName="nonSpecialBackgroundSpaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-            <key-style
-                latin:styleName="smileyKeyStyle"
-                latin:keyLabel=":-)"
-                latin:keyOutputText=":-) "
-                latin:keyHintIcon="@drawable/hint_popup_holo"
-                latin:popupCharacters="@string/alternates_for_smiley"
-                latin:maxPopupKeyboardColumn="5" />
-            <key-style
-                latin:styleName="micKeyStyle"
-                latin:code="@integer/key_voice"
-                latin:keyIcon="@drawable/sym_keyboard_voice_holo"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_mic"
-                latin:parentStyle="settingsPopupStyle" />
-            <key-style
-                latin:styleName="settingsKeyStyle"
-                latin:code="@integer/key_settings"
-                latin:keyIcon="@drawable/sym_keyboard_settings_holo"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_settings"
-                latin:parentStyle="functionalKeyStyle" />
-        </case>
-        <case
-            latin:colorScheme="black"
-        >
-            <key-style
-                latin:styleName="shiftKeyStyle"
-                latin:code="@integer/key_shift"
-                latin:keyIcon="@drawable/sym_bkeyboard_shift"
-                latin:shiftedIcon="@drawable/sym_bkeyboard_shift_locked"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_shift"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isSticky="true" />
-            <key-style
-                latin:styleName="deleteKeyStyle"
-                latin:code="@integer/key_delete"
-                latin:keyIcon="@drawable/sym_bkeyboard_delete"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_delete"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isRepeatable="true" />
-            <key-style
-                latin:styleName="returnKeyStyle"
-                latin:code="@integer/key_return"
-                latin:keyIcon="@drawable/sym_bkeyboard_return"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_return"
-                latin:parentStyle="functionalKeyStyle" />
-            <key-style
-                latin:styleName="spaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-            <key-style
-                latin:styleName="nonSpecialBackgroundSpaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-            <key-style
-                latin:styleName="smileyKeyStyle"
-                latin:keyLabel=":-)"
-                latin:keyOutputText=":-) "
-                latin:keyHintIcon="@drawable/hint_popup_holo"
-                latin:popupCharacters="@string/alternates_for_smiley"
-                latin:maxPopupKeyboardColumn="5" />
-            <key-style
-                latin:styleName="micKeyStyle"
-                latin:code="@integer/key_voice"
-                latin:keyIcon="@drawable/sym_bkeyboard_mic"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_mic"
-                latin:parentStyle="settingsPopupStyle" />
-            <key-style
-                latin:styleName="settingsKeyStyle"
-                latin:code="@integer/key_settings"
-                latin:keyIcon="@drawable/sym_bkeyboard_settings"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_settings"
-                latin:parentStyle="functionalKeyStyle" />
-        </case>
-    </switch>
     <key-style
         latin:styleName="tabKeyStyle"
         latin:code="@integer/key_tab"
-        latin:keyIcon="@drawable/sym_keyboard_tab_holo"
-        latin:iconPreview="@drawable/sym_keyboard_tab_holo"
+        latin:keyIcon="iconTabKey"
+        latin:keyIconPreview="iconPreviewTabKey"
         latin:parentStyle="functionalKeyStyle" />
     <key-style
         latin:styleName="toSymbolKeyStyle"
@@ -189,9 +109,8 @@
     <key-style
         latin:styleName="comKeyStyle"
         latin:keyLabel="@string/keylabel_for_popular_domain"
-        latin:keyLabelOption="fontNormal"
+        latin:keyLabelOption="fontNormal|popupHint"
         latin:keyOutputText="@string/keylabel_for_popular_domain"
-        latin:keyHintIcon="@drawable/hint_popup_holo"
         latin:popupCharacters="@string/alternates_for_popular_domain" />
     <switch>
         <case
diff --git a/java/res/xml-sw600dp/kbd_number.xml b/java/res/xml-sw600dp/kbd_number.xml
index f0972b3..42d7d07 100644
--- a/java/res/xml-sw600dp/kbd_number.xml
+++ b/java/res/xml-sw600dp/kbd_number.xml
@@ -20,14 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
     latin:keyWidth="16.75%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -48,8 +41,8 @@
                     latin:keyStyle="num3KeyStyle" />
                 <Key
                     latin:keyStyle="deleteKeyStyle"
-                    latin:keyXPos="-11.0%p"
-                    latin:keyWidth="0%p"
+                    latin:keyXPos="-11.00%p"
+                    latin:keyWidth="fillRight"
                     latin:keyEdgeFlags="right" />
             </Row>
             <Row>
@@ -62,8 +55,8 @@
                     latin:keyStyle="num6KeyStyle" />
                 <Key
                     latin:keyStyle="returnKeyStyle"
-                    latin:keyXPos="-11.0%p"
-                    latin:keyWidth="0%p"
+                    latin:keyXPos="-11.00%p"
+                    latin:keyWidth="fillRight"
                     latin:keyEdgeFlags="right" />
             </Row>
             <Row>
@@ -79,17 +72,18 @@
             <Row>
                 <Key
                     latin:keyStyle="tabKeyStyle"
-                    latin:keyWidth="11.0%p"
+                    latin:keyWidth="11.00%p"
                     latin:keyEdgeFlags="left" />
                 <Spacer
                     latin:keyXPos="24.875%p" />
                 <Key
                     latin:keyStyle="num0KeyStyle" />
-                <Spacer
+                <Spacer />
+                <Key
+                    latin:keyStyle="shortcutOrSettingsKeyStyle"
                     latin:keyXPos="-11.00%p"
-                    latin:keyWidth="0dp" />
-                <include
-                    latin:keyboardLayout="@xml/kbd_qwerty_f2" />
+                    latin:keyWidth="fillRight"
+                    latin:keyEdgeFlags="right" />
             </Row>
         </case>
         <!-- latin:passwordInput="false" -->
@@ -98,13 +92,16 @@
             <Row>
                 <Key
                     latin:keyLabel="-"
-                    latin:keyXPos="11.0%p"
+                    latin:keyStyle="numLabelStyle"
+                    latin:keyXPos="11.00%p"
                     latin:keyWidth="9.25%p" />
                 <Key
                     latin:keyLabel="+"
+                    latin:keyStyle="numLabelStyle"
                     latin:keyWidth="9.25%p" />
                 <Key
                     latin:keyLabel="."
+                    latin:keyStyle="numLabelStyle"
                     latin:keyWidth="9.25%p" />
                 <Key
                     latin:keyLabel="1"
@@ -116,19 +113,22 @@
                 <Key
                     latin:keyStyle="deleteKeyStyle"
                     latin:keyXPos="-11.00%p"
-                    latin:keyWidth="-11.00%p"
+                    latin:keyWidth="fillBoth"
                     latin:keyEdgeFlags="right" />
             </Row>
             <Row>
                 <Key
                     latin:keyLabel="*"
-                    latin:keyXPos="11.0%p"
+                    latin:keyStyle="numLabelStyle"
+                    latin:keyXPos="11.00%p"
                     latin:keyWidth="9.25%p" />
                 <Key
                     latin:keyLabel="/"
+                    latin:keyStyle="numLabelStyle"
                     latin:keyWidth="9.25%p" />
                 <Key
                     latin:keyLabel=","
+                    latin:keyStyle="numLabelStyle"
                     latin:keyWidth="9.25%p" />
                 <Key
                     latin:keyLabel="4"
@@ -140,7 +140,7 @@
                 <Key
                     latin:keyStyle="returnKeyStyle"
                     latin:keyXPos="-11.00%p"
-                    latin:keyWidth="-11.00%p"
+                    latin:keyWidth="fillBoth"
                     latin:keyEdgeFlags="right" />
             </Row>
             <Row>
@@ -149,13 +149,16 @@
                      edge key. -->
                 <Key
                     latin:keyLabel="("
-                    latin:keyXPos="11.0%p"
+                    latin:keyStyle="numLabelStyle"
+                    latin:keyXPos="11.00%p"
                     latin:keyWidth="9.25%p" />
                 <Key
                     latin:keyLabel=")"
+                    latin:keyStyle="numLabelStyle"
                     latin:keyWidth="9.25%p" />
                 <Key
                     latin:keyLabel="="
+                    latin:keyStyle="numLabelStyle"
                     latin:keyWidth="9.25%p" />
                 <Key
                     latin:keyLabel="7"
@@ -175,17 +178,18 @@
                     latin:keyStyle="nonSpecialBackgroundSpaceKeyStyle"
                     latin:keyWidth="27.75%p" />
                 <Key
-                    latin:keyLabel="*"
+                    latin:keyStyle="numStarKeyStyle"
                     latin:keyXPos="38.75%p" />
                 <Key
                     latin:keyLabel="0" />
                 <Key
-                    latin:keyLabel="#" />
-                <Spacer
+                    latin:keyLabel="#"
+                    latin:keyStyle="numLabelStyle" />
+                <Key
+                    latin:keyStyle="shortcutOrSettingsKeyStyle"
                     latin:keyXPos="-11.00%p"
-                    latin:keyWidth="0dp" />
-                <include
-                    latin:keyboardLayout="@xml/kbd_qwerty_f2" />
+                    latin:keyWidth="fillBoth"
+                    latin:keyEdgeFlags="right" />
             </Row>
         </default>
     </switch>
diff --git a/java/res/xml-sw600dp/kbd_numkey_styles.xml b/java/res/xml-sw600dp/kbd_numkey_styles.xml
deleted file mode 100644
index b10dc93..0000000
--- a/java/res/xml-sw600dp/kbd_numkey_styles.xml
+++ /dev/null
@@ -1,150 +0,0 @@
-<?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"
->
-    <switch>
-        <case
-            latin:colorScheme="white"
-        >
-            <key-style
-                latin:styleName="num0KeyStyle"
-                latin:code="48"
-                latin:keyIcon="@drawable/sym_keyboard_num0_holo" />
-            <key-style
-                latin:styleName="num1KeyStyle"
-                latin:code="49"
-                latin:keyIcon="@drawable/sym_keyboard_num1_holo" />
-            <key-style
-                latin:styleName="num2KeyStyle"
-                latin:code="50"
-                latin:keyIcon="@drawable/sym_keyboard_num2_holo" />
-            <key-style
-                latin:styleName="num3KeyStyle"
-                latin:code="51"
-                latin:keyIcon="@drawable/sym_keyboard_num3_holo" />
-            <key-style
-                latin:styleName="num4KeyStyle"
-                latin:code="52"
-                latin:keyIcon="@drawable/sym_keyboard_num4_holo" />
-            <key-style
-                latin:styleName="num5KeyStyle"
-                latin:code="53"
-                latin:keyIcon="@drawable/sym_keyboard_num5_holo" />
-            <key-style
-                latin:styleName="num6KeyStyle"
-                latin:code="54"
-                latin:keyIcon="@drawable/sym_keyboard_num6_holo" />
-            <key-style
-                latin:styleName="num7KeyStyle"
-                latin:code="55"
-                latin:keyIcon="@drawable/sym_keyboard_num7_holo" />
-            <key-style
-                latin:styleName="num8KeyStyle"
-                latin:code="56"
-                latin:keyIcon="@drawable/sym_keyboard_num8_holo" />
-            <key-style
-                latin:styleName="num9KeyStyle"
-                latin:code="57"
-                latin:keyIcon="@drawable/sym_keyboard_num9_holo" />
-            <key-style
-                latin:styleName="numStarKeyStyle"
-                latin:code="42"
-                latin:keyIcon="@drawable/sym_keyboard_numbstar_holo" />
-            <key-style
-                latin:styleName="numPoundKeyStyle"
-                latin:code="35"
-                latin:keyIcon="@drawable/sym_keyboard_numbpound_holo" />
-            <key-style
-                latin:styleName="numAltKeyStyle"
-                latin:code="@integer/key_switch_alpha_symbol"
-                latin:keyIcon="@drawable/sym_keyboard_numalt"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_numalt" />
-            <key-style
-                latin:styleName="numSpaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:keyIcon="@drawable/sym_keyboard_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-        </case>
-        <case
-            latin:colorScheme="black"
-        >
-            <key-style
-                latin:styleName="num0KeyStyle"
-                latin:code="48"
-                latin:keyIcon="@drawable/sym_bkeyboard_num0" />
-            <key-style
-                latin:styleName="num1KeyStyle"
-                latin:code="49"
-                latin:keyIcon="@drawable/sym_bkeyboard_num1" />
-            <key-style
-                latin:styleName="num2KeyStyle"
-                latin:code="50"
-                latin:keyIcon="@drawable/sym_bkeyboard_num2" />
-            <key-style
-                latin:styleName="num3KeyStyle"
-                latin:code="51"
-                latin:keyIcon="@drawable/sym_bkeyboard_num3" />
-            <key-style
-                latin:styleName="num4KeyStyle"
-                latin:code="52"
-                latin:keyIcon="@drawable/sym_bkeyboard_num4" />
-            <key-style
-                latin:styleName="num5KeyStyle"
-                latin:code="53"
-                latin:keyIcon="@drawable/sym_bkeyboard_num5" />
-            <key-style
-                latin:styleName="num6KeyStyle"
-                latin:code="54"
-                latin:keyIcon="@drawable/sym_bkeyboard_num6" />
-            <key-style
-                latin:styleName="num7KeyStyle"
-                latin:code="55"
-                latin:keyIcon="@drawable/sym_bkeyboard_num7" />
-            <key-style
-                latin:styleName="num8KeyStyle"
-                latin:code="56"
-                latin:keyIcon="@drawable/sym_bkeyboard_num8" />
-            <key-style
-                latin:styleName="num9KeyStyle"
-                latin:code="57"
-                latin:keyIcon="@drawable/sym_bkeyboard_num9" />
-            <key-style
-                latin:styleName="numStarKeyStyle"
-                latin:code="42"
-                latin:keyIcon="@drawable/sym_bkeyboard_numstar" />
-            <key-style
-                latin:styleName="numPoundKeyStyle"
-                latin:code="35"
-                latin:keyIcon="@drawable/sym_bkeyboard_numpound" />
-            <key-style
-                latin:styleName="numAltKeyStyle"
-                latin:code="@integer/key_switch_alpha_symbol"
-                latin:keyIcon="@drawable/sym_bkeyboard_numalt"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_numalt" />
-            <key-style
-                latin:styleName="numSpaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:keyIcon="@drawable/sym_bkeyboard_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-        </case>
-    </switch>
-</merge>
diff --git a/java/res/xml-sw600dp/kbd_phone.xml b/java/res/xml-sw600dp/kbd_phone.xml
index 220c4b8..017d003 100644
--- a/java/res/xml-sw600dp/kbd_phone.xml
+++ b/java/res/xml-sw600dp/kbd_phone.xml
@@ -20,14 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
     latin:keyWidth="16.75%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -36,13 +29,13 @@
     <!-- This row is intentionally not marked as a top row -->
     <Row>
         <Key
-            latin:code="45"
-            latin:keyLabel=" - "
+            latin:keyLabel="-"
+            latin:keyStyle="numLabelStyle"
             latin:keyXPos="15.625%p"
             latin:keyWidth="9.25%p" />
         <Key
-            latin:code="43"
-            latin:keyLabel=" + "
+            latin:keyLabel="+"
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="9.25%p" />
         <Key
             latin:keyStyle="num1KeyStyle"
@@ -54,18 +47,18 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-11.00%p"
-            latin:keyWidth="-11.00%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
         <Key
-            latin:code="44"
-            latin:keyLabel=" , "
+            latin:keyLabel=","
+            latin:keyStyle="numLabelStyle"
             latin:keyXPos="15.625%p"
             latin:keyWidth="9.25%p" />
         <Key
-            latin:code="46"
-            latin:keyLabel=" . "
+            latin:keyLabel="."
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="9.25%p" />
         <Key
             latin:keyStyle="num4KeyStyle"
@@ -77,24 +70,24 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-11.00%p"
-            latin:keyWidth="-11.00%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
         <Key
             latin:keyStyle="moreKeyStyle"
-            latin:keyWidth="11.00%p"
+            latin:keyWidth="11.0%p"
             latin:keyEdgeFlags="left" />
         <!-- There is an empty area below the "More" key and left of the "(" key.  To ignore
              the touch event on the area, "(" is intentionally not marked as a left edge key. -->
         <Key
-            latin:code="40"
-            latin:keyLabel=" ( "
+            latin:keyLabel="("
+            latin:keyStyle="numLabelStyle"
             latin:keyXPos="15.625%p"
             latin:keyWidth="9.25%p" />
         <Key
-            latin:code="41"
-            latin:keyLabel=" ) "
+            latin:keyLabel=")"
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="9.25%p" />
         <Key
             latin:keyStyle="num7KeyStyle"
@@ -120,11 +113,12 @@
         <Key
             latin:keyStyle="num0KeyStyle" />
         <Key
-            latin:keyStyle="numPoundKeyStyle" />
-        <Spacer
+            latin:keyLabel="#"
+            latin:keyStyle="numLabelStyle" />
+        <Key
+            latin:keyStyle="shortcutOrSettingsKeyStyle"
             latin:keyXPos="-11.00%p"
-            latin:keyWidth="0dp" />
-        <include
-            latin:keyboardLayout="@xml/kbd_qwerty_f2" />
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
     </Row>
 </Keyboard>
diff --git a/java/res/xml-sw600dp/kbd_phone_symbols.xml b/java/res/xml-sw600dp/kbd_phone_symbols.xml
index 43c4f6d..168e6b1 100644
--- a/java/res/xml-sw600dp/kbd_phone_symbols.xml
+++ b/java/res/xml-sw600dp/kbd_phone_symbols.xml
@@ -20,14 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
     latin:keyWidth="16.75%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -36,13 +29,13 @@
     <!-- This row is intentionally not marked as a top row -->
     <Row>
         <Key
-            latin:code="45"
-            latin:keyLabel=" - "
-            latin:keyXPos="11.0%p"
+            latin:keyLabel="-"
+            latin:keyStyle="numLabelStyle"
+            latin:keyXPos="11.00%p"
             latin:keyWidth="9.25%p" />
         <Key
-            latin:code="43"
-            latin:keyLabel=" + "
+            latin:keyLabel="+"
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="9.25%p" />
         <Key
             latin:code="44"
@@ -58,18 +51,18 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-11.00%p"
-            latin:keyWidth="-11.00%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
         <Key
-            latin:code="44"
-            latin:keyLabel=" , "
-            latin:keyXPos="11.0%p"
+            latin:keyLabel=","
+            latin:keyStyle="numLabelStyle"
+            latin:keyXPos="11.00%p"
             latin:keyWidth="9.25%p" />
         <Key
-            latin:code="46"
-            latin:keyLabel=" . "
+            latin:keyLabel="."
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="9.25%p" />
         <Key
             latin:code="59"
@@ -85,7 +78,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-11.00%p"
-            latin:keyWidth="-11.00%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
@@ -96,16 +89,16 @@
         <!-- There is an empty area below the "More" key and left of the "(" key.  To ignore
              the touch event on the area, "(" is intentionally not marked as a left edge key. -->
         <Key
-            latin:code="40"
-            latin:keyLabel=" ( "
+            latin:keyLabel="("
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="9.25%p" />
         <Key
-            latin:code="41"
-            latin:keyLabel=" ) "
+            latin:keyLabel=")"
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="9.25%p" />
         <Key
-            latin:code="78"
-            latin:keyLabel=" N "
+            latin:keyLabel="N"
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="9.25%p" />
         <Key
             latin:keyStyle="num7KeyStyle"
@@ -130,11 +123,12 @@
         <Key
             latin:keyStyle="num0KeyStyle" />
         <Key
-            latin:keyStyle="numPoundKeyStyle" />
-        <Spacer
+            latin:keyLabel="#"
+            latin:keyStyle="numLabelStyle" />
+        <Key
+            latin:keyStyle="shortcutOrSettingsKeyStyle"
             latin:keyXPos="-11.00%p"
-            latin:keyWidth="0dp" />
-        <include
-            latin:keyboardLayout="@xml/kbd_qwerty_f2" />
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
     </Row>
 </Keyboard>
diff --git a/java/res/xml-sw600dp/kbd_qwerty.xml b/java/res/xml-sw600dp/kbd_qwerty.xml
deleted file mode 100644
index 9541e13..0000000
--- a/java/res/xml-sw600dp/kbd_qwerty.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:keyWidth="10%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
->
-    <include
-        latin:keyboardLayout="@xml/kbd_qwerty_rows" />
-</Keyboard>
diff --git a/java/res/xml-sw600dp/kbd_qwerty_f2.xml b/java/res/xml-sw600dp/kbd_qwerty_f2.xml
deleted file mode 100644
index e6c57f8..0000000
--- a/java/res/xml-sw600dp/kbd_qwerty_f2.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?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"
->
-    <switch>
-        <case
-            latin:f2KeyMode="settings"
-        >
-            <Key
-                latin:keyStyle="settingsKeyStyle"
-                latin:keyWidth="0%p"
-                latin:keyEdgeFlags="right" />
-        </case>
-        <case
-            latin:f2KeyMode="shortcutIme"
-        >
-            <switch>
-                <case
-                    latin:voiceKeyEnabled="true"
-                >
-                    <Key
-                        latin:keyStyle="micKeyStyle"
-                        latin:keyWidth="0%p"
-                        latin:keyEdgeFlags="right" />
-                </case>
-                <!-- voiceKeyEnabled="false" -->
-                <default>
-                    <Spacer />
-                </default>
-            </switch>
-        </case>
-        <case
-            latin:f2KeyMode="shortcutImeOrSettings"
-        >
-            <switch>
-                <case
-                    latin:voiceKeyEnabled="true"
-                >
-                    <Key
-                        latin:keyStyle="micKeyStyle"
-                        latin:keyWidth="0%p"
-                        latin:keyEdgeFlags="right" />
-                </case>
-                <!-- voiceKeyEnabled="false" -->
-                <default>
-                    <Key
-                        latin:keyStyle="settingsKeyStyle"
-                        latin:keyWidth="0%p"
-                        latin:keyEdgeFlags="right" />
-                </default>
-            </switch>
-        </case>
-        <!-- f2KeyMode="none" -->
-        <default>
-            <Spacer />
-        </default>
-    </switch>
-</merge>
diff --git a/java/res/xml-sw600dp/kbd_qwerty_row1.xml b/java/res/xml-sw600dp/kbd_qwerty_row1.xml
index 1a6ed97..b781d68 100644
--- a/java/res/xml-sw600dp/kbd_qwerty_row1.xml
+++ b/java/res/xml-sw600dp/kbd_qwerty_row1.xml
@@ -59,7 +59,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="-10.0%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
 </merge>
diff --git a/java/res/xml-sw600dp/kbd_qwerty_row2.xml b/java/res/xml-sw600dp/kbd_qwerty_row2.xml
index 1e6011c..05b005a 100644
--- a/java/res/xml-sw600dp/kbd_qwerty_row2.xml
+++ b/java/res/xml-sw600dp/kbd_qwerty_row2.xml
@@ -53,7 +53,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="-14.6%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
 </merge>
diff --git a/java/res/xml-sw600dp/kbd_qwerty_row3.xml b/java/res/xml-sw600dp/kbd_qwerty_row3.xml
index 4118ee1..0dbc5cf 100644
--- a/java/res/xml-sw600dp/kbd_qwerty_row3.xml
+++ b/java/res/xml-sw600dp/kbd_qwerty_row3.xml
@@ -58,15 +58,13 @@
             <default>
                 <Key
                     latin:keyLabel=","
-                    latin:manualTemporaryUpperCaseCode="33"
-                    latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="!"
                     latin:popupCharacters="!" />
                 <Key
                     latin:keyLabel="."
-                    latin:manualTemporaryUpperCaseCode="63"
-                    latin:keyHintIcon="@drawable/key_hint_question_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="\?"
                     latin:popupCharacters="\?" />
             </default>
         </switch>
diff --git a/java/res/xml-sw600dp/kbd_qwerty_row4.xml b/java/res/xml-sw600dp/kbd_qwerty_row4.xml
index 6378ea4..958a7ed 100644
--- a/java/res/xml-sw600dp/kbd_qwerty_row4.xml
+++ b/java/res/xml-sw600dp/kbd_qwerty_row4.xml
@@ -48,9 +48,8 @@
             <default>
                 <Key
                     latin:keyLabel="/"
-                    latin:manualTemporaryUpperCaseCode="64"
-                    latin:keyHintIcon="@drawable/key_hint_at_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_at_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="\@"
                     latin:popupCharacters="\@" />
             </default>
         </switch>
@@ -75,18 +74,16 @@
                     >
                         <Key
                             latin:keyLabel="/"
-                            latin:manualTemporaryUpperCaseCode="58"
-                            latin:keyHintIcon="@drawable/key_hint_colon_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_colon_large_holo"
+                            latin:keyLabelOption="hasUppercaseLetter"
+                            latin:keyHintLetter=":"
                             latin:popupCharacters=":"
                             latin:keyWidth="9.750%p" />
                     </case>
                     <default>
                         <Key
                             latin:keyLabel="\?"
-                            latin:manualTemporaryUpperCaseCode="95"
-                            latin:keyHintIcon="@drawable/key_hint_underline_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_underline_large_holo"
+                            latin:keyLabelOption="hasUppercaseLetter"
+                            latin:keyHintLetter="_"
                             latin:popupCharacters="_"
                             latin:keyWidth="9.750%p" />
                     </default>
@@ -102,9 +99,8 @@
                     <default>
                         <Key
                             latin:keyLabel="!"
-                            latin:manualTemporaryUpperCaseCode="39"
-                            latin:keyHintIcon="@drawable/key_hint_quote_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_quote_large_holo"
+                            latin:keyLabelOption="hasUppercaseLetter"
+                            latin:keyHintLetter="\'"
                             latin:popupCharacters="\'"
                             latin:keyWidth="9.750%p" />
                     </default>
@@ -125,18 +121,16 @@
                     >
                         <Key
                             latin:keyLabel="/"
-                            latin:manualTemporaryUpperCaseCode="58"
-                            latin:keyHintIcon="@drawable/key_hint_colon_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_colon_large_holo"
+                            latin:keyLabelOption="hasUppercaseLetter"
+                            latin:keyHintLetter=":"
                             latin:popupCharacters=":"
                             latin:keyWidth="9.750%p" />
                     </case>
                     <default>
                         <Key
                             latin:keyLabel="\'"
-                            latin:manualTemporaryUpperCaseCode="34"
-                            latin:keyHintIcon="@drawable/key_hint_quote_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_quote_large_holo"
+                            latin:keyLabelOption="hasUppercaseLetter"
+                            latin:keyHintLetter="&quot;"
                             latin:popupCharacters="&quot;"
                             latin:keyWidth="9.750%p" />
                     </default>
@@ -152,19 +146,18 @@
                     <default>
                         <Key
                             latin:keyLabel="-"
-                            latin:manualTemporaryUpperCaseCode="95"
-                            latin:keyHintIcon="@drawable/key_hint_underline_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_underline_large_holo"
+                            latin:keyLabelOption="hasUppercaseLetter"
+                            latin:keyHintLetter="_"
                             latin:popupCharacters="_"
                             latin:keyWidth="9.750%p" />
                     </default>
                 </switch>
             </default>
         </switch>
-        <Spacer
-            latin:keyXPos="-10.00%p"
-            latin:keyWidth="0dp" />
-        <include
-            latin:keyboardLayout="@xml/kbd_qwerty_f2" />
+        <Key
+            latin:keyStyle="shortcutOrSettingsKeyStyle"
+            latin:keyXPos="-10.0%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
     </Row>
 </merge>
diff --git a/java/res/xml-sw600dp/kbd_qwerty_rows_scandinavia.xml b/java/res/xml-sw600dp/kbd_qwerty_rows_scandinavia.xml
deleted file mode 100644
index 59e86c5..0000000
--- a/java/res/xml-sw600dp/kbd_qwerty_rows_scandinavia.xml
+++ /dev/null
@@ -1,114 +0,0 @@
-<?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" />
-    <Row
-        latin:keyWidth="8.0%p"
-        latin:rowEdgeFlags="top"
-    >
-        <Key
-            latin:keyLabel="q"
-            latin:popupCharacters="@string/alternates_for_q"
-            latin:keyXPos="2.15%p"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="w"
-            latin:popupCharacters="@string/alternates_for_w" />
-        <Key
-            latin:keyLabel="e"
-            latin:popupCharacters="@string/alternates_for_e" />
-        <Key
-            latin:keyLabel="r"
-            latin:popupCharacters="@string/alternates_for_r" />
-        <Key
-            latin:keyLabel="t"
-            latin:popupCharacters="@string/alternates_for_t" />
-        <Key
-            latin:keyLabel="y"
-            latin:popupCharacters="@string/alternates_for_y" />
-        <Key
-            latin:keyLabel="u"
-            latin:popupCharacters="@string/alternates_for_u" />
-        <Key
-            latin:keyLabel="i"
-            latin:popupCharacters="@string/alternates_for_i" />
-        <Key
-            latin:keyLabel="o"
-            latin:popupCharacters="@string/alternates_for_o" />
-        <Key
-            latin:keyLabel="p"
-            latin:popupCharacters="@string/alternates_for_p" />
-        <Key
-            latin:keyLabel="å" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyXPos="-10.0%p"
-            latin:keyWidth="-10.0%p"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row
-        latin:keyWidth="8.0%p"
-    >
-        <Key
-            latin:keyLabel="a"
-            latin:popupCharacters="@string/alternates_for_a"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="s"
-            latin:popupCharacters="@string/alternates_for_s" />
-        <Key
-            latin:keyLabel="d"
-            latin:popupCharacters="@string/alternates_for_d" />
-        <Key
-            latin:keyLabel="f" />
-        <Key
-            latin:keyLabel="g"
-            latin:popupCharacters="@string/alternates_for_g" />
-        <Key
-            latin:keyLabel="h" />
-        <Key
-            latin:keyLabel="j" />
-        <Key
-            latin:keyLabel="k"
-            latin:popupCharacters="@string/alternates_for_k" />
-        <Key
-            latin:keyLabel="l"
-            latin:popupCharacters="@string/alternates_for_l" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_scandinavia_row2_10"
-            latin:popupCharacters="@string/alternates_for_scandinavia_row2_10" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_scandinavia_row2_11"
-            latin:popupCharacters="@string/alternates_for_scandinavia_row2_11" />
-        <Key
-            latin:keyStyle="returnKeyStyle"
-            latin:keyXPos="-14.6%p"
-            latin:keyWidth="-14.6%p"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/kbd_qwerty_row3" />
-    <include
-        latin:keyboardLayout="@xml/kbd_qwerty_row4" />
-</merge>
diff --git a/java/res/xml-sw600dp/kbd_row3_right.xml b/java/res/xml-sw600dp/kbd_row3_right.xml
index 7867e17..a9c5eb3 100644
--- a/java/res/xml-sw600dp/kbd_row3_right.xml
+++ b/java/res/xml-sw600dp/kbd_row3_right.xml
@@ -28,7 +28,7 @@
             <Key
                 latin:keyLabel="\@"
                 latin:keyXPos="-8.9%p"
-                latin:keyWidth="-8.9%p"
+                latin:keyWidth="fillBoth"
                 latin:keyEdgeFlags="right" />
         </case>
         <case
@@ -36,12 +36,11 @@
         >
             <Key
                 latin:keyLabel="-"
-                latin:manualTemporaryUpperCaseCode="95"
-                latin:keyHintIcon="@drawable/key_hint_underline_holo"
-                latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_underline_large_holo"
+                latin:keyLabelOption="hasUppercaseLetter"
+                latin:keyHintLetter="_"
                 latin:popupCharacters="_"
                 latin:keyXPos="-8.9%p"
-                latin:keyWidth="-8.9%p"
+                latin:keyWidth="fillBoth"
                 latin:keyEdgeFlags="right" />
         </case>
         <case
@@ -49,19 +48,18 @@
         >
             <Key
                 latin:keyLabel=":"
-                latin:manualTemporaryUpperCaseCode="43"
-                latin:keyHintIcon="@drawable/key_hint_plus_holo"
-                latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_plus_large_holo"
+                latin:keyLabelOption="hasUppercaseLetter"
+                latin:keyHintLetter="+"
                 latin:popupCharacters="+"
                 latin:keyXPos="-8.9%p"
-                latin:keyWidth="-8.9%p"
+                latin:keyWidth="fillBoth"
                 latin:keyEdgeFlags="right" />
         </case>
         <default>
             <Key
                 latin:keyStyle="smileyKeyStyle"
                 latin:keyXPos="-8.9%p"
-                latin:keyWidth="-8.9%p"
+                latin:keyWidth="fillBoth"
                 latin:keyEdgeFlags="right" />
         </default>
     </switch>
diff --git a/java/res/xml-sw600dp/kbd_rows_arabic.xml b/java/res/xml-sw600dp/kbd_rows_arabic.xml
new file mode 100644
index 0000000..bc37d5a
--- /dev/null
+++ b/java/res/xml-sw600dp/kbd_rows_arabic.xml
@@ -0,0 +1,134 @@
+<?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.
+*/
+-->
+
+<!-- This file for Arabic layout is an alpha version. It allows to enter   -->
+<!-- some right-to-left text, but it has gone through no study whatsoever, -->
+<!-- and needs to be run through UX.                                       -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_key_styles" />
+    <Row
+        latin:keyWidth="8.0%p"
+    >
+        <Key
+            latin:keyLabel="ض"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ص" />
+        <Key
+            latin:keyLabel="ث" />
+        <Key
+            latin:keyLabel="ق" />
+        <Key
+            latin:keyLabel="ف"
+            latin:popupCharacters="ف,ڤ" />
+        <Key
+            latin:keyLabel="غ" />
+        <Key
+            latin:keyLabel="ع" />
+        <Key
+            latin:keyLabel="ه"
+            latin:popupCharacters="ه,هـ" />
+        <Key
+            latin:keyLabel="خ" />
+        <Key
+            latin:keyLabel="ح" />
+        <Key
+            latin:keyLabel="ج"
+            latin:popupCharacters="ج,چ" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyXPos="-10.0%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="8.0%p"
+    >
+        <Key
+            latin:keyLabel="ش"
+            latin:keyXPos="3.0%p" />
+        <Key
+            latin:keyLabel="س" />
+        <Key
+            latin:keyLabel="ي" />
+        <Key
+            latin:keyLabel="ب"
+            latin:popupCharacters="ب,پ" />
+        <Key
+            latin:keyLabel="ل"
+            latin:popupCharacters="ل,لا" />
+        <Key
+            latin:keyLabel="ا"
+            latin:popupCharacters="ا,أ,إ,آ" />
+        <Key
+            latin:keyLabel="ت" />
+        <Key
+            latin:keyLabel="ن" />
+        <Key
+            latin:keyLabel="م" />
+        <Key
+            latin:keyLabel="ك"
+            latin:popupCharacters="ك,گ" />
+        <Key
+            latin:keyLabel="ط" />
+        <Key
+            latin:keyStyle="returnKeyStyle"
+            latin:keyXPos="-14.6%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="7.65%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="8.0%p"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ئ" />
+        <Key
+            latin:keyLabel="ء" />
+        <Key
+            latin:keyLabel="ؤ" />
+        <Key
+            latin:keyLabel="ر" />
+        <Key
+            latin:keyLabel="ذ" />
+        <Key
+            latin:keyLabel="ى" />
+        <Key
+            latin:keyLabel="ة" />
+        <Key
+            latin:keyLabel="و" />
+        <Key
+            latin:keyLabel="ز"
+            latin:popupCharacters="ز,ژ" />
+        <Key
+            latin:keyLabel="ظ" />
+        <Key
+            latin:keyLabel="د" />
+        <include
+            latin:keyboardLayout="@xml/kbd_row3_right" />
+        </Row>
+    <include latin:keyboardLayout="@xml/kbd_qwerty_row4" />
+</merge>
diff --git a/java/res/xml-sw600dp/kbd_azerty_rows.xml b/java/res/xml-sw600dp/kbd_rows_azerty.xml
similarity index 87%
rename from java/res/xml-sw600dp/kbd_azerty_rows.xml
rename to java/res/xml-sw600dp/kbd_rows_azerty.xml
index e3a8353..b53bbbb 100644
--- a/java/res/xml-sw600dp/kbd_azerty_rows.xml
+++ b/java/res/xml-sw600dp/kbd_rows_azerty.xml
@@ -24,7 +24,7 @@
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="8.5%p"
         latin:rowEdgeFlags="top"
     >
         <Key
@@ -61,15 +61,16 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="-10.0%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="8.5%p"
     >
         <Key
             latin:keyLabel="q"
             latin:popupCharacters="@string/alternates_for_q"
+            latin:keyXPos="5.0%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="s"
@@ -97,11 +98,11 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="-14.6%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
-        latin:keyWidth="8.9%p"
+        latin:keyWidth="8.5%p"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
@@ -137,21 +138,19 @@
             <default>
                 <Key
                     latin:keyLabel=","
-                    latin:manualTemporaryUpperCaseCode="33"
-                    latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="!"
                     latin:popupCharacters="!" />
                 <Key
                     latin:keyLabel="."
-                    latin:manualTemporaryUpperCaseCode="63"
-                    latin:keyHintIcon="@drawable/key_hint_question_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="\?"
                     latin:popupCharacters="\?" />
             </default>
         </switch>
         <include
             latin:keyboardLayout="@xml/kbd_row3_right" />
-   </Row>
+    </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
 </merge>
diff --git a/java/res/xml-sw600dp/kbd_rows_hebrew.xml b/java/res/xml-sw600dp/kbd_rows_hebrew.xml
new file mode 100644
index 0000000..a147537
--- /dev/null
+++ b/java/res/xml-sw600dp/kbd_rows_hebrew.xml
@@ -0,0 +1,122 @@
+<?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" />
+    <Row
+        latin:keyWidth="8.600%p"
+        latin:rowEdgeFlags="top"
+    >
+        <Key
+            latin:keyLabel="," />
+        <Key
+            latin:keyLabel="." />
+        <Key
+            latin:keyLabel="ק" />
+        <Key
+            latin:keyLabel="ר" />
+        <Key
+            latin:keyLabel="א" />
+        <Key
+            latin:keyLabel="ט" />
+        <Key
+            latin:keyLabel="ו" />
+        <Key
+            latin:keyLabel="ן" />
+        <Key
+            latin:keyLabel="ם" />
+        <Key
+            latin:keyLabel="פ" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyXPos="-12.000%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="8.600%p"
+    >
+        <Key
+            latin:keyLabel="ש"
+            latin:keyXPos="4.000%p" />
+        <Key
+            latin:keyLabel="ד" />
+        <Key
+            latin:keyLabel="ג"
+            latin:popupCharacters="ג,ג׳" />
+        <Key
+            latin:keyLabel="כ" />
+        <Key
+            latin:keyLabel="ע" />
+        <Key
+            latin:keyLabel="י"
+            latin:popupCharacters="י,ײַ" />
+        <Key
+            latin:keyLabel="ח"
+            latin:popupCharacters="ח,ח׳" />
+        <Key
+            latin:keyLabel="ל" />
+        <Key
+            latin:keyLabel="ך" />
+        <Key
+            latin:keyLabel="ף" />
+        <Key
+            latin:keyStyle="returnKeyStyle"
+            latin:keyXPos="-10.400%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="8.600%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="10.000%p"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ז"
+            latin:popupCharacters="ז,ז׳" />
+        <Key
+            latin:keyLabel="ס" />
+        <Key
+            latin:keyLabel="ב" />
+        <Key
+            latin:keyLabel="ה" />
+        <Key
+            latin:keyLabel="נ" />
+        <Key
+            latin:keyLabel="מ" />
+        <Key
+            latin:keyLabel="צ"
+            latin:popupCharacters="צ,צ׳" />
+        <Key
+            latin:keyLabel="ת"
+            latin:popupCharacters="ת,ת׳" />
+        <Key
+            latin:keyLabel="ץ"
+            latin:popupCharacters="ץ,ץ׳" />
+        <include
+            latin:keyboardLayout="@xml/kbd_row3_right" />
+    </Row>
+    <include latin:keyboardLayout="@xml/kbd_qwerty_row4" />
+</merge>
diff --git a/java/res/xml-sw600dp/kbd_qwerty_rows.xml b/java/res/xml-sw600dp/kbd_rows_qwerty.xml
similarity index 100%
rename from java/res/xml-sw600dp/kbd_qwerty_rows.xml
rename to java/res/xml-sw600dp/kbd_rows_qwerty.xml
diff --git a/java/res/xml-sw600dp/kbd_qwertz_rows.xml b/java/res/xml-sw600dp/kbd_rows_qwertz.xml
similarity index 87%
rename from java/res/xml-sw600dp/kbd_qwertz_rows.xml
rename to java/res/xml-sw600dp/kbd_rows_qwertz.xml
index 5c2fbf2..7ea9b0f 100644
--- a/java/res/xml-sw600dp/kbd_qwertz_rows.xml
+++ b/java/res/xml-sw600dp/kbd_rows_qwertz.xml
@@ -61,7 +61,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="-10.0%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <include
@@ -103,15 +103,13 @@
             <default>
                 <Key
                     latin:keyLabel=","
-                    latin:manualTemporaryUpperCaseCode="33"
-                    latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="!"
                     latin:popupCharacters="!" />
                 <Key
                     latin:keyLabel="."
-                    latin:manualTemporaryUpperCaseCode="63"
-                    latin:keyHintIcon="@drawable/key_hint_question_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="\?"
                     latin:popupCharacters="\?" />
             </default>
         </switch>
diff --git a/java/res/xml-sw600dp/kbd_ru_rows.xml b/java/res/xml-sw600dp/kbd_rows_russian.xml
similarity index 92%
rename from java/res/xml-sw600dp/kbd_ru_rows.xml
rename to java/res/xml-sw600dp/kbd_rows_russian.xml
index fb820cc..43fa665 100644
--- a/java/res/xml-sw600dp/kbd_ru_rows.xml
+++ b/java/res/xml-sw600dp/kbd_rows_russian.xml
@@ -55,7 +55,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="-10.0%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -87,7 +87,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="-14.6%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -118,9 +118,8 @@
             latin:keyLabel="ю" />
         <Key
             latin:keyLabel="."
-            latin:manualTemporaryUpperCaseCode="44"
-            latin:keyHintIcon="@drawable/key_hint_comma_holo"
-            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_comma_large_holo"
+            latin:keyLabelOption="hasUppercaseLetter"
+            latin:keyHintLetter=","
             latin:popupCharacters="," />
         <include
             latin:keyboardLayout="@xml/kbd_row3_right" />
diff --git a/java/res/xml-sw600dp/kbd_azerty_rows.xml b/java/res/xml-sw600dp/kbd_rows_scandinavian.xml
similarity index 83%
copy from java/res/xml-sw600dp/kbd_azerty_rows.xml
copy to java/res/xml-sw600dp/kbd_rows_scandinavian.xml
index e3a8353..3666ead 100644
--- a/java/res/xml-sw600dp/kbd_azerty_rows.xml
+++ b/java/res/xml-sw600dp/kbd_rows_scandinavian.xml
@@ -24,16 +24,16 @@
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="7.9%p"
         latin:rowEdgeFlags="top"
     >
         <Key
-            latin:keyLabel="a"
-            latin:popupCharacters="@string/alternates_for_a"
+            latin:keyLabel="q"
+            latin:popupCharacters="@string/alternates_for_q"
             latin:keyEdgeFlags="left" />
         <Key
-            latin:keyLabel="z"
-            latin:popupCharacters="@string/alternates_for_z" />
+            latin:keyLabel="w"
+            latin:popupCharacters="@string/alternates_for_w" />
         <Key
             latin:keyLabel="e"
             latin:popupCharacters="@string/alternates_for_e" />
@@ -59,17 +59,20 @@
             latin:keyLabel="p"
             latin:popupCharacters="@string/alternates_for_p" />
         <Key
+            latin:keyLabel="å" />
+        <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="-10.0%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="7.9%p"
     >
         <Key
-            latin:keyLabel="q"
-            latin:popupCharacters="@string/alternates_for_q"
+            latin:keyLabel="a"
+            latin:keyXPos="3.5%p"
+            latin:popupCharacters="@string/alternates_for_a"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="s"
@@ -93,23 +96,29 @@
             latin:keyLabel="l"
             latin:popupCharacters="@string/alternates_for_l" />
         <Key
-            latin:keyLabel="m" />
+            latin:keyLabel="@string/keylabel_for_scandinavia_row2_10"
+            latin:popupCharacters="@string/alternates_for_scandinavia_row2_10" />
+        <Key
+            latin:keyLabel="@string/keylabel_for_scandinavia_row2_11"
+            latin:popupCharacters="@string/alternates_for_scandinavia_row2_11" />
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="-14.6%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
-        latin:keyWidth="8.9%p"
+        latin:keyWidth="7.9%p"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="10.0%p"
             latin:keyEdgeFlags="left" />
+        <Spacer
+            latin:keyWidth="4.35%p" />
         <Key
-            latin:keyLabel="w"
-            latin:popupCharacters="@string/alternates_for_w" />
+            latin:keyLabel="z"
+            latin:popupCharacters="@string/alternates_for_z" />
         <Key
             latin:keyLabel="x" />
         <Key
@@ -124,7 +133,7 @@
             latin:keyLabel="n"
             latin:popupCharacters="@string/alternates_for_n" />
         <Key
-            latin:keyLabel="\'" />
+            latin:keyLabel="m" />
         <switch>
             <case
                 latin:mode="email"
@@ -137,21 +146,21 @@
             <default>
                 <Key
                     latin:keyLabel=","
-                    latin:manualTemporaryUpperCaseCode="33"
-                    latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="!"
                     latin:popupCharacters="!" />
                 <Key
                     latin:keyLabel="."
-                    latin:manualTemporaryUpperCaseCode="63"
-                    latin:keyHintIcon="@drawable/key_hint_question_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="\?"
                     latin:popupCharacters="\?" />
             </default>
         </switch>
+        <Spacer
+            latin:keyWidth="4.35%p" />
         <include
             latin:keyboardLayout="@xml/kbd_row3_right" />
-   </Row>
+    </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
 </merge>
diff --git a/java/res/xml-sw600dp/kbd_sr_rows.xml b/java/res/xml-sw600dp/kbd_rows_serbian.xml
similarity index 86%
rename from java/res/xml-sw600dp/kbd_sr_rows.xml
rename to java/res/xml-sw600dp/kbd_rows_serbian.xml
index 09dc2b7..80debda 100644
--- a/java/res/xml-sw600dp/kbd_sr_rows.xml
+++ b/java/res/xml-sw600dp/kbd_rows_serbian.xml
@@ -54,7 +54,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="-10.0%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -86,7 +86,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="-14.6%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -126,15 +126,13 @@
             <default>
                 <Key
                     latin:keyLabel=","
-                    latin:manualTemporaryUpperCaseCode="33"
-                    latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="!"
                     latin:popupCharacters="!" />
                 <Key
                     latin:keyLabel="."
-                    latin:manualTemporaryUpperCaseCode="63"
-                    latin:keyHintIcon="@drawable/key_hint_question_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="\?"
                     latin:popupCharacters="\?" />
             </default>
         </switch>
diff --git a/java/res/xml-sw600dp/kbd_rows_spanish.xml b/java/res/xml-sw600dp/kbd_rows_spanish.xml
new file mode 100644
index 0000000..7ef44bb
--- /dev/null
+++ b/java/res/xml-sw600dp/kbd_rows_spanish.xml
@@ -0,0 +1,69 @@
+<?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_qwerty_row1" />
+    <Row
+        latin:keyWidth="8.5%p"
+    >
+        <Key
+            latin:keyLabel="a"
+            latin:popupCharacters="@string/alternates_for_a"
+            latin:keyXPos="5.0%p"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="s"
+            latin:popupCharacters="@string/alternates_for_s" />
+        <Key
+            latin:keyLabel="d"
+            latin:popupCharacters="@string/alternates_for_d" />
+        <Key
+            latin:keyLabel="f" />
+        <Key
+            latin:keyLabel="g"
+            latin:popupCharacters="@string/alternates_for_g" />
+        <Key
+            latin:keyLabel="h" />
+        <Key
+            latin:keyLabel="j" />
+        <Key
+            latin:keyLabel="k"
+            latin:popupCharacters="@string/alternates_for_k" />
+        <Key
+            latin:keyLabel="l"
+            latin:popupCharacters="@string/alternates_for_l" />
+        <Key
+            latin:keyLabel="ñ" />
+        <Key
+            latin:keyStyle="returnKeyStyle"
+            latin:keyXPos="-14.6%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/kbd_qwerty_row3" />
+    <include
+        latin:keyboardLayout="@xml/kbd_qwerty_row4" />
+</merge>
diff --git a/java/res/xml-sw600dp/kbd_symbols.xml b/java/res/xml-sw600dp/kbd_symbols.xml
index 3f555aa..fca97c7 100644
--- a/java/res/xml-sw600dp/kbd_symbols.xml
+++ b/java/res/xml-sw600dp/kbd_symbols.xml
@@ -20,13 +20,6 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -66,7 +59,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="-10.0%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -100,7 +93,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="-14.6%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -173,7 +166,7 @@
         </switch>
         <Key
             latin:keyLabel="/"
-            latin:keyWidth="0%p" />
+            latin:keyWidth="fillRight" />
     </Row>
     <!-- This row is intentionally not marked as a bottom row -->
     <Row
@@ -216,10 +209,10 @@
                     latin:keyWidth="9.750%p" />
             </default>
         </switch>
-        <Spacer
-            latin:keyXPos="-10.00%p"
-            latin:keyWidth="0dp" />
-        <include
-            latin:keyboardLayout="@xml/kbd_qwerty_f2" />
+        <Key
+            latin:keyStyle="shortcutOrSettingsKeyStyle"
+            latin:keyXPos="-10.0%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
     </Row>
 </Keyboard>
diff --git a/java/res/xml-sw600dp/kbd_symbols_shift.xml b/java/res/xml-sw600dp/kbd_symbols_shift.xml
index 7ad7afc..c23681d 100644
--- a/java/res/xml-sw600dp/kbd_symbols_shift.xml
+++ b/java/res/xml-sw600dp/kbd_symbols_shift.xml
@@ -20,13 +20,6 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -67,7 +60,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="-10.0%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -104,7 +97,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="-14.6%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -154,10 +147,10 @@
             latin:keyStyle="spaceKeyStyle"
             latin:keyXPos="30.750%p"
             latin:keyWidth="39.750%p" />
-        <Spacer
-            latin:keyXPos="-10.00%p"
-            latin:keyWidth="0dp" />
-        <include
-            latin:keyboardLayout="@xml/kbd_qwerty_f2" />
+        <Key
+            latin:keyStyle="shortcutOrSettingsKeyStyle"
+            latin:keyXPos="-10.0%p"
+            latin:keyWidth="fillRight"
+            latin:keyEdgeFlags="right" />
     </Row>
 </Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_key_styles.xml b/java/res/xml-sw768dp/kbd_key_styles.xml
index fc06d00..5d8c081 100644
--- a/java/res/xml-sw768dp/kbd_key_styles.xml
+++ b/java/res/xml-sw768dp/kbd_key_styles.xml
@@ -22,117 +22,54 @@
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <!-- Functional key styles -->
-    <switch>
-        <case
-            latin:colorScheme="white"
-        >
-            <key-style
-                latin:styleName="functionalKeyStyle"
-                latin:isModifier="true" />
-            <key-style
-                latin:styleName="shiftKeyStyle"
-                latin:code="@integer/key_shift"
-                latin:keyIcon="@drawable/sym_keyboard_shift_holo"
-                latin:shiftedIcon="@drawable/sym_keyboard_shift_locked_holo"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_shift"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isSticky="true" />
-            <key-style
-                latin:styleName="deleteKeyStyle"
-                latin:code="@integer/key_delete"
-                latin:keyIcon="@drawable/sym_keyboard_delete_holo"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_delete"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isRepeatable="true" />
-            <key-style
-                latin:styleName="returnKeyStyle"
-                latin:code="@integer/key_return"
-                latin:keyIcon="@drawable/sym_keyboard_return_holo"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_return"
-                latin:parentStyle="functionalKeyStyle" />
-            <key-style
-                latin:styleName="spaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-            <key-style
-                latin:styleName="nonSpecialBackgroundSpaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-            <key-style
-                latin:styleName="smileyKeyStyle"
-                latin:keyLabel=":-)"
-                latin:keyOutputText=":-) "
-                latin:keyHintIcon="@drawable/hint_popup_holo"
-                latin:popupCharacters="@string/alternates_for_smiley"
-                latin:maxPopupKeyboardColumn="5" />
-            <key-style
-                latin:styleName="settingsKeyStyle"
-                latin:code="@integer/key_settings"
-                latin:keyIcon="@drawable/sym_keyboard_settings_holo"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_settings"
-                latin:parentStyle="functionalKeyStyle" />
-            <key-style
-                latin:styleName="micKeyStyle"
-                latin:code="@integer/key_voice"
-                latin:keyIcon="@drawable/sym_keyboard_voice_holo"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_mic"
-                latin:parentStyle="functionalKeyStyle" />
-        </case>
-        <case
-            latin:colorScheme="black"
-        >
-            <key-style
-                latin:styleName="functionalKeyStyle" />
-            <key-style
-                latin:styleName="shiftKeyStyle"
-                latin:code="@integer/key_shift"
-                latin:keyIcon="@drawable/sym_bkeyboard_shift"
-                latin:shiftedIcon="@drawable/sym_bkeyboard_shift_locked"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_shift"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isSticky="true" />
-            <key-style
-                latin:styleName="deleteKeyStyle"
-                latin:code="@integer/key_delete"
-                latin:keyIcon="@drawable/sym_bkeyboard_delete"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_delete"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isRepeatable="true" />
-            <key-style
-                latin:styleName="returnKeyStyle"
-                latin:code="@integer/key_return"
-                latin:keyIcon="@drawable/sym_bkeyboard_return"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_return"
-                latin:parentStyle="functionalKeyStyle" />
-            <key-style
-                latin:styleName="spaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-            <key-style
-                latin:styleName="nonSpecialBackgroundSpaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-            <key-style
-                latin:styleName="smileyKeyStyle"
-                latin:keyLabel=":-)"
-                latin:keyOutputText=":-) "
-                latin:keyHintIcon="@drawable/hint_popup_holo"
-                latin:popupCharacters="@string/alternates_for_smiley"
-                latin:maxPopupKeyboardColumn="5" />
-            <key-style
-                latin:styleName="settingsKeyStyle"
-                latin:code="@integer/key_settings"
-                latin:keyIcon="@drawable/sym_bkeyboard_settings"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_settings"
-                latin:parentStyle="functionalKeyStyle" />
-            <key-style
-                latin:styleName="micKeyStyle"
-                latin:code="@integer/key_voice"
-                latin:keyIcon="@drawable/sym_bkeyboard_mic"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_mic"
-                latin:parentStyle="functionalKeyStyle" />
-        </case>
-    </switch>
+    <key-style
+        latin:styleName="functionalKeyStyle"
+        latin:isFunctional="true" />
+    <key-style
+        latin:styleName="shiftKeyStyle"
+        latin:code="@integer/key_shift"
+        latin:keyIcon="iconShiftKey"
+        latin:keyIconShifted="iconShiftedShiftKey"
+        latin:parentStyle="functionalKeyStyle"
+        latin:isSticky="true" />
+    <key-style
+        latin:styleName="deleteKeyStyle"
+        latin:code="@integer/key_delete"
+        latin:keyIcon="iconDeleteKey"
+        latin:parentStyle="functionalKeyStyle"
+        latin:isRepeatable="true" />
+    <key-style
+        latin:styleName="returnKeyStyle"
+        latin:code="@integer/key_return"
+        latin:keyIcon="iconReturnKey"
+        latin:parentStyle="functionalKeyStyle" />
+    <key-style
+        latin:styleName="spaceKeyStyle"
+        latin:code="@integer/key_space"
+        latin:keyIconPreview="iconPreviewSpaceKey" />
+    <key-style
+        latin:styleName="nonSpecialBackgroundSpaceKeyStyle"
+        latin:code="@integer/key_space"
+        latin:keyIconPreview="iconPreviewSpaceKey" />
+    <key-style
+        latin:styleName="smileyKeyStyle"
+        latin:keyLabel=":-)"
+        latin:keyOutputText=":-) "
+        latin:keyLabelOption="popupHint"
+        latin:popupCharacters="@string/alternates_for_smiley"
+        latin:maxPopupKeyboardColumn="5" />
+    <key-style
+        latin:styleName="settingsKeyStyle"
+        latin:code="@integer/key_settings"
+        latin:keyIcon="iconSettingsKey"
+        latin:keyIconPreview="iconPreviewSettingsKey"
+        latin:parentStyle="functionalKeyStyle" />
+    <key-style
+        latin:styleName="shortcutKeyStyle"
+        latin:code="@integer/key_shortcut"
+        latin:keyIcon="iconShortcutKey"
+        latin:keyIconPreview="iconPreviewShortcutKey"
+        latin:parentStyle="functionalKeyStyle" />
     <key-style
         latin:styleName="tabKeyStyle"
         latin:code="@integer/key_tab"
@@ -161,9 +98,8 @@
     <key-style
         latin:styleName="comKeyStyle"
         latin:keyLabel="@string/keylabel_for_popular_domain"
-        latin:keyLabelOption="fontNormal"
+        latin:keyLabelOption="fontNormal|popupHint"
         latin:keyOutputText="@string/keylabel_for_popular_domain"
-        latin:keyHintIcon="@drawable/hint_popup_holo"
         latin:popupCharacters="@string/alternates_for_popular_domain" />
     <switch>
         <case
diff --git a/java/res/xml-sw768dp/kbd_number.xml b/java/res/xml-sw768dp/kbd_number.xml
index 8ffee3c..1ae6984 100644
--- a/java/res/xml-sw768dp/kbd_number.xml
+++ b/java/res/xml-sw768dp/kbd_number.xml
@@ -20,14 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
     latin:keyWidth="13.250%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -54,7 +47,7 @@
                 <Key
                     latin:keyStyle="deleteKeyStyle"
                     latin:keyXPos="-11.172%p"
-                    latin:keyWidth="0%p"
+                    latin:keyWidth="fillRight"
                     latin:keyEdgeFlags="right" />
             </Row>
             <Row>
@@ -68,7 +61,7 @@
                 <Key
                     latin:keyStyle="returnKeyStyle"
                     latin:keyXPos="-11.172%p"
-                    latin:keyWidth="0%p"
+                    latin:keyWidth="fillRight"
                     latin:keyEdgeFlags="right" />
             </Row>
             <Row>
@@ -105,13 +98,16 @@
                     latin:keyEdgeFlags="left" />
                 <Key
                     latin:keyLabel="-"
+                    latin:keyStyle="numLabelStyle"
                     latin:keyXPos="13.829%p"
                     latin:keyWidth="8.047%p" />
                 <Key
                     latin:keyLabel="+"
+                    latin:keyStyle="numLabelStyle"
                     latin:keyWidth="8.047%p" />
                 <Key
                     latin:keyLabel="."
+                    latin:keyStyle="numLabelStyle"
                     latin:keyWidth="8.047%p" />
                 <Key
                     latin:keyLabel="1"
@@ -123,19 +119,22 @@
                 <Key
                     latin:keyStyle="deleteKeyStyle"
                     latin:keyXPos="-11.172%p"
-                    latin:keyWidth="0%p"
+                    latin:keyWidth="fillRight"
                     latin:keyEdgeFlags="right" />
             </Row>
             <Row>
                 <Key
                     latin:keyLabel="*"
+                    latin:keyStyle="numLabelStyle"
                     latin:keyXPos="13.829%p"
                     latin:keyWidth="8.047%p" />
                 <Key
                     latin:keyLabel="/"
+                    latin:keyStyle="numLabelStyle"
                     latin:keyWidth="8.047%p" />
                 <Key
                     latin:keyLabel=","
+                    latin:keyStyle="numLabelStyle"
                     latin:keyWidth="8.047%p" />
                 <Key
                     latin:keyLabel="4"
@@ -147,7 +146,7 @@
                 <Key
                     latin:keyStyle="returnKeyStyle"
                     latin:keyXPos="-11.172%p"
-                    latin:keyWidth="0%p"
+                    latin:keyWidth="fillRight"
                     latin:keyEdgeFlags="right" />
             </Row>
             <Row>
@@ -156,13 +155,16 @@
                      edge key. -->
                 <Key
                     latin:keyLabel="("
+                    latin:keyStyle="numLabelStyle"
                     latin:keyXPos="13.829%p"
                     latin:keyWidth="8.047%p" />
                 <Key
                     latin:keyLabel=")"
+                    latin:keyStyle="numLabelStyle"
                     latin:keyWidth="8.047%p" />
                 <Key
                     latin:keyLabel="="
+                    latin:keyStyle="numLabelStyle"
                     latin:keyWidth="8.047%p" />
                 <Key
                     latin:keyLabel="7"
@@ -183,30 +185,28 @@
                             latin:keyStyle="settingsKeyStyle"
                             latin:keyWidth="8.047%p" />
                     </case>
-                    <default>
-                        <Spacer
-                            latin:keyWidth="8.047%p" />
-                    </default>
                 </switch>
                 <Key
                     latin:keyStyle="nonSpecialBackgroundSpaceKeyStyle"
                     latin:keyXPos="13.829%p"
-                    latin:keyWidth="24.14%p" />
+                    latin:keyWidth="24.140%p" />
                 <Key
                     latin:keyLabel="*"
+                    latin:keyStyle="numLabelStyle"
                     latin:keyXPos="43.125%p" />
                 <Key
                     latin:keyLabel="0" />
                 <Key
-                    latin:keyLabel="#" />
+                    latin:keyLabel="#"
+                    latin:keyStyle="numLabelStyle" />
                 <switch>
                     <case
                         latin:voiceKeyEnabled="true"
                     >
                         <Key
-                            latin:keyStyle="micKeyStyle"
+                            latin:keyStyle="shortcutKeyStyle"
                             latin:keyXPos="-8.047%p"
-                            latin:keyWidth="0%p" />
+                            latin:keyWidth="fillRight" />
                     </case>
                 </switch>
             </Row>
diff --git a/java/res/xml-sw768dp/kbd_numkey_styles.xml b/java/res/xml-sw768dp/kbd_numkey_styles.xml
deleted file mode 100644
index e27db94..0000000
--- a/java/res/xml-sw768dp/kbd_numkey_styles.xml
+++ /dev/null
@@ -1,148 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <switch>
-        <case
-            latin:colorScheme="white"
-        >
-            <key-style
-                latin:styleName="num0KeyStyle"
-                latin:code="48"
-                latin:keyIcon="@drawable/sym_keyboard_num0_holo" />
-            <key-style
-                latin:styleName="num1KeyStyle"
-                latin:code="49"
-                latin:keyIcon="@drawable/sym_keyboard_num1_holo" />
-            <key-style
-                latin:styleName="num2KeyStyle"
-                latin:code="50"
-                latin:keyIcon="@drawable/sym_keyboard_num2_holo" />
-            <key-style
-                latin:styleName="num3KeyStyle"
-                latin:code="51"
-                latin:keyIcon="@drawable/sym_keyboard_num3_holo" />
-            <key-style
-                latin:styleName="num4KeyStyle"
-                latin:code="52"
-                latin:keyIcon="@drawable/sym_keyboard_num4_holo" />
-            <key-style
-                latin:styleName="num5KeyStyle"
-                latin:code="53"
-                latin:keyIcon="@drawable/sym_keyboard_num5_holo" />
-            <key-style
-                latin:styleName="num6KeyStyle"
-                latin:code="54"
-                latin:keyIcon="@drawable/sym_keyboard_num6_holo" />
-            <key-style
-                latin:styleName="num7KeyStyle"
-                latin:code="55"
-                latin:keyIcon="@drawable/sym_keyboard_num7_holo" />
-            <key-style
-                latin:styleName="num8KeyStyle"
-                latin:code="56"
-                latin:keyIcon="@drawable/sym_keyboard_num8_holo" />
-            <key-style
-                latin:styleName="num9KeyStyle"
-                latin:code="57"
-                latin:keyIcon="@drawable/sym_keyboard_num9_holo" />
-            <key-style
-                latin:styleName="numStarKeyStyle"
-                latin:code="42"
-                latin:keyIcon="@drawable/sym_keyboard_numbstar_holo" />
-            <key-style
-                latin:styleName="numPoundKeyStyle"
-                latin:code="35"
-                latin:keyIcon="@drawable/sym_keyboard_numbpound_holo" />
-            <key-style
-                latin:styleName="numAltKeyStyle"
-                latin:code="@integer/key_switch_alpha_symbol"
-                latin:keyIcon="@drawable/sym_keyboard_numalt"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_numalt" />
-            <key-style
-                latin:styleName="numSpaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-        </case>
-        <case
-            latin:colorScheme="black"
-        >
-            <key-style
-                latin:styleName="num0KeyStyle"
-                latin:code="48"
-                latin:keyIcon="@drawable/sym_bkeyboard_num0" />
-            <key-style
-                latin:styleName="num1KeyStyle"
-                latin:code="49"
-                latin:keyIcon="@drawable/sym_bkeyboard_num1" />
-            <key-style
-                latin:styleName="num2KeyStyle"
-                latin:code="50"
-                latin:keyIcon="@drawable/sym_bkeyboard_num2" />
-            <key-style
-                latin:styleName="num3KeyStyle"
-                latin:code="51"
-                latin:keyIcon="@drawable/sym_bkeyboard_num3" />
-            <key-style
-                latin:styleName="num4KeyStyle"
-                latin:code="52"
-                latin:keyIcon="@drawable/sym_bkeyboard_num4" />
-            <key-style
-                latin:styleName="num5KeyStyle"
-                latin:code="53"
-                latin:keyIcon="@drawable/sym_bkeyboard_num5" />
-            <key-style
-                latin:styleName="num6KeyStyle"
-                latin:code="54"
-                latin:keyIcon="@drawable/sym_bkeyboard_num6" />
-            <key-style
-                latin:styleName="num7KeyStyle"
-                latin:code="55"
-                latin:keyIcon="@drawable/sym_bkeyboard_num7" />
-            <key-style
-                latin:styleName="num8KeyStyle"
-                latin:code="56"
-                latin:keyIcon="@drawable/sym_bkeyboard_num8" />
-            <key-style
-                latin:styleName="num9KeyStyle"
-                latin:code="57"
-                latin:keyIcon="@drawable/sym_bkeyboard_num9" />
-            <key-style
-                latin:styleName="numStarKeyStyle"
-                latin:code="42"
-                latin:keyIcon="@drawable/sym_bkeyboard_numstar" />
-            <key-style
-                latin:styleName="numPoundKeyStyle"
-                latin:code="35"
-                latin:keyIcon="@drawable/sym_bkeyboard_numpound" />
-            <key-style
-                latin:styleName="numAltKeyStyle"
-                latin:code="@integer/key_switch_alpha_symbol"
-                latin:keyIcon="@drawable/sym_bkeyboard_numalt"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_numalt" />
-            <key-style
-                latin:styleName="numSpaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-        </case>
-    </switch>
-</merge>
diff --git a/java/res/xml-sw768dp/kbd_phone.xml b/java/res/xml-sw768dp/kbd_phone.xml
index 8fc5e7d..6801ac5 100644
--- a/java/res/xml-sw768dp/kbd_phone.xml
+++ b/java/res/xml-sw768dp/kbd_phone.xml
@@ -20,14 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
     latin:keyWidth="13.250%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -40,16 +33,14 @@
             latin:keyLabelOption="alignLeft"
             latin:keyWidth="11.172%p"
             latin:keyEdgeFlags="left" />
-        <!-- To match one character label size with "Tab", I placed spaces around the char '-'
-             and '+'. -->
         <Key
-            latin:code="45"
-            latin:keyLabel=" - "
-            latin:keyXPos="20.4%p"
+            latin:keyLabel="-"
+            latin:keyStyle="numLabelStyle"
+            latin:keyXPos="20.400%p"
             latin:keyWidth="8.047%p" />
         <Key
-            latin:code="43"
-            latin:keyLabel=" + "
+            latin:keyLabel="+"
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="8.047%p" />
         <Key
             latin:keyStyle="num1KeyStyle"
@@ -61,7 +52,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-11.172%p"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
@@ -69,16 +60,14 @@
             latin:keyStyle="moreKeyStyle"
             latin:keyWidth="11.172%p"
             latin:keyEdgeFlags="left" />
-        <!-- To match one character label size with "More", I placed spaces around the char ','
-             and '.'. -->
         <Key
-            latin:code="44"
-            latin:keyLabel=" , "
-            latin:keyXPos="20.4%p"
+            latin:keyLabel=","
+            latin:keyStyle="numLabelStyle"
+            latin:keyXPos="20.400%p"
             latin:keyWidth="8.047%p" />
         <Key
-            latin:code="46"
-            latin:keyLabel=" . "
+            latin:keyLabel="."
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="8.047%p" />
         <Key
             latin:keyStyle="num4KeyStyle"
@@ -90,22 +79,20 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-11.172%p"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
-        <!-- To match one character label size with "More", I placed spaces around the char '('
-             and ')'. -->
         <!-- There is an empty area below the "More" key and left of the "(" key.  To ignore
              the touch event on the area, "(" is intentionally not marked as a left edge key. -->
         <Key
-            latin:code="40"
-            latin:keyLabel=" ( "
-            latin:keyXPos="20.4%p"
+            latin:keyLabel="("
+            latin:keyStyle="numLabelStyle"
+            latin:keyXPos="20.400%p"
             latin:keyWidth="8.047%p" />
         <Key
-            latin:code="41"
-            latin:keyLabel=" ) "
+            latin:keyLabel=")"
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="8.047%p" />
         <Key
             latin:keyStyle="num7KeyStyle"
@@ -125,14 +112,10 @@
                     latin:keyStyle="settingsKeyStyle"
                     latin:keyWidth="8.047%p" />
             </case>
-            <default>
-                <Spacer
-                    latin:keyWidth="8.047%p" />
-            </default>
         </switch>
         <Key
             latin:keyStyle="nonSpecialBackgroundSpaceKeyStyle"
-            latin:keyXPos="20.4%p"
+            latin:keyXPos="20.400%p"
             latin:keyWidth="16.084%p" />
         <Key
             latin:keyStyle="numStarKeyStyle"
@@ -140,15 +123,16 @@
         <Key
             latin:keyStyle="num0KeyStyle" />
         <Key
-            latin:keyStyle="numPoundKeyStyle" />
+            latin:keyLabel="#"
+            latin:keyStyle="numLabelStyle" />
         <switch>
             <case
                 latin:voiceKeyEnabled="true"
             >
                 <Key
-                    latin:keyStyle="micKeyStyle"
+                    latin:keyStyle="shortcutKeyStyle"
                     latin:keyXPos="-8.047%p"
-                    latin:keyWidth="0%p" />
+                    latin:keyWidth="fillRight" />
             </case>
         </switch>
     </Row>
diff --git a/java/res/xml-sw768dp/kbd_phone_symbols.xml b/java/res/xml-sw768dp/kbd_phone_symbols.xml
index 449be47..22f78b6 100644
--- a/java/res/xml-sw768dp/kbd_phone_symbols.xml
+++ b/java/res/xml-sw768dp/kbd_phone_symbols.xml
@@ -20,14 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
     latin:keyWidth="13.250%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -41,20 +34,18 @@
             latin:keyWidth="11.172%p"
             latin:keyEdgeFlags="left" />
         <Key
-            latin:code="45"
-            latin:keyLabel=" - "
+            latin:keyLabel="-"
+            latin:keyStyle="numLabelStyle"
             latin:keyXPos="13.829%p"
             latin:keyWidth="8.047%p" />
         <Key
-            latin:code="43"
-            latin:keyLabel=" + "
+            latin:keyLabel="+"
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="8.047%p" />
         <Key
             latin:code="44"
             latin:keyLabel="@string/label_pause_key"
             latin:keyWidth="8.047%p" />
-        <!-- To match one character label size with "Tab" and "Pause, I placed spaces around the
-             char '-' and '+'. -->
         <Key
             latin:keyStyle="num1KeyStyle"
             latin:keyXPos="43.125%p" />
@@ -65,7 +56,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-11.172%p"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
@@ -74,20 +65,18 @@
             latin:keyWidth="11.172%p"
             latin:keyEdgeFlags="left" />
         <Key
-            latin:code="44"
-            latin:keyLabel=" , "
+            latin:keyLabel=","
+            latin:keyStyle="numLabelStyle"
             latin:keyXPos="13.829%p"
             latin:keyWidth="8.047%p" />
         <Key
-            latin:code="46"
-            latin:keyLabel=" . "
+            latin:keyLabel="."
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="8.047%p" />
         <Key
             latin:code="59"
             latin:keyLabel="@string/label_wait_key"
             latin:keyWidth="8.047%p" />
-        <!-- To match one character label size with "More" and "Wait", I placed spaces around the
-             char ',' and '.'. -->
         <Key
             latin:keyStyle="num4KeyStyle"
             latin:keyXPos="43.125%p" />
@@ -98,26 +87,24 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-11.172%p"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
-        <!-- To match one character label size with "More" and etc., I placed spaces around the
-             char 'N', '(' and ')'. -->
         <!-- There is an empty area below the "More" key and left of the "(" key.  To ignore
              the touch event on the area, "(" is intentionally not marked as a left edge key. -->
         <Key
-            latin:code="40"
-            latin:keyLabel=" ( "
+            latin:keyLabel="("
+            latin:keyStyle="numLabelStyle"
             latin:keyXPos="13.829%p"
             latin:keyWidth="8.047%p" />
         <Key
-            latin:code="41"
-            latin:keyLabel=" ) "
+            latin:keyLabel=")"
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="8.047%p" />
         <Key
-            latin:code="78"
-            latin:keyLabel=" N "
+            latin:keyLabel="N"
+            latin:keyStyle="numLabelStyle"
             latin:keyWidth="8.047%p" />
         <Key
             latin:keyStyle="num7KeyStyle"
@@ -137,30 +124,27 @@
                     latin:keyStyle="settingsKeyStyle"
                     latin:keyWidth="8.047%p" />
             </case>
-            <default>
-                <Spacer
-                    latin:keyWidth="8.047%p" />
-            </default>
         </switch>
         <Key
             latin:keyStyle="nonSpecialBackgroundSpaceKeyStyle"
             latin:keyXPos="13.829%p"
-            latin:keyWidth="24.14%p" />
+            latin:keyWidth="24.140%p" />
         <Key
             latin:keyStyle="numStarKeyStyle"
             latin:keyXPos="43.125%p" />
         <Key
             latin:keyStyle="num0KeyStyle" />
         <Key
-            latin:keyStyle="numPoundKeyStyle" />
+            latin:keyLabel="#"
+            latin:keyStyle="numLabelStyle" />
         <switch>
             <case
                 latin:voiceKeyEnabled="true"
             >
                 <Key
-                    latin:keyStyle="micKeyStyle"
+                    latin:keyStyle="shortcutKeyStyle"
                     latin:keyXPos="-8.047%p"
-                    latin:keyWidth="0%p" />
+                    latin:keyWidth="fillRight" />
             </case>
         </switch>
     </Row>
diff --git a/java/res/xml-sw768dp/kbd_qwerty.xml b/java/res/xml-sw768dp/kbd_qwerty.xml
deleted file mode 100644
index 1c8d51f..0000000
--- a/java/res/xml-sw768dp/kbd_qwerty.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<Keyboard
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
->
-    <include
-        latin:keyboardLayout="@xml/kbd_qwerty_rows" />
-</Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_qwerty_row1.xml b/java/res/xml-sw768dp/kbd_qwerty_row1.xml
index 0a79302..3727cf3 100644
--- a/java/res/xml-sw768dp/kbd_qwerty_row1.xml
+++ b/java/res/xml-sw768dp/kbd_qwerty_row1.xml
@@ -63,7 +63,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-9.219%p"
-            latin:keyWidth="-9.219%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
 </merge>
diff --git a/java/res/xml-sw768dp/kbd_qwerty_row2.xml b/java/res/xml-sw768dp/kbd_qwerty_row2.xml
index aeeab23..45af120 100644
--- a/java/res/xml-sw768dp/kbd_qwerty_row2.xml
+++ b/java/res/xml-sw768dp/kbd_qwerty_row2.xml
@@ -56,7 +56,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-15.704%p"
-            latin:keyWidth="-15.704%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
 </merge>
diff --git a/java/res/xml-sw768dp/kbd_qwerty_row3.xml b/java/res/xml-sw768dp/kbd_qwerty_row3.xml
index e488cf3..7297ea5 100644
--- a/java/res/xml-sw768dp/kbd_qwerty_row3.xml
+++ b/java/res/xml-sw768dp/kbd_qwerty_row3.xml
@@ -46,34 +46,12 @@
             latin:popupCharacters="@string/alternates_for_n" />
         <Key
             latin:keyLabel="m" />
-        <switch>
-            <case
-                latin:mode="email"
-            >
-                <Key
-                    latin:keyLabel="," />
-                <Key
-                    latin:keyLabel="." />
-            </case>
-            <default>
-                <Key
-                    latin:keyLabel=","
-                    latin:manualTemporaryUpperCaseCode="33"
-                    latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
-                    latin:popupCharacters="!" />
-                <Key
-                    latin:keyLabel="."
-                    latin:manualTemporaryUpperCaseCode="63"
-                    latin:keyHintIcon="@drawable/key_hint_question_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
-                    latin:popupCharacters="\?" />
-            </default>
-        </switch>
+        <include
+            latin:keyboardLayout="@xml/kbd_row3_right2" />
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyXPos="-13.750%p"
-            latin:keyWidth="-13.750%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
 </merge>
diff --git a/java/res/xml-sw768dp/kbd_qwerty_row4.xml b/java/res/xml-sw768dp/kbd_qwerty_row4.xml
index 44e8e10..00eabab 100644
--- a/java/res/xml-sw768dp/kbd_qwerty_row4.xml
+++ b/java/res/xml-sw768dp/kbd_qwerty_row4.xml
@@ -26,51 +26,45 @@
         latin:keyWidth="8.047%p"
     >
         <switch>
-            <case latin:hasSettingsKey="true">
+            <case
+                latin:hasSettingsKey="true"
+            >
                 <Key
                     latin:keyStyle="settingsKeyStyle"
                     latin:keyWidth="8.047%p" />
             </case>
-            <default>
-                <Spacer
-                    latin:keyWidth="8.047%p" />
-            </default>
         </switch>
+        <Spacer
+            latin:keyXPos="15.157%p"
+            latin:keyWidth="fillRight" />
         <switch>
             <case
-                latin:languageCode="ru"
+                latin:mode="url"
             >
+                <Key
+                    latin:keyStyle="comKeyStyle"
+                    latin:keyWidth="16.084%p" />
+            </case>
+            <default>
                 <switch>
-                    <!-- TODO: implement logical OR for <case> attribute -->
                     <case
                         latin:mode="email"
                     >
                         <Key
-                            latin:keyStyle="comKeyStyle"
-                            latin:keyXPos="15.157%p" />
-                    </case>
-                    <case
-                        latin:mode="url"
-                    >
-                        <Key
-                            latin:keyStyle="comKeyStyle"
-                            latin:keyXPos="15.157%p" />
+                            latin:keyStyle="comKeyStyle" />
                     </case>
                     <case
                         latin:imeAction="actionSearch"
                     >
                         <Key
                             latin:keyLabel=":"
-                            latin:manualTemporaryUpperCaseCode="43"
-                            latin:keyHintIcon="@drawable/key_hint_plus_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_plus_large_holo"
-                            latin:popupCharacters="+"
-                            latin:keyXPos="15.157%p" />
+                            latin:keyLabelOption="hasUppercaseLetter"
+                            latin:keyHintLetter="+"
+                            latin:popupCharacters="+" />
                     </case>
                     <default>
                         <Key
-                            latin:keyStyle="smileyKeyStyle"
-                            latin:keyXPos="15.157%p" />
+                            latin:keyStyle="smileyKeyStyle" />
                     </default>
                 </switch>
                 <switch>
@@ -80,81 +74,14 @@
                         <Key
                             latin:keyLabel="\@" />
                     </case>
-                    <case
-                        latin:mode="url"
-                    >
-                        <Key
-                            latin:keyLabel="-"
-                            latin:manualTemporaryUpperCaseCode="95"
-                            latin:keyHintIcon="@drawable/key_hint_underline_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_underline_large_holo"
-                            latin:popupCharacters="_" />
-                    </case>
                     <default>
                         <Key
                             latin:keyLabel="/"
-                            latin:manualTemporaryUpperCaseCode="64"
-                            latin:keyHintIcon="@drawable/key_hint_at_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_at_large_holo"
+                            latin:keyLabelOption="hasUppercaseLetter"
+                            latin:keyHintLetter="\@"
                             latin:popupCharacters="\@" />
                     </default>
                 </switch>
-            </case>
-            <!-- not languageCode="ru" -->
-            <default>
-                <switch>
-                    <case
-                        latin:mode="url"
-                    >
-                        <Key
-                            latin:keyStyle="comKeyStyle"
-                            latin:keyXPos="15.157%p"
-                            latin:keyWidth="16.084%p" />
-                    </case>
-                    <default>
-                        <switch>
-                            <case
-                                latin:mode="email"
-                            >
-                                <Key
-                                    latin:keyStyle="comKeyStyle"
-                                    latin:keyXPos="15.157%p" />
-                            </case>
-                            <case
-                                latin:imeAction="actionSearch"
-                            >
-                                <Key
-                                    latin:keyLabel=":"
-                                    latin:manualTemporaryUpperCaseCode="43"
-                                    latin:keyHintIcon="@drawable/key_hint_plus_holo"
-                                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_plus_large_holo"
-                                    latin:popupCharacters="+"
-                                    latin:keyXPos="15.157%p" />
-                            </case>
-                            <default>
-                                <Key
-                                    latin:keyStyle="smileyKeyStyle"
-                                    latin:keyXPos="15.157%p" />
-                            </default>
-                        </switch>
-                        <switch>
-                            <case
-                                latin:mode="email"
-                            >
-                                <Key
-                                    latin:keyLabel="\@" />
-                            </case>
-                            <default>
-                                <Key
-                                    latin:keyLabel="/"
-                                    latin:manualTemporaryUpperCaseCode="64"
-                                    latin:keyHintIcon="@drawable/key_hint_at_holo"
-                                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_at_large_holo"
-                                    latin:popupCharacters="\@" />
-                            </default>
-                        </switch>
-                    </default>
-                </switch>
             </default>
         </switch>
         <Key
@@ -163,95 +90,41 @@
             latin:keyWidth="37.500%p" />
         <switch>
             <case
-                latin:languageCode="ru"
+                latin:mode="email"
             >
-                <switch>
-                    <case
-                        latin:mode="email"
-                    >
-                        <Key
-                            latin:keyLabel="-" />
-                    </case>
-                    <case
-                        latin:mode="url"
-                    >
-                        <Key
-                            latin:keyLabel="/"
-                            latin:manualTemporaryUpperCaseCode="58"
-                            latin:keyHintIcon="@drawable/key_hint_colon_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_colon_large_holo"
-                            latin:popupCharacters=":" />
-                    </case>
-                    <default>
-                        <Key
-                            latin:keyLabel="\?"
-                            latin:manualTemporaryUpperCaseCode="95"
-                            latin:keyHintIcon="@drawable/key_hint_underline_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_underline_large_holo"
-                            latin:popupCharacters="_" />
-                    </default>
-                </switch>
-                <switch>
-                    <case
-                        latin:mode="email"
-                    >
-                        <Key
-                            latin:keyLabel="_" />
-                    </case>
-                    <default>
-                        <Key
-                            latin:keyLabel="!"
-                            latin:manualTemporaryUpperCaseCode="34"
-                            latin:keyHintIcon="@drawable/key_hint_quote_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_quote_large_holo"
-                            latin:popupCharacters="\'" />
-                    </default>
-                </switch>
+                <Key
+                    latin:keyLabel="-" />
             </case>
-            <!-- not languageCode="ru" -->
+            <case
+                latin:mode="url"
+            >
+                <Key
+                    latin:keyLabel="/"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter=":"
+                    latin:popupCharacters=":" />
+            </case>
             <default>
-                <switch>
-                    <case
-                        latin:mode="email"
-                    >
-                        <Key
-                            latin:keyLabel="-" />
-                    </case>
-                    <case
-                        latin:mode="url"
-                    >
-                        <Key
-                            latin:keyLabel="/"
-                            latin:manualTemporaryUpperCaseCode="58"
-                            latin:keyHintIcon="@drawable/key_hint_colon_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_colon_large_holo"
-                            latin:popupCharacters=":" />
-                    </case>
-                    <default>
-                        <Key
-                            latin:keyLabel="\'"
-                            latin:manualTemporaryUpperCaseCode="34"
-                            latin:keyHintIcon="@drawable/key_hint_quote_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_quote_large_holo"
-                            latin:popupCharacters="&quot;" />
-                    </default>
-                </switch>
-                <switch>
-                    <case
-                        latin:mode="email"
-                    >
-                        <Key
-                            latin:keyLabel="_" />
-                    </case>
-                    <default>
-                        <Key
-                            latin:keyLabel="-"
-                            latin:manualTemporaryUpperCaseCode="95"
-                            latin:keyHintIcon="@drawable/key_hint_underline_holo"
-                            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_underline_large_holo"
-                            latin:popupCharacters="_" />
-                    </default>
-                </switch>
+                <Key
+                    latin:keyLabel="\'"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="&quot;"
+                    latin:popupCharacters="&quot;" />
+            </default>
+        </switch>
+        <switch>
+            <case
+                latin:mode="email"
+            >
+                <Key
+                    latin:keyLabel="_" />
+            </case>
+            <default>
+                <Key
+                    latin:keyLabel="-"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="_"
+                    latin:popupCharacters="_" />
             </default>
         </switch>
         <switch>
@@ -259,9 +132,9 @@
                 latin:voiceKeyEnabled="true"
             >
                 <Key
-                    latin:keyStyle="micKeyStyle"
+                    latin:keyStyle="shortcutKeyStyle"
                     latin:keyXPos="-8.047%p"
-                    latin:keyWidth="0%p" />
+                    latin:keyWidth="fillRight" />
             </case>
         </switch>
     </Row>
diff --git a/java/res/xml-sw768dp/kbd_row3_right2.xml b/java/res/xml-sw768dp/kbd_row3_right2.xml
new file mode 100644
index 0000000..e90de8e
--- /dev/null
+++ b/java/res/xml-sw768dp/kbd_row3_right2.xml
@@ -0,0 +1,46 @@
+<?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"
+>
+    <switch>
+        <case
+            latin:mode="email"
+        >
+            <Key
+                latin:keyLabel="," />
+            <Key
+                latin:keyLabel="." />
+        </case>
+        <default>
+            <Key
+                latin:keyLabel=","
+                latin:keyLabelOption="hasUppercaseLetter"
+                latin:keyHintLetter="!"
+                latin:popupCharacters="!" />
+            <Key
+                latin:keyLabel="."
+                latin:keyLabelOption="hasUppercaseLetter"
+                latin:keyHintLetter="\?"
+                latin:popupCharacters="\?" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml-sw768dp/kbd_rows_arabic.xml b/java/res/xml-sw768dp/kbd_rows_arabic.xml
new file mode 100644
index 0000000..c181002
--- /dev/null
+++ b/java/res/xml-sw768dp/kbd_rows_arabic.xml
@@ -0,0 +1,140 @@
+<?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.
+*/
+-->
+
+<!-- This file for Arabic layout is an alpha version. It allows to enter   -->
+<!-- some right-to-left text, but it has gone through no study whatsoever, -->
+<!-- and needs to be run through UX.                                       -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_key_styles" />
+    <Row
+        latin:keyWidth="7.375%p"
+    >
+        <Key
+            latin:keyStyle="tabKeyStyle"
+            latin:keyLabelOption="alignLeft"
+            latin:keyWidth="7.500%p"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ض" />
+        <Key
+            latin:keyLabel="ص" />
+        <Key
+            latin:keyLabel="ث" />
+        <Key
+            latin:keyLabel="ق" />
+        <Key
+            latin:keyLabel="ف"
+            latin:popupCharacters="ف,ڤ" />
+        <Key
+            latin:keyLabel="غ" />
+        <Key
+            latin:keyLabel="ع" />
+        <Key
+            latin:keyLabel="ه"
+            latin:popupCharacters="ه,هـ" />
+        <Key
+            latin:keyLabel="خ" />
+        <Key
+            latin:keyLabel="ح" />
+        <Key
+            latin:keyLabel="ج"
+            latin:popupCharacters="ج,چ" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyXPos="-11.500%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="7.375%p"
+    >
+        <Key
+            latin:keyStyle="toSymbolKeyStyle"
+            latin:keyLabelOption="alignLeft"
+            latin:keyWidth="9.375%p"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ش" />
+        <Key
+            latin:keyLabel="س" />
+        <Key
+            latin:keyLabel="ي" />
+        <Key
+            latin:keyLabel="ب"
+            latin:popupCharacters="ب,پ" />
+        <Key
+            latin:keyLabel="ل"
+            latin:popupCharacters="ل,لا" />
+        <Key
+            latin:keyLabel="ا"
+            latin:popupCharacters="ا,أ,إ,آ" />
+        <Key
+            latin:keyLabel="ت" />
+        <Key
+            latin:keyLabel="ن" />
+        <Key
+            latin:keyLabel="م" />
+        <Key
+            latin:keyLabel="ك"
+            latin:popupCharacters="ك,گ" />
+        <Key
+            latin:keyLabel="ط" />
+        <Key
+            latin:keyStyle="returnKeyStyle"
+            latin:keyXPos="-9.375%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="7.375%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="12.750%p"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ئ" />
+        <Key
+            latin:keyLabel="ء" />
+        <Key
+            latin:keyLabel="ؤ" />
+        <Key
+            latin:keyLabel="ر" />
+        <Key
+            latin:keyLabel="ذ" />
+        <Key
+            latin:keyLabel="ى" />
+        <Key
+            latin:keyLabel="ة" />
+        <Key
+            latin:keyLabel="و" />
+        <Key
+            latin:keyLabel="ز"
+            latin:popupCharacters="ز,ژ" />
+        <Key
+            latin:keyLabel="ظ" />
+        <Key
+            latin:keyLabel="د" />
+    </Row>
+    <include latin:keyboardLayout="@xml/kbd_qwerty_row4" />
+</merge>
diff --git a/java/res/xml-sw768dp/kbd_azerty_rows.xml b/java/res/xml-sw768dp/kbd_rows_azerty.xml
similarity index 86%
rename from java/res/xml-sw768dp/kbd_azerty_rows.xml
rename to java/res/xml-sw768dp/kbd_rows_azerty.xml
index f6ebfe6..3bcba00 100644
--- a/java/res/xml-sw768dp/kbd_azerty_rows.xml
+++ b/java/res/xml-sw768dp/kbd_rows_azerty.xml
@@ -64,7 +64,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-9.219%p"
-            latin:keyWidth="-9.219%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -105,7 +105,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-15.704%p"
-            latin:keyWidth="-15.704%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -133,9 +133,8 @@
             latin:popupCharacters="@string/alternates_for_n" />
         <Key
             latin:keyLabel="\'"
-            latin:manualTemporaryUpperCaseCode="58"
-            latin:keyHintIcon="@drawable/key_hint_colon_holo"
-            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_colon_large_holo"
+            latin:keyLabelOption="hasUppercaseLetter"
+            latin:keyHintLetter=":"
             latin:popupCharacters=":" />
         <switch>
             <case
@@ -149,22 +148,20 @@
             <default>
                 <Key
                     latin:keyLabel=","
-                    latin:manualTemporaryUpperCaseCode="33"
-                    latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="!"
                     latin:popupCharacters="!" />
                 <Key
                     latin:keyLabel="."
-                    latin:manualTemporaryUpperCaseCode="63"
-                    latin:keyHintIcon="@drawable/key_hint_question_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="\?"
                     latin:popupCharacters="\?" />
             </default>
         </switch>
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyXPos="-13.750%p"
-            latin:keyWidth="-13.750%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <include
diff --git a/java/res/xml-sw768dp/kbd_rows_hebrew.xml b/java/res/xml-sw768dp/kbd_rows_hebrew.xml
new file mode 100644
index 0000000..5275a1f
--- /dev/null
+++ b/java/res/xml-sw768dp/kbd_rows_hebrew.xml
@@ -0,0 +1,127 @@
+<?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" />
+    <Row
+        latin:keyWidth="8.000%p"
+    >
+        <Key
+            latin:keyStyle="tabKeyStyle"
+            latin:keyLabelOption="alignLeft"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="," />
+        <Key
+            latin:keyLabel="." />
+        <Key
+            latin:keyLabel="ק" />
+        <Key
+            latin:keyLabel="ר" />
+        <Key
+            latin:keyLabel="א" />
+        <Key
+            latin:keyLabel="ט" />
+        <Key
+            latin:keyLabel="ו" />
+        <Key
+            latin:keyLabel="ן" />
+        <Key
+            latin:keyLabel="ם" />
+        <Key
+            latin:keyLabel="פ" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyXPos="-12.000%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="8.000%p"
+    >
+        <Key
+            latin:keyStyle="toSymbolKeyStyle"
+            latin:keyLabelOption="alignLeft"
+            latin:keyWidth="9.600%p"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ש" />
+        <Key
+            latin:keyLabel="ד" />
+        <Key
+            latin:keyLabel="ג"
+            latin:popupCharacters="ג,ג׳" />
+        <Key
+            latin:keyLabel="כ" />
+        <Key
+            latin:keyLabel="ע" />
+        <Key
+            latin:keyLabel="י"
+            latin:popupCharacters="י,ײַ" />
+        <Key
+            latin:keyLabel="ח"
+            latin:popupCharacters="ח,ח׳" />
+        <Key
+            latin:keyLabel="ל" />
+        <Key
+            latin:keyLabel="ך" />
+        <Key
+            latin:keyLabel="ף" />
+        <Key
+            latin:keyStyle="returnKeyStyle"
+            latin:keyXPos="-10.400%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="8.000%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="13.200%p"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ז"
+            latin:popupCharacters="ז,ז׳" />
+        <Key
+            latin:keyLabel="ס" />
+        <Key
+            latin:keyLabel="ב" />
+        <Key
+            latin:keyLabel="ה" />
+        <Key
+            latin:keyLabel="נ" />
+        <Key
+            latin:keyLabel="מ" />
+        <Key
+            latin:keyLabel="צ"
+            latin:popupCharacters="צ,צ׳" />
+        <Key
+            latin:keyLabel="ת"
+            latin:popupCharacters="ת,ת׳" />
+        <Key
+            latin:keyLabel="ץ"
+            latin:popupCharacters="ץ,ץ׳" />
+    </Row>
+    <include latin:keyboardLayout="@xml/kbd_qwerty_row4" />
+</merge>
diff --git a/java/res/xml-sw768dp/kbd_qwerty_rows.xml b/java/res/xml-sw768dp/kbd_rows_qwerty.xml
similarity index 100%
rename from java/res/xml-sw768dp/kbd_qwerty_rows.xml
rename to java/res/xml-sw768dp/kbd_rows_qwerty.xml
diff --git a/java/res/xml-sw768dp/kbd_qwertz_rows.xml b/java/res/xml-sw768dp/kbd_rows_qwertz.xml
similarity index 87%
rename from java/res/xml-sw768dp/kbd_qwertz_rows.xml
rename to java/res/xml-sw768dp/kbd_rows_qwertz.xml
index 9a8b661..8d5f1e6 100644
--- a/java/res/xml-sw768dp/kbd_qwertz_rows.xml
+++ b/java/res/xml-sw768dp/kbd_rows_qwertz.xml
@@ -64,7 +64,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-9.219%p"
-            latin:keyWidth="-9.219%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <include
@@ -106,22 +106,20 @@
             <default>
                 <Key
                     latin:keyLabel=","
-                    latin:manualTemporaryUpperCaseCode="33"
-                    latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="!"
                     latin:popupCharacters="!" />
                 <Key
                     latin:keyLabel="."
-                    latin:manualTemporaryUpperCaseCode="63"
-                    latin:keyHintIcon="@drawable/key_hint_question_holo"
-                    latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
+                    latin:keyLabelOption="hasUppercaseLetter"
+                    latin:keyHintLetter="\?"
                     latin:popupCharacters="\?" />
             </default>
         </switch>
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyXPos="-13.750%p"
-            latin:keyWidth="-13.750%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
    <include
diff --git a/java/res/xml-sw768dp/kbd_ru_rows.xml b/java/res/xml-sw768dp/kbd_rows_russian.xml
similarity index 79%
rename from java/res/xml-sw768dp/kbd_ru_rows.xml
rename to java/res/xml-sw768dp/kbd_rows_russian.xml
index 22e7d56..ee91481 100644
--- a/java/res/xml-sw768dp/kbd_ru_rows.xml
+++ b/java/res/xml-sw768dp/kbd_rows_russian.xml
@@ -25,12 +25,11 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <!-- This row is intentionally not marked as a top row -->
     <Row
-        latin:keyWidth="7.579%p"
+        latin:keyWidth="7.125%p"
     >
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.969%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="й" />
@@ -56,18 +55,19 @@
         <Key
             latin:keyLabel="х" />
         <Key
+            latin:keyLabel="ъ" />
+        <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyXPos="-9.219%p"
-            latin:keyWidth="-9.219%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
-        latin:keyWidth="7.500%p"
+        latin:keyWidth="7.125%p"
     >
         <Key
             latin:keyStyle="toSymbolKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="9.219%p"
+            latin:keyWidth="9.375%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="ф" />
@@ -93,16 +93,16 @@
             latin:keyLabel="э" />
         <Key
             latin:keyStyle="returnKeyStyle"
-            latin:keyXPos="-15.704%p"
-            latin:keyWidth="-15.704%p"
+            latin:keyXPos="-9.375%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
-        latin:keyWidth="7.500%p"
+        latin:keyWidth="7.125%p"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="12.400%p"
+            latin:keyWidth="12.750%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="я" />
@@ -117,22 +117,16 @@
         <Key
             latin:keyLabel="т" />
         <Key
-            latin:keyLabel="ь"
-            latin:popupCharacters="@string/alternates_for_cyrillic_soft_sign" />
+            latin:keyLabel="ь" />
         <Key
             latin:keyLabel="б" />
         <Key
             latin:keyLabel="ю" />
-        <Key
-            latin:keyLabel="."
-            latin:manualTemporaryUpperCaseCode="44"
-            latin:keyHintIcon="@drawable/key_hint_comma_holo"
-            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_comma_large_holo"
-            latin:popupCharacters="," />
+        <include
+            latin:keyboardLayout="@xml/kbd_row3_right2" />
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyXPos="-13.750%p"
-            latin:keyWidth="-13.750%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <include
diff --git a/java/res/xml-sw768dp/kbd_qwerty_rows_scandinavia.xml b/java/res/xml-sw768dp/kbd_rows_scandinavian.xml
similarity index 73%
rename from java/res/xml-sw768dp/kbd_qwerty_rows_scandinavia.xml
rename to java/res/xml-sw768dp/kbd_rows_scandinavian.xml
index 363de4b..7e9f604 100644
--- a/java/res/xml-sw768dp/kbd_qwerty_rows_scandinavia.xml
+++ b/java/res/xml-sw768dp/kbd_rows_scandinavian.xml
@@ -24,12 +24,12 @@
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
-        latin:keyWidth="7.579%p"
+        latin:keyWidth="7.375%p"
     >
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.969%p"
+            latin:keyWidth="7.500%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="q"
@@ -65,17 +65,17 @@
             latin:keyLabel="å" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyXPos="-9.219%p"
-            latin:keyWidth="-9.219%p"
+            latin:keyXPos="-11.500%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
-        latin:keyWidth="7.500%p"
+        latin:keyWidth="7.375%p"
     >
         <Key
             latin:keyStyle="toSymbolKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.969%p"
+            latin:keyWidth="9.375%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="a"
@@ -109,13 +109,43 @@
             latin:popupCharacters="@string/alternates_for_scandinavia_row2_11" />
         <Key
             latin:keyStyle="returnKeyStyle"
-            latin:keyXPos="-15.704%p"
-            latin:keyWidth="-15.704%p"
+            latin:keyXPos="-9.375%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
-
     </Row>
-    <include
-        latin:keyboardLayout="@xml/kbd_qwerty_row3" />
+    <Row
+        latin:keyWidth="7.375%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="12.750%p"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="z"
+            latin:popupCharacters="@string/alternates_for_z" />
+        <Key
+            latin:keyLabel="x" />
+        <Key
+            latin:keyLabel="c"
+            latin:popupCharacters="@string/alternates_for_c" />
+        <Key
+            latin:keyLabel="v"
+            latin:popupCharacters="@string/alternates_for_v" />
+        <Key
+            latin:keyLabel="b" />
+        <Key
+            latin:keyLabel="n"
+            latin:popupCharacters="@string/alternates_for_n" />
+        <Key
+            latin:keyLabel="m" />
+        <include
+            latin:keyboardLayout="@xml/kbd_row3_right2" />
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyXPos="-12.750%p"
+            latin:keyWidth="fillRight"
+            latin:keyEdgeFlags="right" />
+    </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
 </merge>
diff --git a/java/res/xml-sw768dp/kbd_ru_rows.xml b/java/res/xml-sw768dp/kbd_rows_serbian.xml
similarity index 71%
copy from java/res/xml-sw768dp/kbd_ru_rows.xml
copy to java/res/xml-sw768dp/kbd_rows_serbian.xml
index 22e7d56..4997dab 100644
--- a/java/res/xml-sw768dp/kbd_ru_rows.xml
+++ b/java/res/xml-sw768dp/kbd_rows_serbian.xml
@@ -25,114 +25,104 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <!-- This row is intentionally not marked as a top row -->
     <Row
-        latin:keyWidth="7.579%p"
+        latin:keyWidth="7.125%p"
     >
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.969%p"
             latin:keyEdgeFlags="left" />
         <Key
-            latin:keyLabel="й" />
+            latin:keyLabel="љ" />
         <Key
-            latin:keyLabel="ц" />
+            latin:keyLabel="њ" />
         <Key
-            latin:keyLabel="у" />
+            latin:keyLabel="е" />
         <Key
-            latin:keyLabel="к" />
+            latin:keyLabel="р" />
         <Key
-            latin:keyLabel="е"
-            latin:popupCharacters="@string/alternates_for_cyrillic_e" />
-        <Key
-            latin:keyLabel="н" />
-        <Key
-            latin:keyLabel="г" />
-        <Key
-            latin:keyLabel="ш" />
-        <Key
-            latin:keyLabel="щ" />
+            latin:keyLabel="т" />
         <Key
             latin:keyLabel="з" />
         <Key
-            latin:keyLabel="х" />
+            latin:keyLabel="у" />
         <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyXPos="-9.219%p"
-            latin:keyWidth="-9.219%p"
-            latin:keyEdgeFlags="right" />
+            latin:keyLabel="и" />
+        <Key
+            latin:keyLabel="о" />
+        <Key
+            latin:keyLabel="п" />
+        <Key
+            latin:keyLabel="ш" />
+        <Key
+            latin:keyLabel="ђ" />
+        <Key
+            latin:keyLabel="ж"
+            latin:keyWidth="fillRight"
+            latin:keyEdgeFlags="right"/>
     </Row>
     <Row
-        latin:keyWidth="7.500%p"
+        latin:keyWidth="7.250%p"
     >
         <Key
             latin:keyStyle="toSymbolKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="9.219%p"
+            latin:keyWidth="11.172%p"
             latin:keyEdgeFlags="left" />
         <Key
-            latin:keyLabel="ф" />
-        <Key
-            latin:keyLabel="ы" />
-        <Key
-            latin:keyLabel="в" />
-        <Key
             latin:keyLabel="а" />
         <Key
-            latin:keyLabel="п" />
-        <Key
-            latin:keyLabel="р" />
-        <Key
-            latin:keyLabel="о" />
-        <Key
-            latin:keyLabel="л" />
+            latin:keyLabel="с" />
         <Key
             latin:keyLabel="д" />
         <Key
-            latin:keyLabel="ж" />
+            latin:keyLabel="ф" />
         <Key
-            latin:keyLabel="э" />
+            latin:keyLabel="г" />
         <Key
-            latin:keyStyle="returnKeyStyle"
-            latin:keyXPos="-15.704%p"
-            latin:keyWidth="-15.704%p"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row
-        latin:keyWidth="7.500%p"
-    >
+            latin:keyLabel="х" />
         <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="12.400%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyLabel="ј" />
         <Key
-            latin:keyLabel="я" />
+            latin:keyLabel="к" />
+        <Key
+            latin:keyLabel="л" />
         <Key
             latin:keyLabel="ч" />
         <Key
-            latin:keyLabel="с" />
+            latin:keyLabel="ћ" />
         <Key
-            latin:keyLabel="м" />
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyXPos="-9.219%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="7.250%p"
+    >
         <Key
-            latin:keyLabel="и" />
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="13.829%p"
+            latin:keyEdgeFlags="left" />
         <Key
-            latin:keyLabel="т" />
+            latin:keyLabel="ѕ" />
         <Key
-            latin:keyLabel="ь"
-            latin:popupCharacters="@string/alternates_for_cyrillic_soft_sign" />
+            latin:keyLabel="џ" />
+        <Key
+            latin:keyLabel="ц" />
+        <Key
+            latin:keyLabel="в" />
         <Key
             latin:keyLabel="б" />
         <Key
-            latin:keyLabel="ю" />
+            latin:keyLabel="н" />
         <Key
-            latin:keyLabel="."
-            latin:manualTemporaryUpperCaseCode="44"
-            latin:keyHintIcon="@drawable/key_hint_comma_holo"
-            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_comma_large_holo"
-            latin:popupCharacters="," />
+            latin:keyLabel="м" />
+        <include
+            latin:keyboardLayout="@xml/kbd_row3_right2" />
         <Key
-            latin:keyStyle="shiftKeyStyle"
+            latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-13.750%p"
-            latin:keyWidth="-13.750%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <include
diff --git a/java/res/xml-sw768dp/kbd_rows_spanish.xml b/java/res/xml-sw768dp/kbd_rows_spanish.xml
new file mode 100644
index 0000000..300592e
--- /dev/null
+++ b/java/res/xml-sw768dp/kbd_rows_spanish.xml
@@ -0,0 +1,72 @@
+<?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_qwerty_row1" />
+    <Row
+        latin:keyWidth="8.125%p"
+    >
+        <Key
+            latin:keyStyle="toSymbolKeyStyle"
+            latin:keyLabelOption="alignLeft"
+            latin:keyWidth="10.167%p"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="a"
+            latin:popupCharacters="@string/alternates_for_a" />
+        <Key
+            latin:keyLabel="s"
+            latin:popupCharacters="@string/alternates_for_s" />
+        <Key
+            latin:keyLabel="d"
+            latin:popupCharacters="@string/alternates_for_d" />
+        <Key
+            latin:keyLabel="f" />
+        <Key
+            latin:keyLabel="g"
+            latin:popupCharacters="@string/alternates_for_g" />
+        <Key
+            latin:keyLabel="h" />
+        <Key
+            latin:keyLabel="j" />
+        <Key
+            latin:keyLabel="k"
+            latin:popupCharacters="@string/alternates_for_k" />
+        <Key
+            latin:keyLabel="l"
+            latin:popupCharacters="@string/alternates_for_l" />
+        <Key
+            latin:keyLabel="ñ" />
+        <Key
+            latin:keyStyle="returnKeyStyle"
+            latin:keyXPos="-15.704%p"
+            latin:keyWidth="fillBoth"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/kbd_qwerty_row3" />
+    <include
+        latin:keyboardLayout="@xml/kbd_qwerty_row4" />
+</merge>
diff --git a/java/res/xml-sw768dp/kbd_sr_rows.xml b/java/res/xml-sw768dp/kbd_sr_rows.xml
deleted file mode 100644
index 693658d..0000000
--- a/java/res/xml-sw768dp/kbd_sr_rows.xml
+++ /dev/null
@@ -1,154 +0,0 @@
-<?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" />
-    <!-- This row is intentionally not marked as a top row -->
-    <Row
-        latin:keyWidth="7.579%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.969%p"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="љ"
-            latin:popupCharacters="1" />
-        <Key
-            latin:keyLabel="њ"
-            latin:popupCharacters="2" />
-        <Key
-            latin:keyLabel="е"
-            latin:popupCharacters="3" />
-        <Key
-            latin:keyLabel="р"
-            latin:popupCharacters="4" />
-        <Key
-            latin:keyLabel="т"
-            latin:popupCharacters="5" />
-        <Key
-            latin:keyLabel="з"
-            latin:popupCharacters="6" />
-        <Key
-            latin:keyLabel="у"
-            latin:popupCharacters="7" />
-        <Key
-            latin:keyLabel="и"
-            latin:popupCharacters="8" />
-        <Key
-            latin:keyLabel="о"
-            latin:popupCharacters="9" />
-        <Key
-            latin:keyLabel="п"
-            latin:popupCharacters="0" />
-        <Key
-            latin:keyLabel="ш" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyXPos="-9.219%p"
-            latin:keyWidth="-9.219%p"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row
-        latin:keyWidth="7.500%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelOption="alignLeft"
-            latin:keyWidth="9.219%p"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="а" />
-        <Key
-            latin:keyLabel="с" />
-        <Key
-            latin:keyLabel="д" />
-        <Key
-            latin:keyLabel="ф" />
-        <Key
-            latin:keyLabel="г" />
-        <Key
-            latin:keyLabel="х" />
-        <Key
-            latin:keyLabel="ј" />
-        <Key
-            latin:keyLabel="к" />
-        <Key
-            latin:keyLabel="л" />
-        <Key
-            latin:keyLabel="ч" />
-        <Key
-            latin:keyLabel="ћ" />
-        <Key
-            latin:keyStyle="returnKeyStyle"
-            latin:keyXPos="-15.704%p"
-            latin:keyWidth="-15.704%p"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row
-        latin:keyWidth="7.500%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="9.219%p"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="ѕ" />
-        <Key
-            latin:keyLabel="џ" />
-        <Key
-            latin:keyLabel="ц" />
-        <Key
-            latin:keyLabel="в" />
-        <Key
-            latin:keyLabel="б" />
-        <Key
-            latin:keyLabel="н" />
-        <Key
-            latin:keyLabel="м" />
-        <Key
-            latin:keyLabel="ђ" />
-        <Key
-            latin:keyLabel="ж" />
-        <Key
-            latin:keyLabel=","
-            latin:manualTemporaryUpperCaseCode="33"
-            latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
-            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
-            latin:popupCharacters="!" />
-        <Key
-            latin:keyLabel="."
-            latin:manualTemporaryUpperCaseCode="63"
-            latin:keyHintIcon="@drawable/key_hint_question_holo"
-            latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
-            latin:popupCharacters="\?" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyXPos="-13.750%p"
-            latin:keyWidth="-13.750%p"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/kbd_qwerty_row4" />
-</merge>
diff --git a/java/res/xml-sw768dp/kbd_symbols.xml b/java/res/xml-sw768dp/kbd_symbols.xml
index 15c75ca..0e6103a 100644
--- a/java/res/xml-sw768dp/kbd_symbols.xml
+++ b/java/res/xml-sw768dp/kbd_symbols.xml
@@ -20,13 +20,6 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -71,7 +64,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-9.219%p"
-            latin:keyWidth="-9.219%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -109,7 +102,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-15.704%p"
-            latin:keyWidth="-15.704%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -130,12 +123,6 @@
             latin:popupCharacters="≠,≈" />
         <switch>
             <case
-                latin:languageCode="ru"
-            >
-                <Key
-                    latin:keyLabel=":" />
-            </case>
-            <case
                 latin:mode="url"
             >
                 <Key
@@ -149,40 +136,20 @@
         </switch>
         <Key
             latin:keyLabel=";" />
-        <switch>
-            <case
-                latin:languageCode="ru"
-            >
-                <Key
-                    latin:keyLabel="\'"
-                    latin:popupCharacters="‘,’,‚,‛" />
-                <!-- Note: DroidSans doesn't have double-high-reversed-quotation '\u201f' glyph. -->
-                <!-- latin:popupCharacters="“,”,„,‟,«,»" -->
-                <Key
-                    latin:keyLabel="&quot;"
-                    latin:popupCharacters="“,”,«,»" />
-                <Key
-                    latin:keyLabel="." />
-                <Key
-                    latin:keyLabel="," />
-            </case>
-            <default>
-                <Key
-                    latin:keyLabel="," />
-                <Key
-                    latin:keyLabel="." />
-                <Key
-                    latin:keyLabel="!"
-                    latin:popupCharacters="¡" />
-                <Key
-                    latin:keyLabel="\?"
-                    latin:popupCharacters="¿" />
-            </default>
-        </switch>
+        <Key
+            latin:keyLabel="," />
+        <Key
+            latin:keyLabel="." />
+        <Key
+            latin:keyLabel="!"
+            latin:popupCharacters="¡" />
+        <Key
+            latin:keyLabel="\?"
+            latin:popupCharacters="¿" />
         <Key
             latin:keyStyle="moreKeyStyle"
             latin:keyXPos="-13.750%p"
-            latin:keyWidth="-13.750%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <!-- This row is intentionally not marked as a bottom row -->
@@ -190,15 +157,13 @@
         latin:keyWidth="8.047%p"
     >
         <switch>
-            <case latin:hasSettingsKey="true">
+            <case
+                latin:hasSettingsKey="true"
+            >
                 <Key
                     latin:keyStyle="settingsKeyStyle"
                     latin:keyWidth="8.047%p" />
             </case>
-            <default>
-                <Spacer
-                    latin:keyWidth="8.047%p" />
-            </default>
         </switch>
         <Key
             latin:keyLabel="/"
@@ -209,33 +174,21 @@
             latin:keyStyle="spaceKeyStyle"
             latin:keyXPos="31.250%p"
             latin:keyWidth="37.500%p" />
-        <switch>
-            <case
-                latin:languageCode="ru"
-            >
-                <Key
-                    latin:keyLabel="_" />
-                <Key
-                    latin:keyLabel="-" />
-            </case>
-           <default>
-                <!-- Note: DroidSans doesn't have double-high-reversed-quotation '\u201f' glyph. -->
-                <!-- latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛" -->
-                <Key
-                    latin:keyLabel="&quot;"
-                    latin:popupCharacters="“,”,«,»,‘,’,‚,‛" />
-                <Key
-                    latin:keyLabel="_" />
-            </default>
-        </switch>
+        <!-- Note: DroidSans doesn't have double-high-reversed-quotation '\u201f' glyph. -->
+        <!-- latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛" -->
+        <Key
+            latin:keyLabel="&quot;"
+            latin:popupCharacters="“,”,«,»,‘,’,‚,‛" />
+        <Key
+            latin:keyLabel="_" />
         <switch>
             <case
                 latin:voiceKeyEnabled="true"
             >
                 <Key
-                    latin:keyStyle="micKeyStyle"
+                    latin:keyStyle="shortcutKeyStyle"
                     latin:keyXPos="-8.047%p"
-                    latin:keyWidth="0%p" />
+                    latin:keyWidth="fillRight" />
             </case>
         </switch>
     </Row>
diff --git a/java/res/xml-sw768dp/kbd_symbols_shift.xml b/java/res/xml-sw768dp/kbd_symbols_shift.xml
index 44340ab..b368141 100644
--- a/java/res/xml-sw768dp/kbd_symbols_shift.xml
+++ b/java/res/xml-sw768dp/kbd_symbols_shift.xml
@@ -20,13 +20,6 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -72,7 +65,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-9.219%p"
-            latin:keyWidth="-9.219%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -113,7 +106,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-15.704%p"
-            latin:keyWidth="-15.704%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -150,7 +143,7 @@
         <Key
             latin:keyStyle="moreKeyStyle"
             latin:keyXPos="-13.750%p"
-            latin:keyWidth="-13.750%p"
+            latin:keyWidth="fillBoth"
             latin:keyEdgeFlags="right" />
     </Row>
     <!-- This row is intentionally not marked as a bottom row -->
@@ -163,10 +156,6 @@
                     latin:keyStyle="settingsKeyStyle"
                     latin:keyWidth="8.047%p" />
             </case>
-            <default>
-                <Spacer
-                    latin:keyWidth="8.047%p" />
-            </default>
         </switch>
         <Key
             latin:keyStyle="spaceKeyStyle"
@@ -177,9 +166,9 @@
                 latin:voiceKeyEnabled="true"
             >
                 <Key
-                    latin:keyStyle="micKeyStyle"
+                    latin:keyStyle="shortcutKeyStyle"
                     latin:keyXPos="-8.047%p"
-                    latin:keyWidth="0%p" />
+                    latin:keyWidth="fillRight" />
             </case>
         </switch>
     </Row>
diff --git a/java/res/drawable/btn_candidate_holo.xml b/java/res/xml-tr/kbd_qwerty.xml
similarity index 69%
copy from java/res/drawable/btn_candidate_holo.xml
copy to java/res/xml-tr/kbd_qwerty.xml
index 66cd246..d2c38f6 100644
--- a/java/res/drawable/btn_candidate_holo.xml
+++ b/java/res/xml-tr/kbd_qwerty.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** 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.
@@ -18,10 +18,10 @@
 */
 -->
 
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyboardLocale="tr"
 >
-    <item
-        android:state_pressed="true"
-        android:drawable="@drawable/btn_keyboard_key_popup_selected_holo" />
-</selector>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_qwerty" />
+</Keyboard>
diff --git a/java/res/xml/kbd_currency_key_styles.xml b/java/res/xml/kbd_currency_key_styles.xml
index b30dd64..9d3bb47 100644
--- a/java/res/xml/kbd_currency_key_styles.xml
+++ b/java/res/xml/kbd_currency_key_styles.xml
@@ -27,229 +27,59 @@
         >
             <key-style
                 latin:styleName="currencyKeyStyle"
-                latin:keyLabel="$"
-                latin:popupCharacters="@string/alternates_for_currency_dollar" />
+                latin:keyLabel="$" />
         </case>
         <!-- Countries using Euro currency, 23 countries as for January 2011. -->
-        <!-- 1. Andorra (ca_AD, ca_ES) -->
+              1. Andorra (ca_AD, ca_ES)
+              2. Austria (de_AT)
+              3. Belgium (nl_BE, fr_BE, de_BE)
+              4. Cyprus (el_CY, tr_CY)
+              5. Estonia (et_EE)
+              6. Finland (fi_FI, sv_FI)
+              7. France (fr_FR)
+              8. Germany (de_DE)
+              9. Greece (el_GR)
+             10. Ireland (ga_IE, en_IE)
+             11. Italy (it_IT)
+             12. Kosovo (??_XK) ??
+             13. Luxembourg (lb_LU, fr_LU, de_LU)
+             14. Malta (mt_MT, en_MT)
+             15. Monaco (fr_MO)
+             16. Montenegro (sla_ME)
+             17. Netherlands (nl_NL)
+             18. Portugal (pt_PT)
+             19. San Marino (it_SM)
+             20. Slovakia (sk_SK)
+             21. Slovenia (sl_SI)
+             22. Spain (es_ES, ca_ES)
+             23. Vatican City (it_VA)
+        -->
+        <!-- Note: Some locales may not have country code, and it it supposed to indicate the
+             country where the language originally/mainly spoken. -->
         <case
-            latin:languageCode="ca"
-            latin:countryCode=""
+            latin:localeCode="de|es|el|fi|fr|it|nl|sk|sl|pt_PT"
         >
             <key-style
                 latin:styleName="currencyKeyStyle"
                 latin:keyLabel="€"
                 latin:popupCharacters="@string/alternates_for_currency_euro" />
         </case>
-        <!-- 2. Austria (de_AT) -->
-<!--        <case-->
-<!--            latin:countryCode="AT"-->
-<!--        >-->
-<!--            <key-style-->
-<!--                latin:styleName="currencyKeyStyle"-->
-<!--                latin:keyLabel="€"-->
-<!--                latin:popupCharacters="@string/alternates_for_currency_euro" />-->
-<!--        </case>-->
-        <!-- 3. Belgium (nl_BE, fr_BE, de_BE) -->
-<!--        <case-->
-<!--            latin:countryCode="BE"-->
-<!--        >-->
-<!--            <key-style-->
-<!--                latin:styleName="currencyKeyStyle"-->
-<!--                latin:keyLabel="€"-->
-<!--                latin:popupCharacters="@string/alternates_for_currency_euro" />-->
-<!--        </case>-->
-        <!-- 4. Cyprus (el_CY, tr_CY) -->
         <case
-            latin:countryCode="CY"
+            latin:languageCode="ca|et|lb|mt|sla"
         >
             <key-style
                 latin:styleName="currencyKeyStyle"
                 latin:keyLabel="€"
                 latin:popupCharacters="@string/alternates_for_currency_euro" />
         </case>
-        <!-- 5. Estonia (et_EE) -->
-<!--        <case-->
-<!--            latin:languageCode="et"-->
-<!--            latin:countryCode=""-->
-<!--        >-->
-<!--            <key-style-->
-<!--                latin:styleName="currencyKeyStyle"-->
-<!--                latin:keyLabel="€"-->
-<!--                latin:popupCharacters="@string/alternates_for_currency_euro" />-->
-<!--        </case>-->
-        <!-- 6. Finland (fi_FI, sv_FI) -->
         <case
-            latin:languageCode="fi"
-            latin:countryCode=""
+            latin:countryCode="AD|AT|BE|CY|EE|FI|FR|DE|GR|IE|IT|XK|LU|MT|MO|ME|NL|PT|SM|SK|SI|ES|VA"
         >
             <key-style
                 latin:styleName="currencyKeyStyle"
                 latin:keyLabel="€"
                 latin:popupCharacters="@string/alternates_for_currency_euro" />
         </case>
-        <!-- 7. France (fr_FR) -->
-        <case
-            latin:languageCode="fr"
-            latin:countryCode=""
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 8. Germany (de_DE) -->
-        <case
-            latin:languageCode="de"
-            latin:countryCode=""
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 9. Greece (el_GR) -->
-        <case
-            latin:languageCode="el"
-            latin:countryCode=""
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 10. Ireland (ga_IE, en_IE) -->
-        <case
-            latin:countryCode="IE"
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 11. Italy (it_IT) -->
-        <case
-            latin:languageCode="it"
-            latin:countryCode=""
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 12. Kosovo -->
-<!--        <case-->
-<!--            latin:countryCode="XK"-->
-<!--        >-->
-<!--            <key-style-->
-<!--                latin:styleName="currencyKeyStyle"-->
-<!--                latin:keyLabel="€"-->
-<!--                latin:popupCharacters="@string/alternates_for_currency_euro" />-->
-<!--        </case>-->
-        <!-- 13. Luxembourg (lb_LU, fr_LU, de_LU) -->
-        <case
-            latin:countryCode="LU"
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 14. Malta (mt_MT, en_MT) -->
-        <case
-            latin:countryCode="MT"
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 15. Monaco (fr_MO) -->
-<!--        <case-->
-<!--            latin:countryCode="MO"-->
-<!--        >-->
-<!--            <key-style-->
-<!--                latin:styleName="currencyKeyStyle"-->
-<!--                latin:keyLabel="€"-->
-<!--                latin:popupCharacters="@string/alternates_for_currency_euro" />-->
-<!--        </case>-->
-        <!-- 16. Montenegro (sla_ME) -->
-        <case
-            latin:countryCode="ME"
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 17. Netherlands (nl_NL) -->
-        <case
-            latin:languageCode="nl"
-            latin:countryCode=""
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 18. Portugal (pt_PT) -->
-        <case
-            latin:languageCode="pt"
-            latin:countryCode=""
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 19. San Marino (it_SM) -->
-<!--        <case-->
-<!--            latin:countryCode="SM"-->
-<!--        >-->
-<!--            <key-style-->
-<!--                latin:styleName="currencyKeyStyle"-->
-<!--                latin:keyLabel="€"-->
-<!--                latin:popupCharacters="@string/alternates_for_currency_euro" />-->
-<!--        </case>-->
-        <!-- 20. Slovakia (sk_SK) -->
-        <case
-            latin:languageCode="sk"
-            latin:countryCode=""
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 21. Slovenia (sl_SI) -->
-        <case
-            latin:languageCode="sl"
-            latin:countryCode=""
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 22. Spain (es_ES, ca_ES) -->
-        <case
-            latin:languageCode="es"
-            latin:countryCode=""
-        >
-            <key-style
-                latin:styleName="currencyKeyStyle"
-                latin:keyLabel="€"
-                latin:popupCharacters="@string/alternates_for_currency_euro" />
-        </case>
-        <!-- 23. Vatican City (it_VA) -->
-<!--        <case-->
-<!--            latin:countryCode="VA"-->
-<!--        >-->
-<!--            <key-style-->
-<!--                latin:styleName="currencyKeyStyle"-->
-<!--                latin:keyLabel="€"-->
-<!--                latin:popupCharacters="@string/alternates_for_currency_euro" />-->
-<!--        </case>-->
         <!-- United Kingdom -->
         <case
             latin:countryCode="GB"
@@ -266,4 +96,4 @@
                 latin:popupCharacters="@string/alternates_for_currency_dollar" />
         </default>
     </switch>
-</merge>
\ No newline at end of file
+</merge>
diff --git a/java/res/xml/kbd_key_styles.xml b/java/res/xml/kbd_key_styles.xml
index 28869a8..e7d8d31 100644
--- a/java/res/xml/kbd_key_styles.xml
+++ b/java/res/xml/kbd_key_styles.xml
@@ -22,25 +22,13 @@
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <!-- Base key style for the functional key -->
-    <switch>
-       <case
-            latin:colorScheme="white"
-        >
-            <key-style
-                latin:styleName="functionalKeyStyle"
-                latin:isModifier="true" />
-        </case>
-        <case
-            latin:colorScheme="black"
-        >
-            <key-style
-                latin:styleName="functionalKeyStyle" />
-        </case>
-    </switch>
+    <key-style
+        latin:styleName="functionalKeyStyle"
+        latin:isFunctional="true" />
     <!-- Base key style for the key which may have settings key as popup key -->
     <switch>
         <case
-            latin:clobberSettingsKey="true"
+            latin:hasSettingsKey="true"
         >
             <key-style
                 latin:styleName="settingsPopupStyle"
@@ -50,160 +38,76 @@
         <default>
             <key-style
                 latin:styleName="settingsPopupStyle"
-                latin:keyHintIcon="@drawable/hint_popup"
-                latin:popupCharacters="\@drawable/sym_keyboard_settings_holo|\@integer/key_settings"
+                latin:keyLabelOption="popupHint"
+                latin:popupCharacters="\@icon/5|\@integer/key_settings"
                 latin:parentStyle="functionalKeyStyle" />
         </default>
     </switch>
     <!-- Functional key styles -->
+    <key-style
+        latin:styleName="shiftKeyStyle"
+        latin:code="@integer/key_shift"
+        latin:keyIcon="iconShiftKey"
+        latin:keyIconShifted="iconShiftedShiftKey"
+        latin:parentStyle="functionalKeyStyle"
+        latin:isSticky="true" />
+    <key-style
+        latin:styleName="deleteKeyStyle"
+        latin:code="@integer/key_delete"
+        latin:keyIcon="iconDeleteKey"
+        latin:parentStyle="functionalKeyStyle"
+        latin:isRepeatable="true" />
     <switch>
+        <!-- When this qwerty keyboard has no voice key but voice key is enabled, then symbol
+             keyboard will have mic key. That means we should use "?123mic" key here. -->
         <case
-            latin:colorScheme="white"
+            latin:voiceKeyEnabled="true"
+            latin:hasVoiceKey="false"
         >
             <key-style
-                latin:styleName="shiftKeyStyle"
-                latin:code="@integer/key_shift"
-                latin:keyIcon="@drawable/sym_keyboard_shift"
-                latin:shiftedIcon="@drawable/sym_keyboard_shift_locked"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_shift"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isSticky="true" />
-            <key-style
-                latin:styleName="deleteKeyStyle"
-                latin:code="@integer/key_delete"
-                latin:keyIcon="@drawable/sym_keyboard_delete"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_delete"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isRepeatable="true" />
-            <switch>
-                <!-- When this qwerty keyboard has no voice key but voice key is enabled, then
-                     symbol keyboard will have mic key. That means we should use "?123mic" key
-                     here. -->
-                <case
-                    latin:voiceKeyEnabled="true"
-                    latin:hasVoiceKey="false"
-                >
-                    <key-style
-                        latin:styleName="toSymbolKeyStyle"
-                        latin:code="@integer/key_switch_alpha_symbol"
-                        latin:keyIcon="@drawable/sym_keyboard_123_mic"
-                        latin:iconPreview="@drawable/sym_keyboard_feedback_123_mic"
-                        latin:parentStyle="functionalKeyStyle" />
-                </case>
-                <default>
-                    <key-style
-                        latin:styleName="toSymbolKeyStyle"
-                        latin:code="@integer/key_switch_alpha_symbol"
-                        latin:keyLabel="@string/label_to_symbol_key"
-                        latin:parentStyle="functionalKeyStyle" />
-                </default>
-            </switch>
-            <key-style
-                latin:styleName="settingsKeyStyle"
-                latin:code="@integer/key_settings"
-                latin:keyIcon="@drawable/sym_keyboard_settings_holo"
-                latin:iconPreview="@drawable/sym_keyboard_settings_holo"
+                latin:styleName="toSymbolKeyStyle"
+                latin:code="@integer/key_switch_alpha_symbol"
+                latin:keyIcon="iconToSymbolKeyWithShortcut"
                 latin:parentStyle="functionalKeyStyle" />
-            <key-style
-                latin:styleName="spaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:keyIcon="@drawable/sym_keyboard_space_holo"
-                latin:iconPreview="@drawable/sym_keyboard_space_holo"
-                latin:parentStyle="functionalKeyStyle" />
-            <key-style
-                latin:styleName="tabKeyStyle"
-                latin:code="@integer/key_tab"
-                latin:keyIcon="@drawable/sym_keyboard_tab_holo"
-                latin:iconPreview="@drawable/sym_keyboard_tab_holo"
-                latin:parentStyle="functionalKeyStyle" />
-            <key-style
-                latin:styleName="micKeyStyle"
-                latin:code="@integer/key_voice"
-                latin:keyIcon="@drawable/sym_keyboard_mic"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_mic"
-                latin:parentStyle="settingsPopupStyle" />
-            <!-- Note: This key style is not for functional tab key. This is used for the tab key
-                 which is laid out as normal letter key. -->
-            <key-style
-                latin:styleName="nonSpecialBackgroundTabKeyStyle"
-                latin:code="@integer/key_tab"
-                latin:keyIcon="@drawable/sym_keyboard_tab_holo"
-                latin:iconPreview="@drawable/sym_keyboard_tab_holo" />
         </case>
-        <case
-            latin:colorScheme="black"
-        >
+        <default>
             <key-style
-                latin:styleName="shiftKeyStyle"
-                latin:code="@integer/key_shift"
-                latin:keyIcon="@drawable/sym_bkeyboard_shift"
-                latin:shiftedIcon="@drawable/sym_bkeyboard_shift_locked"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_shift"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isSticky="true" />
-            <key-style
-                latin:styleName="deleteKeyStyle"
-                latin:code="@integer/key_delete"
-                latin:keyIcon="@drawable/sym_bkeyboard_delete"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_delete"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isRepeatable="true" />
-            <switch>
-                <!-- When this qwerty keyboard has no voice key but voice key is enabled, then
-                     symbol keyboard will have mic key. That means we should use "?123mic" key
-                     here. -->
-                <case
-                    latin:voiceKeyEnabled="true"
-                    latin:hasVoiceKey="false"
-                >
-                    <key-style
-                        latin:styleName="toSymbolKeyStyle"
-                        latin:code="@integer/key_switch_alpha_symbol"
-                        latin:keyIcon="@drawable/sym_bkeyboard_123_mic"
-                        latin:iconPreview="@drawable/sym_keyboard_feedback_123_mic"
-                        latin:parentStyle="functionalKeyStyle" />
-                </case>
-                <default>
-                    <key-style
-                        latin:styleName="toSymbolKeyStyle"
-                        latin:code="@integer/key_switch_alpha_symbol"
-                        latin:keyLabel="@string/label_to_symbol_key"
-                        latin:parentStyle="functionalKeyStyle" />
-                </default>
-            </switch>
-            <key-style
-                latin:styleName="settingsKeyStyle"
-                latin:code="@integer/key_settings"
-                latin:keyIcon="@drawable/sym_bkeyboard_settings"
-                latin:iconPreview="@drawable/sym_keyboard_settings_holo"
+                latin:styleName="toSymbolKeyStyle"
+                latin:code="@integer/key_switch_alpha_symbol"
+                latin:keyLabel="@string/label_to_symbol_key"
                 latin:parentStyle="functionalKeyStyle" />
-            <key-style
-                latin:styleName="spaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:keyIcon="@drawable/sym_bkeyboard_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space"
-                latin:parentStyle="functionalKeyStyle" />
-            <key-style
-                latin:styleName="tabKeyStyle"
-                latin:code="@integer/key_tab"
-                latin:keyIcon="@drawable/sym_bkeyboard_tab"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_tab"
-                latin:parentStyle="functionalKeyStyle" />
-            <key-style
-                latin:styleName="micKeyStyle"
-                latin:code="@integer/key_voice"
-                latin:keyIcon="@drawable/sym_bkeyboard_mic"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_mic"
-                latin:parentStyle="settingsPopupStyle" />
-            <!-- Note: This key style is not for functional tab key. This is used for the tab key
-                 which is laid out as normal letter key. -->
-            <key-style
-                latin:styleName="nonSpecialBackgroundTabKeyStyle"
-                latin:code="@integer/key_tab"
-                latin:keyIcon="@drawable/sym_bkeyboard_tab"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_tab" />
-        </case>
+        </default>
     </switch>
+    <key-style
+        latin:styleName="settingsKeyStyle"
+        latin:code="@integer/key_settings"
+        latin:keyIcon="iconSettingsKey"
+        latin:keyIconPreview="iconPreviewSettingsKey"
+        latin:parentStyle="functionalKeyStyle" />
+    <key-style
+        latin:styleName="spaceKeyStyle"
+        latin:code="@integer/key_space"
+        latin:keyIconPreview="iconPreviewSpaceKey"
+        latin:parentStyle="functionalKeyStyle" />
+    <key-style
+        latin:styleName="tabKeyStyle"
+        latin:code="@integer/key_tab"
+        latin:keyIcon="iconTabKey"
+        latin:keyIconPreview="iconPreviewTabKey"
+        latin:parentStyle="functionalKeyStyle" />
+    <key-style
+        latin:styleName="shortcutKeyStyle"
+        latin:code="@integer/key_shortcut"
+        latin:keyIcon="iconShortcutKey"
+        latin:keyIconPreview="iconPreviewShortcutKey"
+        latin:parentStyle="settingsPopupStyle" />
+    <!-- Note: This key style is not for functional tab key. This is used for the tab key which is
+         laid out as normal letter key. -->
+    <key-style
+        latin:styleName="nonSpecialBackgroundTabKeyStyle"
+        latin:code="@integer/key_tab"
+        latin:keyIcon="iconTabKey"
+        latin:keyIconPreview="iconPreviewTabKey" />
     <!-- Return key style -->
     <switch>
         <case
@@ -245,52 +149,18 @@
         <case
             latin:imeAction="actionSearch"
         >
-            <switch>
-                <case
-                    latin:colorScheme="white"
-                >
-                    <key-style
-                        latin:styleName="returnKeyStyle"
-                        latin:code="@integer/key_return"
-                        latin:keyIcon="@drawable/sym_keyboard_search"
-                        latin:iconPreview="@drawable/sym_keyboard_feedback_search"
-                        latin:parentStyle="functionalKeyStyle" />
-                </case>
-                <case
-                    latin:colorScheme="black"
-                >
-                    <key-style
-                        latin:styleName="returnKeyStyle"
-                        latin:code="@integer/key_return"
-                        latin:keyIcon="@drawable/sym_bkeyboard_search"
-                        latin:iconPreview="@drawable/sym_keyboard_feedback_search"
-                        latin:parentStyle="functionalKeyStyle" />
-                </case>
-            </switch>
+            <key-style
+                latin:styleName="returnKeyStyle"
+                latin:code="@integer/key_return"
+                latin:keyIcon="iconSearchKey"
+                latin:parentStyle="functionalKeyStyle" />
         </case>
         <default>
-            <switch>
-                <case
-                    latin:colorScheme="white"
-                >
-                    <key-style
-                        latin:styleName="returnKeyStyle"
-                        latin:code="@integer/key_return"
-                        latin:keyIcon="@drawable/sym_keyboard_return_holo"
-                        latin:iconPreview="@drawable/sym_keyboard_return_holo"
-                        latin:parentStyle="functionalKeyStyle" />
-                </case>
-                <case
-                    latin:colorScheme="black"
-                >
-                    <key-style
-                        latin:styleName="returnKeyStyle"
-                        latin:code="@integer/key_return"
-                        latin:keyIcon="@drawable/sym_bkeyboard_return"
-                        latin:iconPreview="@drawable/sym_keyboard_feedback_return"
-                        latin:parentStyle="functionalKeyStyle" />
-                </case>
-            </switch>
+            <key-style
+                latin:styleName="returnKeyStyle"
+                latin:code="@integer/key_return"
+                latin:keyIcon="iconReturnKey"
+                latin:parentStyle="functionalKeyStyle" />
         </default>
     </switch>
     <key-style
@@ -308,7 +178,7 @@
         latin:styleName="smileyKeyStyle"
         latin:keyLabel=":-)"
         latin:keyOutputText=":-) "
-        latin:keyHintIcon="@drawable/hint_popup"
+        latin:keyLabelOption="popupHint"
         latin:popupCharacters="@string/alternates_for_smiley"
         latin:maxPopupKeyboardColumn="5"
         latin:parentStyle="functionalKeyStyle" />
diff --git a/java/res/xml/kbd_number.xml b/java/res/xml/kbd_number.xml
index 3c83846..429b818 100644
--- a/java/res/xml/kbd_number.xml
+++ b/java/res/xml/kbd_number.xml
@@ -20,14 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
     latin:keyWidth="26.67%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -67,7 +60,7 @@
                     latin:keyStyle="num9KeyStyle" />
                 <Key
                     latin:keyStyle="deleteKeyStyle"
-                    latin:keyWidth="0%p"
+                    latin:keyWidth="fillRight"
                     latin:keyEdgeFlags="right" />
             </Row>
             <Row
@@ -79,10 +72,11 @@
                 <Spacer />
                 <Key
                     latin:keyStyle="returnKeyStyle"
-                    latin:keyWidth="0%p"
+                    latin:keyWidth="fillRight"
                     latin:keyEdgeFlags="right" />
             </Row>
         </case>
+        <!-- latin:passwordInput="false" -->
         <default>
             <Row
                 latin:rowEdgeFlags="top"
@@ -97,7 +91,7 @@
                 <Key
                     latin:keyLabel="-"
                     latin:keyStyle="functionalKeyStyle"
-                    latin:keyWidth="0%p"
+                    latin:keyWidth="fillRight"
                     latin:keyEdgeFlags="right" />
             </Row>
             <Row>
@@ -111,7 +105,7 @@
                 <Key
                     latin:keyLabel=","
                     latin:keyStyle="functionalKeyStyle"
-                    latin:keyWidth="0%p"
+                    latin:keyWidth="fillRight"
                     latin:keyEdgeFlags="right" />
             </Row>
             <Row>
@@ -124,7 +118,7 @@
                     latin:keyLabel="9" />
                 <Key
                     latin:keyStyle="deleteKeyStyle"
-                    latin:keyWidth="0%p"
+                    latin:keyWidth="fillRight"
                     latin:keyEdgeFlags="right" />
             </Row>
             <Row
@@ -139,7 +133,7 @@
                     latin:keyLabel="." />
                 <Key
                     latin:keyStyle="returnKeyStyle"
-                    latin:keyWidth="0%p"
+                    latin:keyWidth="fillRight"
                     latin:keyEdgeFlags="right" />
             </Row>
         </default>
diff --git a/java/res/xml/kbd_numkey_styles.xml b/java/res/xml/kbd_numkey_styles.xml
index 7439d63..3a26a02 100644
--- a/java/res/xml/kbd_numkey_styles.xml
+++ b/java/res/xml/kbd_numkey_styles.xml
@@ -21,130 +21,75 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <switch>
-        <case
-            latin:colorScheme="white"
-        >
-            <key-style
-                latin:styleName="num0KeyStyle"
-                latin:code="48"
-                latin:keyIcon="@drawable/sym_keyboard_num0" />
-            <key-style
-                latin:styleName="num1KeyStyle"
-                latin:code="49"
-                latin:keyIcon="@drawable/sym_keyboard_num1" />
-            <key-style
-                latin:styleName="num2KeyStyle"
-                latin:code="50"
-                latin:keyIcon="@drawable/sym_keyboard_num2" />
-            <key-style
-                latin:styleName="num3KeyStyle"
-                latin:code="51"
-                latin:keyIcon="@drawable/sym_keyboard_num3" />
-            <key-style
-                latin:styleName="num4KeyStyle"
-                latin:code="52"
-                latin:keyIcon="@drawable/sym_keyboard_num4" />
-            <key-style
-                latin:styleName="num5KeyStyle"
-                latin:code="53"
-                latin:keyIcon="@drawable/sym_keyboard_num5" />
-            <key-style
-                latin:styleName="num6KeyStyle"
-                latin:code="54"
-                latin:keyIcon="@drawable/sym_keyboard_num6" />
-            <key-style
-                latin:styleName="num7KeyStyle"
-                latin:code="55"
-                latin:keyIcon="@drawable/sym_keyboard_num7" />
-            <key-style
-                latin:styleName="num8KeyStyle"
-                latin:code="56"
-                latin:keyIcon="@drawable/sym_keyboard_num8" />
-            <key-style
-                latin:styleName="num9KeyStyle"
-                latin:code="57"
-                latin:keyIcon="@drawable/sym_keyboard_num9" />
-            <key-style
-                latin:styleName="numStarKeyStyle"
-                latin:code="42"
-                latin:keyIcon="@drawable/sym_keyboard_numstar" />
-            <key-style
-                latin:styleName="numPoundKeyStyle"
-                latin:code="35"
-                latin:keyIcon="@drawable/sym_keyboard_numpound" />
-            <key-style
-                latin:styleName="numAltKeyStyle"
-                latin:code="@integer/key_switch_alpha_symbol"
-                latin:keyIcon="@drawable/sym_keyboard_numsymbol_holo"
-                latin:iconPreview="@drawable/sym_keyboard_numsymbol_holo" />
-            <key-style
-                latin:styleName="numSpaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:keyIcon="@drawable/sym_keyboard_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-        </case>
-        <case
-            latin:colorScheme="black"
-        >
-            <key-style
-                latin:styleName="num0KeyStyle"
-                latin:code="48"
-                latin:keyIcon="@drawable/sym_bkeyboard_num0" />
-            <key-style
-                latin:styleName="num1KeyStyle"
-                latin:code="49"
-                latin:keyIcon="@drawable/sym_bkeyboard_num1" />
-            <key-style
-                latin:styleName="num2KeyStyle"
-                latin:code="50"
-                latin:keyIcon="@drawable/sym_bkeyboard_num2" />
-            <key-style
-                latin:styleName="num3KeyStyle"
-                latin:code="51"
-                latin:keyIcon="@drawable/sym_bkeyboard_num3" />
-            <key-style
-                latin:styleName="num4KeyStyle"
-                latin:code="52"
-                latin:keyIcon="@drawable/sym_bkeyboard_num4" />
-            <key-style
-                latin:styleName="num5KeyStyle"
-                latin:code="53"
-                latin:keyIcon="@drawable/sym_bkeyboard_num5" />
-            <key-style
-                latin:styleName="num6KeyStyle"
-                latin:code="54"
-                latin:keyIcon="@drawable/sym_bkeyboard_num6" />
-            <key-style
-                latin:styleName="num7KeyStyle"
-                latin:code="55"
-                latin:keyIcon="@drawable/sym_bkeyboard_num7" />
-            <key-style
-                latin:styleName="num8KeyStyle"
-                latin:code="56"
-                latin:keyIcon="@drawable/sym_bkeyboard_num8" />
-            <key-style
-                latin:styleName="num9KeyStyle"
-                latin:code="57"
-                latin:keyIcon="@drawable/sym_bkeyboard_num9" />
-            <key-style
-                latin:styleName="numStarKeyStyle"
-                latin:code="42"
-                latin:keyIcon="@drawable/sym_bkeyboard_numstar" />
-            <key-style
-                latin:styleName="numPoundKeyStyle"
-                latin:code="35"
-                latin:keyIcon="@drawable/sym_bkeyboard_numpound" />
-            <key-style
-                latin:styleName="numAltKeyStyle"
-                latin:code="@integer/key_switch_alpha_symbol"
-                latin:keyIcon="@drawable/sym_bkeyboard_numalt"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_numalt" />
-            <key-style
-                latin:styleName="numSpaceKeyStyle"
-                latin:code="@integer/key_space"
-                latin:keyIcon="@drawable/sym_bkeyboard_space"
-                latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
-        </case>
-    </switch>
+    <key-style
+        latin:styleName="num0KeyStyle"
+        latin:code="48"
+        latin:keyIcon="iconNum0Key" />
+    <key-style
+        latin:styleName="num1KeyStyle"
+        latin:code="49"
+        latin:keyIcon="iconNum1Key" />
+    <key-style
+        latin:styleName="num2KeyStyle"
+        latin:code="50"
+        latin:keyIcon="iconNum2Key" />
+    <key-style
+        latin:styleName="num3KeyStyle"
+        latin:code="51"
+        latin:keyIcon="iconNum3Key" />
+    <key-style
+        latin:styleName="num4KeyStyle"
+        latin:code="52"
+        latin:keyIcon="iconNum4Key" />
+    <key-style
+        latin:styleName="num5KeyStyle"
+        latin:code="53"
+        latin:keyIcon="iconNum5Key" />
+    <key-style
+        latin:styleName="num6KeyStyle"
+        latin:code="54"
+        latin:keyIcon="iconNum6Key" />
+    <key-style
+        latin:styleName="num7KeyStyle"
+        latin:code="55"
+        latin:keyIcon="iconNum7Key" />
+    <key-style
+        latin:styleName="num8KeyStyle"
+        latin:code="56"
+        latin:keyIcon="iconNum8Key" />
+    <key-style
+        latin:styleName="num9KeyStyle"
+        latin:code="57"
+        latin:keyIcon="iconNum9Key" />
+    <key-style
+        latin:styleName="numLabelStyle"
+        latin:keyLabelOption="fontFixedWidth|followKeyLetterRatio" />
+    <key-style
+        latin:styleName="numLabelFunctionalStyle"
+        latin:keyLabelOption="fontFixedWidth|followKeyLetterRatio"
+        latin:parentStyle="functionalKeyStyle" />
+    <key-style
+        latin:styleName="numStarKeyStyle"
+        latin:code="42"
+        latin:keyLabel="\uff0a"
+        latin:parentStyle="numLabelStyle" />
+    <key-style
+        latin:styleName="numPoundKeyStyle"
+        latin:keyLabel="#"
+        latin:parentStyle="numLabelStyle" />
+    <key-style
+        latin:styleName="numSwitchToAltKeyStyle"
+        latin:code="@integer/key_switch_alpha_symbol"
+        latin:keyLabel="@string/label_to_phone_symbols_key"
+        latin:parentStyle="numLabelStyle" />
+    <key-style
+        latin:styleName="numSwitchToNumericKeyStyle"
+        latin:code="@integer/key_switch_alpha_symbol"
+        latin:keyLabel="@string/label_to_phone_numeric_key"
+        latin:parentStyle="numLabelStyle" />
+    <key-style
+        latin:styleName="numSpaceKeyStyle"
+        latin:code="@integer/key_space"
+        latin:keyIcon="iconSpaceKey"
+        latin:keyIconPreview="iconPreviewSpaceKey" />
 </merge>
diff --git a/java/res/xml/kbd_phone.xml b/java/res/xml/kbd_phone.xml
index 7af8d07..794528c 100644
--- a/java/res/xml/kbd_phone.xml
+++ b/java/res/xml/kbd_phone.xml
@@ -20,14 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
     latin:keyWidth="26.67%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -45,8 +38,8 @@
             latin:keyStyle="num3KeyStyle" />
         <Key
             latin:keyLabel="-"
-            latin:keyStyle="functionalKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyStyle="numLabelFunctionalStyle"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
@@ -59,8 +52,8 @@
             latin:keyStyle="num6KeyStyle" />
         <Key
             latin:keyLabel="."
-            latin:keyStyle="functionalKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyStyle="numLabelFunctionalStyle"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
@@ -73,14 +66,14 @@
             latin:keyStyle="num9KeyStyle" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
         latin:rowEdgeFlags="bottom"
     >
         <Key
-            latin:keyStyle="numAltKeyStyle"
+            latin:keyStyle="numSwitchToAltKeyStyle"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyStyle="num0KeyStyle" />
@@ -88,7 +81,7 @@
             latin:keyStyle="numSpaceKeyStyle" />
         <Key
             latin:keyStyle="returnKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
 </Keyboard>
diff --git a/java/res/xml/kbd_phone_symbols.xml b/java/res/xml/kbd_phone_symbols.xml
index 4b6319e..1405c3c 100644
--- a/java/res/xml/kbd_phone_symbols.xml
+++ b/java/res/xml/kbd_phone_symbols.xml
@@ -20,14 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
     latin:keyWidth="26.67%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -38,20 +31,24 @@
     >
         <Key
             latin:keyLabel="("
+            latin:keyStyle="numLabelStyle"
             latin:keyEdgeFlags="left" />
         <Key
-            latin:keyLabel="/" />
+            latin:keyLabel="/"
+            latin:keyStyle="numLabelStyle" />
         <Key
-            latin:keyLabel=")" />
+            latin:keyLabel=")"
+            latin:keyStyle="numLabelStyle" />
         <Key
             latin:keyLabel="-"
-            latin:keyStyle="functionalKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyStyle="numLabelFunctionalStyle"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
         <Key
             latin:keyLabel="N"
+            latin:keyStyle="numLabelStyle"
             latin:keyEdgeFlags="left" />
         <!-- Pause is a comma. Check PhoneNumberUtils.java to see if this 
             has changed. -->
@@ -59,11 +56,12 @@
             latin:code="44"
             latin:keyLabel="Pause" />
         <Key
-            latin:keyLabel="," />
+            latin:keyLabel=","
+            latin:keyStyle="numLabelStyle" />
         <Key
             latin:keyLabel="."
-            latin:keyStyle="functionalKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyStyle="numLabelFunctionalStyle"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
@@ -75,26 +73,27 @@
             latin:code="59"
             latin:keyLabel="Wait" />
         <Key
-            latin:keyStyle="numPoundKeyStyle" />
+            latin:keyLabel="#"
+            latin:keyStyle="numLabelStyle" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
         latin:rowEdgeFlags="bottom"
     >
         <Key
-            latin:code="@integer/key_switch_alpha_symbol"
-            latin:keyLabel="@string/label_to_numeric_key"
+            latin:keyStyle="numSwitchToNumericKeyStyle"
             latin:keyEdgeFlags="left" />
         <Key
-            latin:keyLabel="+" />
+            latin:keyLabel="+"
+            latin:keyStyle="numLabelStyle" />
         <Key
             latin:keyStyle="numSpaceKeyStyle" />
         <Key
             latin:keyStyle="returnKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
 </Keyboard>
diff --git a/java/res/xml/kbd_qwerty.xml b/java/res/xml/kbd_qwerty.xml
index 92d92f0..40917b9 100644
--- a/java/res/xml/kbd_qwerty.xml
+++ b/java/res/xml/kbd_qwerty.xml
@@ -20,15 +20,8 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
-    latin:keyWidth="10%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
+    latin:keyboardLocale="en_GB,en_US"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_qwerty_rows" />
+        latin:keyboardLayout="@xml/kbd_rows_qwerty" />
 </Keyboard>
diff --git a/java/res/xml/kbd_qwerty_f1.xml b/java/res/xml/kbd_qwerty_f1.xml
index e89d02d..51b9800 100644
--- a/java/res/xml/kbd_qwerty_f1.xml
+++ b/java/res/xml/kbd_qwerty_f1.xml
@@ -23,39 +23,92 @@
 >
     <switch>
         <case
-            latin:mode="url"
+            latin:hasSettingsKey="true"
         >
-            <Key
-                latin:keyLabel="/"
-                latin:keyStyle="settingsPopupStyle" />
-        </case>
-        <case
-            latin:mode="email"
-        >
-            <Key
-                latin:keyLabel="\@"
-                latin:keyStyle="settingsPopupStyle" />
-        </case>
-        <default>
             <switch>
                 <case
-                    latin:hasVoiceKey="true"
+                    latin:mode="url"
                 >
                     <Key
-                        latin:keyStyle="micKeyStyle" />
+                        latin:keyLabel="/"
+                        latin:keyWidth="9.2%p"
+                        latin:keyStyle="settingsPopupStyle" />
                 </case>
-                <!-- latin:hasVoiceKey="false" -->
                 <case
-                    latin:webInput="true"
+                    latin:mode="email"
                 >
                     <Key
-                        latin:keyLabel="."
+                        latin:keyLabel="\@"
+                        latin:keyWidth="9.2%p"
                         latin:keyStyle="settingsPopupStyle" />
                 </case>
                 <default>
+                    <switch>
+                        <case
+                            latin:hasVoiceKey="true"
+                        >
+                            <Key
+                                latin:keyStyle="shortcutKeyStyle"
+                                latin:keyWidth="9.2%p" />
+                        </case>
+                        <!-- latin:hasVoiceKey="false" -->
+                        <case
+                            latin:navigateAction="true"
+                        >
+                            <Key
+                                latin:keyLabel="."
+                                latin:keyWidth="9.2%p"
+                                latin:keyStyle="settingsPopupStyle" />
+                        </case>
+                        <default>
+                            <Key
+                                latin:keyLabel=","
+                                latin:keyWidth="9.2%p"
+                                latin:keyStyle="settingsPopupStyle" />
+                        </default>
+                    </switch>
+                </default>
+            </switch>
+        </case>
+        <!-- hasSettingsKey="false" -->
+        <default>
+            <switch>
+                <case
+                    latin:mode="url"
+                >
                     <Key
-                        latin:keyLabel=","
+                        latin:keyLabel="/"
                         latin:keyStyle="settingsPopupStyle" />
+                </case>
+                <case
+                    latin:mode="email"
+                >
+                    <Key
+                        latin:keyLabel="\@"
+                        latin:keyStyle="settingsPopupStyle" />
+                </case>
+                <default>
+                    <switch>
+                        <case
+                            latin:hasVoiceKey="true"
+                        >
+                            <Key
+                                latin:keyStyle="shortcutKeyStyle" />
+                        </case>
+                        <!-- latin:hasVoiceKey="false" -->
+                        <case
+                            latin:navigateAction="true"
+                        >
+                            <Key
+                                latin:keyLabel="."
+                                latin:keyStyle="settingsPopupStyle" />
+                        </case>
+                        <default>
+                            <Key
+                                latin:keyLabel=","
+                                latin:keyStyle="settingsPopupStyle" />
+                        </default>
+                    </switch>
                 </default>
             </switch>
         </default>
diff --git a/java/res/xml/kbd_qwerty_row1.xml b/java/res/xml/kbd_qwerty_row1.xml
index 9ee1643..8c3669d 100644
--- a/java/res/xml/kbd_qwerty_row1.xml
+++ b/java/res/xml/kbd_qwerty_row1.xml
@@ -27,46 +27,46 @@
     >
         <Key
             latin:keyLabel="q"
-            latin:keyHintIcon="@drawable/key_hint_num1"
+            latin:keyHintLetter="1"
             latin:popupCharacters="@string/alternates_for_q"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="w"
-            latin:keyHintIcon="@drawable/key_hint_num2"
+            latin:keyHintLetter="2"
             latin:popupCharacters="@string/alternates_for_w" />
         <Key
             latin:keyLabel="e"
-            latin:keyHintIcon="@drawable/key_hint_num3"
+            latin:keyHintLetter="3"
             latin:popupCharacters="@string/alternates_for_e" />
         <Key
             latin:keyLabel="r"
-            latin:keyHintIcon="@drawable/key_hint_num4"
+            latin:keyHintLetter="4"
             latin:popupCharacters="@string/alternates_for_r" />
         <Key
             latin:keyLabel="t"
-            latin:keyHintIcon="@drawable/key_hint_num5"
+            latin:keyHintLetter="5"
             latin:popupCharacters="@string/alternates_for_t" />
         <Key
             latin:keyLabel="y"
-            latin:keyHintIcon="@drawable/key_hint_num6"
+            latin:keyHintLetter="6"
             latin:popupCharacters="@string/alternates_for_y" />
         <Key
             latin:keyLabel="u"
-            latin:keyHintIcon="@drawable/key_hint_num7"
+            latin:keyHintLetter="7"
             latin:popupCharacters="@string/alternates_for_u" />
         <Key
             latin:keyLabel="i"
-            latin:keyHintIcon="@drawable/key_hint_num8"
+            latin:keyHintLetter="8"
             latin:popupCharacters="@string/alternates_for_i" />
         <Key
             latin:keyLabel="o"
-            latin:keyHintIcon="@drawable/key_hint_num9"
+            latin:keyHintLetter="9"
             latin:popupCharacters="@string/alternates_for_o" />
         <Key
             latin:keyLabel="p"
-            latin:keyHintIcon="@drawable/key_hint_num0"
+            latin:keyHintLetter="0"
             latin:popupCharacters="@string/alternates_for_p"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
 </merge>
diff --git a/java/res/xml/kbd_qwerty_row2.xml b/java/res/xml/kbd_qwerty_row2.xml
index cf9b304..57bbad7 100644
--- a/java/res/xml/kbd_qwerty_row2.xml
+++ b/java/res/xml/kbd_qwerty_row2.xml
@@ -26,8 +26,8 @@
     >
         <Key
             latin:keyLabel="a"
-            latin:keyXPos="5%p"
             latin:popupCharacters="@string/alternates_for_a"
+            latin:keyXPos="5%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="s"
@@ -50,7 +50,7 @@
         <Key
             latin:keyLabel="l"
             latin:popupCharacters="@string/alternates_for_l"
-            latin:keyWidth="0%p"
             latin:keyEdgeFlags="right" />
+        <!-- Here is 5%p space -->
     </Row>
 </merge>
diff --git a/java/res/xml/kbd_qwerty_row3.xml b/java/res/xml/kbd_qwerty_row3.xml
index ffbf061..98f0404 100644
--- a/java/res/xml/kbd_qwerty_row3.xml
+++ b/java/res/xml/kbd_qwerty_row3.xml
@@ -27,6 +27,7 @@
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="15%p"
+            latin:visualInsetsRight="1%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="z"
@@ -48,7 +49,8 @@
             latin:keyLabel="m" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillBoth"
+            latin:visualInsetsLeft="1%p"
             latin:keyEdgeFlags="right" />
     </Row>
 </merge>
diff --git a/java/res/xml/kbd_qwerty_row4.xml b/java/res/xml/kbd_qwerty_row4.xml
index 67a1a76..0951339 100644
--- a/java/res/xml/kbd_qwerty_row4.xml
+++ b/java/res/xml/kbd_qwerty_row4.xml
@@ -27,104 +27,89 @@
     >
         <switch>
             <case
-                latin:hasSettingsKey="false"
-            >
-                <Key
-                    latin:keyStyle="toSymbolKeyStyle"
-                    latin:keyWidth="20%p"
-                    latin:keyEdgeFlags="left" />
-                <include
-                    latin:keyboardLayout="@xml/kbd_qwerty_f1" />
-                <switch>
-                    <case
-                        latin:webInput="true"
-                    >
-                        <Key
-                            latin:keyStyle="spaceKeyStyle"
-                            latin:keyWidth="20%p" />
-                        <Key
-                            latin:keyStyle="tabKeyStyle"
-                            latin:keyWidth="20%p" />
-                    </case>
-                    <default>
-                        <Key
-                            latin:keyStyle="spaceKeyStyle"
-                            latin:keyWidth="40%p" />
-                    </default>
-                </switch>
-                <Key
-                    latin:keyLabel="."
-                    latin:keyHintIcon="@drawable/hint_popup"
-                    latin:popupCharacters="@string/alternates_for_punctuation"
-                    latin:maxPopupKeyboardColumn="7"
-                    latin:keyStyle="functionalKeyStyle" />
-                <switch>
-                    <case
-                        latin:mode="im"
-                    >
-                        <Key
-                            latin:keyStyle="smileyKeyStyle"
-                            latin:keyWidth="0%p"
-                            latin:keyEdgeFlags="right" />
-                    </case>
-                    <default>
-                        <Key
-                            latin:keyStyle="returnKeyStyle"
-                            latin:keyWidth="0%p"
-                            latin:keyEdgeFlags="right" />
-                    </default>
-                </switch>
-            </case>
-            <case
                 latin:hasSettingsKey="true"
             >
                 <Key
                     latin:keyStyle="toSymbolKeyStyle"
-                    latin:keyWidth="15%p"
+                    latin:keyWidth="13.75%p"
                     latin:keyEdgeFlags="left" />
                 <Key
-                    latin:keyStyle="settingsKeyStyle" />
+                    latin:keyStyle="settingsKeyStyle"
+                    latin:keyWidth="9.2%p" />
                 <include
                     latin:keyboardLayout="@xml/kbd_qwerty_f1" />
-                <switch>
-                    <case
-                        latin:webInput="true"
-                    >
-                        <Key
-                            latin:keyStyle="spaceKeyStyle"
-                            latin:keyWidth="30%p" />
-                        <Key
-                            latin:keyStyle="tabKeyStyle" />
-                    </case>
-                    <default>
-                        <Key
-                            latin:keyStyle="spaceKeyStyle"
-                            latin:keyWidth="30%p" />
-                    </default>
-                </switch>
                 <Key
-                    latin:keyLabel="."
-                    latin:keyHintIcon="@drawable/hint_popup"
-                    latin:popupCharacters="@string/alternates_for_punctuation"
-                    latin:maxPopupKeyboardColumn="7"
-                    latin:keyStyle="functionalKeyStyle" />
+                    latin:keyStyle="spaceKeyStyle"
+                    latin:keyWidth="35.83%p" />
                 <switch>
                     <case
-                        latin:mode="im"
+                        latin:navigateAction="true"
                     >
                         <Key
-                            latin:keyStyle="smileyKeyStyle"
-                            latin:keyWidth="0%p"
-                            latin:keyEdgeFlags="right" />
+                            latin:keyStyle="tabKeyStyle"
+                            latin:keyLabelOption="popupHint"
+                            latin:popupCharacters="@string/alternates_for_web_tab_punctuation"
+                            latin:keyWidth="9.2%p"
+                            latin:maxPopupKeyboardColumn="8" />
                     </case>
                     <default>
                         <Key
-                            latin:keyStyle="returnKeyStyle"
-                            latin:keyWidth="0%p"
-                            latin:keyEdgeFlags="right" />
+                            latin:keyLabel="."
+                            latin:keyLabelOption="popupHint"
+                            latin:popupCharacters="@string/alternates_for_punctuation"
+                            latin:keyWidth="9.2%p"
+                            latin:maxPopupKeyboardColumn="7"
+                            latin:keyStyle="functionalKeyStyle" />
                     </default>
                 </switch>
             </case>
+            <!-- latin:hasSettingsKey="false" -->
+            <default>
+                <Key
+                    latin:keyStyle="toSymbolKeyStyle"
+                    latin:keyWidth="15%p"
+                    latin:keyEdgeFlags="left" />
+                <include
+                    latin:keyboardLayout="@xml/kbd_qwerty_f1" />
+                <Key
+                    latin:keyStyle="spaceKeyStyle"
+                    latin:keyWidth="50%p" />
+                <switch>
+                    <case
+                        latin:navigateAction="true"
+                    >
+                        <Key
+                            latin:keyStyle="tabKeyStyle"
+                            latin:keyLabelOption="popupHint"
+                            latin:popupCharacters="@string/alternates_for_web_tab_punctuation"
+                            latin:maxPopupKeyboardColumn="8" />
+                    </case>
+                    <default>
+                        <Key
+                            latin:keyLabel="."
+                            latin:keyLabelOption="popupHint"
+                            latin:popupCharacters="@string/alternates_for_punctuation"
+                            latin:maxPopupKeyboardColumn="7"
+                            latin:keyStyle="functionalKeyStyle" />
+                    </default>
+                </switch>
+            </default>
+        </switch>
+        <switch>
+            <case
+                latin:mode="im"
+            >
+                <Key
+                    latin:keyStyle="smileyKeyStyle"
+                    latin:keyWidth="fillRight"
+                    latin:keyEdgeFlags="right" />
+            </case>
+            <default>
+                <Key
+                    latin:keyStyle="returnKeyStyle"
+                    latin:keyWidth="fillRight"
+                    latin:keyEdgeFlags="right" />
+            </default>
         </switch>
     </Row>
 </merge>
diff --git a/java/res/xml/kbd_rows_arabic.xml b/java/res/xml/kbd_rows_arabic.xml
new file mode 100644
index 0000000..a548775
--- /dev/null
+++ b/java/res/xml/kbd_rows_arabic.xml
@@ -0,0 +1,122 @@
+<?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.
+*/
+-->
+
+<!-- This file for Arabic layout is an alpha version. It allows to enter   -->
+<!-- some right-to-left text, but it has gone through no study whatsoever, -->
+<!-- and needs to be run through UX.                                       -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_key_styles" />
+    <Row
+        latin:keyWidth="10%p"
+    >
+        <Key
+            latin:keyLabel="ض"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ص" />
+        <Key
+            latin:keyLabel="ق" />
+        <Key
+            latin:keyLabel="ف"
+            latin:popupCharacters="ڤ" />
+        <Key
+            latin:keyLabel="غ" />
+        <Key
+            latin:keyLabel="ع" />
+        <Key
+            latin:keyLabel="ه"
+            latin:popupCharacters="هـ" />
+        <Key
+            latin:keyLabel="خ" />
+        <Key
+            latin:keyLabel="ح" />
+        <Key
+            latin:keyLabel="ج"
+            latin:popupCharacters="چ"
+            latin:keyWidth="fillRight"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="10%p"
+    >
+        <Key
+            latin:keyLabel="ش"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="س" />
+        <Key
+            latin:keyLabel="ي"
+            latin:popupCharacters="ى,ئ" />
+        <Key
+            latin:keyLabel="ب"
+            latin:popupCharacters="پ" />
+        <Key
+            latin:keyLabel="ل"
+            latin:popupCharacters="لا" />
+        <Key
+            latin:keyLabel="ا"
+            latin:popupCharacters="أ,إ,آ,ء" />
+        <Key
+            latin:keyLabel="ت" />
+        <Key
+            latin:keyLabel="ن" />
+        <Key
+            latin:keyLabel="م" />
+        <Key
+            latin:keyLabel="ك"
+            latin:popupCharacters="گ"
+            latin:keyWidth="fillRight"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="9.7%p"
+    >
+        <Key
+            latin:keyLabel="ظ"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ط" />
+        <Key
+            latin:keyLabel="ذ" />
+        <Key
+            latin:keyLabel="د" />
+        <Key
+            latin:keyLabel="ز"
+            latin:popupCharacters="ژ" />
+        <Key
+            latin:keyLabel="ر" />
+        <Key
+            latin:keyLabel="و"
+            latin:popupCharacters="ؤ" />
+        <Key
+            latin:keyLabel="ة" />
+        <Key
+            latin:keyLabel="ث" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight"
+            latin:visualInsetsLeft="1%p"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <include latin:keyboardLayout="@xml/kbd_qwerty_row4" />
+</merge>
diff --git a/java/res/xml/kbd_azerty_rows.xml b/java/res/xml/kbd_rows_azerty.xml
similarity index 84%
rename from java/res/xml/kbd_azerty_rows.xml
rename to java/res/xml/kbd_rows_azerty.xml
index 4a76698..9825c4a 100644
--- a/java/res/xml/kbd_azerty_rows.xml
+++ b/java/res/xml/kbd_rows_azerty.xml
@@ -29,46 +29,46 @@
     >
         <Key
             latin:keyLabel="a"
-            latin:keyHintIcon="@drawable/key_hint_num1"
+            latin:keyHintLetter="1"
             latin:popupCharacters="@string/alternates_for_a"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="z"
-            latin:keyHintIcon="@drawable/key_hint_num2"
+            latin:keyHintLetter="2"
             latin:popupCharacters="@string/alternates_for_z" />
         <Key
             latin:keyLabel="e"
-            latin:keyHintIcon="@drawable/key_hint_num3"
+            latin:keyHintLetter="3"
             latin:popupCharacters="@string/alternates_for_e" />
         <Key
             latin:keyLabel="r"
-            latin:keyHintIcon="@drawable/key_hint_num4"
+            latin:keyHintLetter="4"
             latin:popupCharacters="@string/alternates_for_r" />
         <Key
             latin:keyLabel="t"
-            latin:keyHintIcon="@drawable/key_hint_num5"
+            latin:keyHintLetter="5"
             latin:popupCharacters="@string/alternates_for_t" />
         <Key
             latin:keyLabel="y"
-            latin:keyHintIcon="@drawable/key_hint_num6"
+            latin:keyHintLetter="6"
             latin:popupCharacters="@string/alternates_for_y" />
         <Key
             latin:keyLabel="u"
-            latin:keyHintIcon="@drawable/key_hint_num7"
+            latin:keyHintLetter="7"
             latin:popupCharacters="@string/alternates_for_u" />
         <Key
             latin:keyLabel="i"
-            latin:keyHintIcon="@drawable/key_hint_num8"
+            latin:keyHintLetter="8"
             latin:popupCharacters="@string/alternates_for_i" />
         <Key
             latin:keyLabel="o"
-            latin:keyHintIcon="@drawable/key_hint_num9"
+            latin:keyHintLetter="9"
             latin:popupCharacters="@string/alternates_for_o" />
         <Key
             latin:keyLabel="p"
-            latin:keyHintIcon="@drawable/key_hint_num0"
+            latin:keyHintLetter="0"
             latin:popupCharacters="@string/alternates_for_p"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -101,7 +101,7 @@
             latin:popupCharacters="@string/alternates_for_l" />
         <Key
             latin:keyLabel="m"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -110,6 +110,7 @@
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="15%p"
+            latin:visualInsetsRight="1%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="w"
@@ -129,10 +130,12 @@
             latin:keyLabel="n"
             latin:popupCharacters="@string/alternates_for_n" />
         <Key
-            latin:keyLabel="\'" />
+            latin:keyLabel="\'"
+            latin:popupCharacters="‘,’,‚,‛" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
+            latin:visualInsetsLeft="1%p"
             latin:keyEdgeFlags="right" />
     </Row>
     <include
diff --git a/java/res/xml/kbd_rows_hebrew.xml b/java/res/xml/kbd_rows_hebrew.xml
new file mode 100644
index 0000000..c3c8f7d
--- /dev/null
+++ b/java/res/xml/kbd_rows_hebrew.xml
@@ -0,0 +1,111 @@
+<?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.
+*/
+-->
+
+<!-- This file for Hebrew layout is an alpha version. It allows to enter   -->
+<!-- some right-to-left text, but it has gone through no study whatsoever, -->
+<!-- and needs to be run through UX.                                       -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_key_styles" />
+    <Row
+        latin:keyWidth="10%p"
+        latin:rowEdgeFlags="top"
+    >
+        <Key
+            latin:keyLabel="ק"
+            latin:keyXPos="5%p"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ר" />
+        <Key
+            latin:keyLabel="א" />
+        <Key
+            latin:keyLabel="ט" />
+        <Key
+            latin:keyLabel="ו" />
+        <Key
+            latin:keyLabel="ן" />
+        <Key
+            latin:keyLabel="ם" />
+        <Key
+            latin:keyLabel="פ" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight"
+            latin:visualInsetsLeft="1%p"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="10%p"
+    >
+        <Key
+            latin:keyLabel="ש"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ד" />
+        <Key
+            latin:keyLabel="ג" />
+        <Key
+            latin:keyLabel="כ" />
+        <Key
+            latin:keyLabel="ע" />
+        <Key
+            latin:keyLabel="י" />
+        <Key
+            latin:keyLabel="ח" />
+        <Key
+            latin:keyLabel="ל" />
+        <Key
+            latin:keyLabel="ך" />
+        <Key
+            latin:keyLabel="ף"
+            latin:keyWidth="fillRight"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <Row
+        latin:keyWidth="10%p"
+    >
+        <Key
+            latin:keyLabel="ז"
+            latin:keyXPos="5%p"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="ס" />
+        <Key
+            latin:keyLabel="ב" />
+        <Key
+            latin:keyLabel="ה" />
+        <Key
+            latin:keyLabel="נ" />
+        <Key
+            latin:keyLabel="מ" />
+        <Key
+            latin:keyLabel="צ" />
+        <Key
+            latin:keyLabel="ת" />
+        <Key
+            latin:keyLabel="ץ"
+            latin:keyEdgeFlags="right" />
+        <!-- Here is 5%p space -->
+    </Row>
+    <include latin:keyboardLayout="@xml/kbd_qwerty_row4" />
+</merge>
diff --git a/java/res/xml/kbd_qwerty_rows.xml b/java/res/xml/kbd_rows_qwerty.xml
similarity index 100%
rename from java/res/xml/kbd_qwerty_rows.xml
rename to java/res/xml/kbd_rows_qwerty.xml
diff --git a/java/res/xml/kbd_qwertz_rows.xml b/java/res/xml/kbd_rows_qwertz.xml
similarity index 82%
rename from java/res/xml/kbd_qwertz_rows.xml
rename to java/res/xml/kbd_rows_qwertz.xml
index 960b9f5..0d70b26 100644
--- a/java/res/xml/kbd_qwertz_rows.xml
+++ b/java/res/xml/kbd_rows_qwertz.xml
@@ -29,46 +29,46 @@
     >
         <Key
             latin:keyLabel="q"
-            latin:keyHintIcon="@drawable/key_hint_num1"
+            latin:keyHintLetter="1"
             latin:popupCharacters="@string/alternates_for_q"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="w"
-            latin:keyHintIcon="@drawable/key_hint_num2"
+            latin:keyHintLetter="2"
             latin:popupCharacters="@string/alternates_for_w" />
         <Key
             latin:keyLabel="e"
-            latin:keyHintIcon="@drawable/key_hint_num3"
+            latin:keyHintLetter="3"
             latin:popupCharacters="@string/alternates_for_e" />
         <Key
             latin:keyLabel="r"
-            latin:keyHintIcon="@drawable/key_hint_num4"
+            latin:keyHintLetter="4"
             latin:popupCharacters="@string/alternates_for_r" />
         <Key
             latin:keyLabel="t"
-            latin:keyHintIcon="@drawable/key_hint_num5"
+            latin:keyHintLetter="5"
             latin:popupCharacters="@string/alternates_for_t" />
         <Key
             latin:keyLabel="z"
-            latin:keyHintIcon="@drawable/key_hint_num6"
+            latin:keyHintLetter="6"
             latin:popupCharacters="@string/alternates_for_z" />
         <Key
             latin:keyLabel="u"
-            latin:keyHintIcon="@drawable/key_hint_num7"
+            latin:keyHintLetter="7"
             latin:popupCharacters="@string/alternates_for_u" />
         <Key
             latin:keyLabel="i"
-            latin:keyHintIcon="@drawable/key_hint_num8"
+            latin:keyHintLetter="8"
             latin:popupCharacters="@string/alternates_for_i" />
         <Key
             latin:keyLabel="o"
-            latin:keyHintIcon="@drawable/key_hint_num9"
+            latin:keyHintLetter="9"
             latin:popupCharacters="@string/alternates_for_o" />
         <Key
             latin:keyLabel="p"
-            latin:keyHintIcon="@drawable/key_hint_num0"
+            latin:keyHintLetter="0"
             latin:popupCharacters="@string/alternates_for_p"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <include
@@ -79,6 +79,7 @@
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="15%p"
+            latin:visualInsetsRight="1%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="y"
@@ -100,7 +101,8 @@
             latin:keyLabel="m" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
+            latin:visualInsetsLeft="1%p"
             latin:keyEdgeFlags="right" />
     </Row>
    <include
diff --git a/java/res/xml/kbd_ru_rows.xml b/java/res/xml/kbd_rows_russian.xml
similarity index 84%
rename from java/res/xml/kbd_ru_rows.xml
rename to java/res/xml/kbd_rows_russian.xml
index 5d30221..ff9fef4 100644
--- a/java/res/xml/kbd_ru_rows.xml
+++ b/java/res/xml/kbd_rows_russian.xml
@@ -29,49 +29,49 @@
     >
         <Key
             latin:keyLabel="й"
-            latin:keyHintIcon="@drawable/key_hint_num1"
+            latin:keyHintLetter="1"
             latin:popupCharacters="1"
             latin:keyWidth="8.75%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="ц"
-            latin:keyHintIcon="@drawable/key_hint_num2"
+            latin:keyHintLetter="2"
             latin:popupCharacters="2" />
         <Key
             latin:keyLabel="у"
-            latin:keyHintIcon="@drawable/key_hint_num3"
+            latin:keyHintLetter="3"
             latin:popupCharacters="3" />
         <Key
             latin:keyLabel="к"
-            latin:keyHintIcon="@drawable/key_hint_num4"
+            latin:keyHintLetter="4"
             latin:popupCharacters="4" />
         <Key
             latin:keyLabel="е"
-            latin:keyHintIcon="@drawable/key_hint_num5"
+            latin:keyHintLetter="5"
             latin:popupCharacters="@string/alternates_for_cyrillic_e" />
         <Key
             latin:keyLabel="н"
-            latin:keyHintIcon="@drawable/key_hint_num6"
+            latin:keyHintLetter="6"
             latin:popupCharacters="6" />
         <Key
             latin:keyLabel="г"
-            latin:keyHintIcon="@drawable/key_hint_num7"
+            latin:keyHintLetter="7"
             latin:popupCharacters="7" />
         <Key
             latin:keyLabel="ш"
-            latin:keyHintIcon="@drawable/key_hint_num8"
+            latin:keyHintLetter="8"
             latin:popupCharacters="8" />
         <Key
             latin:keyLabel="щ"
-            latin:keyHintIcon="@drawable/key_hint_num9"
+            latin:keyHintLetter="9"
             latin:popupCharacters="9" />
         <Key
             latin:keyLabel="з"
-            latin:keyHintIcon="@drawable/key_hint_num0"
+            latin:keyHintLetter="0"
             latin:popupCharacters="0" />
         <Key
             latin:keyLabel="х"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -101,7 +101,7 @@
             latin:keyLabel="ж" />
         <Key
             latin:keyLabel="э"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -132,7 +132,7 @@
             latin:keyLabel="ю" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <include
diff --git a/java/res/xml/kbd_qwerty_rows_scandinavia.xml b/java/res/xml/kbd_rows_scandinavian.xml
similarity index 84%
rename from java/res/xml/kbd_qwerty_rows_scandinavia.xml
rename to java/res/xml/kbd_rows_scandinavian.xml
index be841dc..2d7dcde 100644
--- a/java/res/xml/kbd_qwerty_rows_scandinavia.xml
+++ b/java/res/xml/kbd_rows_scandinavian.xml
@@ -29,49 +29,49 @@
     >
         <Key
             latin:keyLabel="q"
-            latin:keyHintIcon="@drawable/key_hint_num1"
+            latin:keyHintLetter="1"
             latin:popupCharacters="@string/alternates_for_q"
             latin:keyWidth="8.75%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="w"
-            latin:keyHintIcon="@drawable/key_hint_num2"
+            latin:keyHintLetter="2"
             latin:popupCharacters="@string/alternates_for_w" />
         <Key
             latin:keyLabel="e"
-            latin:keyHintIcon="@drawable/key_hint_num3"
+            latin:keyHintLetter="3"
             latin:popupCharacters="@string/alternates_for_e" />
         <Key
             latin:keyLabel="r"
-            latin:keyHintIcon="@drawable/key_hint_num4"
+            latin:keyHintLetter="4"
             latin:popupCharacters="@string/alternates_for_r" />
         <Key
             latin:keyLabel="t"
-            latin:keyHintIcon="@drawable/key_hint_num5"
+            latin:keyHintLetter="5"
             latin:popupCharacters="@string/alternates_for_t" />
         <Key
             latin:keyLabel="y"
-            latin:keyHintIcon="@drawable/key_hint_num6"
+            latin:keyHintLetter="6"
             latin:popupCharacters="@string/alternates_for_y" />
         <Key
             latin:keyLabel="u"
-            latin:keyHintIcon="@drawable/key_hint_num7"
+            latin:keyHintLetter="7"
             latin:popupCharacters="@string/alternates_for_u" />
         <Key
             latin:keyLabel="i"
-            latin:keyHintIcon="@drawable/key_hint_num8"
+            latin:keyHintLetter="8"
             latin:popupCharacters="@string/alternates_for_i" />
         <Key
             latin:keyLabel="o"
-            latin:keyHintIcon="@drawable/key_hint_num9"
+            latin:keyHintLetter="9"
             latin:popupCharacters="@string/alternates_for_o" />
         <Key
             latin:keyLabel="p"
-            latin:keyHintIcon="@drawable/key_hint_num0"
+            latin:keyHintLetter="0"
             latin:popupCharacters="@string/alternates_for_p" />
         <Key
             latin:keyLabel="å"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -109,7 +109,7 @@
         <Key
             latin:keyLabel="@string/keylabel_for_scandinavia_row2_11"
             latin:popupCharacters="@string/alternates_for_scandinavia_row2_11"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <include
diff --git a/java/res/xml/kbd_sr_rows.xml b/java/res/xml/kbd_rows_serbian.xml
similarity index 83%
rename from java/res/xml/kbd_sr_rows.xml
rename to java/res/xml/kbd_rows_serbian.xml
index 8239f24..9f4b6a2 100644
--- a/java/res/xml/kbd_sr_rows.xml
+++ b/java/res/xml/kbd_rows_serbian.xml
@@ -29,48 +29,48 @@
     >
         <Key
             latin:keyLabel="љ"
-            latin:keyHintIcon="@drawable/key_hint_num1"
+            latin:keyHintLetter="1"
             latin:popupCharacters="1"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="њ"
-            latin:keyHintIcon="@drawable/key_hint_num2"
+            latin:keyHintLetter="2"
             latin:popupCharacters="2" />
         <Key
             latin:keyLabel="е"
-            latin:keyHintIcon="@drawable/key_hint_num3"
+            latin:keyHintLetter="3"
             latin:popupCharacters="3" />
         <Key
             latin:keyLabel="р"
-            latin:keyHintIcon="@drawable/key_hint_num4"
+            latin:keyHintLetter="4"
             latin:popupCharacters="4" />
         <Key
             latin:keyLabel="т"
-            latin:keyHintIcon="@drawable/key_hint_num5"
+            latin:keyHintLetter="5"
             latin:popupCharacters="5" />
         <Key
             latin:keyLabel="з"
-            latin:keyHintIcon="@drawable/key_hint_num6"
+            latin:keyHintLetter="6"
             latin:popupCharacters="6" />
         <Key
             latin:keyLabel="у"
-            latin:keyHintIcon="@drawable/key_hint_num7"
+            latin:keyHintLetter="7"
             latin:popupCharacters="7" />
         <Key
             latin:keyLabel="и"
-            latin:keyHintIcon="@drawable/key_hint_num8"
+            latin:keyHintLetter="8"
             latin:popupCharacters="8" />
         <Key
             latin:keyLabel="о"
-            latin:keyHintIcon="@drawable/key_hint_num9"
+            latin:keyHintLetter="9"
             latin:popupCharacters="9" />
         <Key
             latin:keyLabel="п"
-            latin:keyHintIcon="@drawable/key_hint_num0"
+            latin:keyHintLetter="0"
             latin:popupCharacters="0" />
         <Key
             latin:keyLabel="ш"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -99,7 +99,7 @@
             latin:keyLabel="ч" />
         <Key
             latin:keyLabel="ћ"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row
@@ -128,7 +128,7 @@
             latin:keyLabel="ж" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <include
diff --git a/java/res/xml/kbd_rows_spanish.xml b/java/res/xml/kbd_rows_spanish.xml
new file mode 100644
index 0000000..c5ead10
--- /dev/null
+++ b/java/res/xml/kbd_rows_spanish.xml
@@ -0,0 +1,64 @@
+<?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_qwerty_row1" />
+    <Row
+        latin:keyWidth="10%p"
+    >
+        <Key
+            latin:keyLabel="a"
+            latin:popupCharacters="@string/alternates_for_a"
+            latin:keyEdgeFlags="left" />
+        <Key
+            latin:keyLabel="s"
+            latin:popupCharacters="@string/alternates_for_s" />
+        <Key
+            latin:keyLabel="d"
+            latin:popupCharacters="@string/alternates_for_d" />
+        <Key
+            latin:keyLabel="f" />
+        <Key
+            latin:keyLabel="g"
+            latin:popupCharacters="@string/alternates_for_g" />
+        <Key
+            latin:keyLabel="h" />
+        <Key
+            latin:keyLabel="j" />
+        <Key
+            latin:keyLabel="k"
+            latin:popupCharacters="@string/alternates_for_k" />
+        <Key
+            latin:keyLabel="l"
+            latin:popupCharacters="@string/alternates_for_l" />
+        <Key
+            latin:keyLabel="ñ"
+            latin:keyEdgeFlags="right" />
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/kbd_qwerty_row3" />
+    <include
+        latin:keyboardLayout="@xml/kbd_qwerty_row4" />
+</merge>
diff --git a/java/res/xml/kbd_symbols.xml b/java/res/xml/kbd_symbols.xml
index 3c9098d..8e9124f 100644
--- a/java/res/xml/kbd_symbols.xml
+++ b/java/res/xml/kbd_symbols.xml
@@ -20,14 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
     latin:keyWidth="10%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -64,7 +57,7 @@
         <Key
             latin:keyLabel="0"
             latin:popupCharacters="ⁿ,∅"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
@@ -95,13 +88,14 @@
         <Key
             latin:keyLabel=")"
             latin:popupCharacters="],},&gt;"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
         <Key
             latin:keyStyle="altKeyStyle"
             latin:keyWidth="15%p"
+            latin:visualInsetsRight="1%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="!"
@@ -126,7 +120,8 @@
             latin:popupCharacters="¿" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
+            latin:visualInsetsLeft="1%p"
             latin:keyEdgeFlags="right" />
     </Row>
     <include latin:keyboardLayout="@xml/kbd_symbols_row4" />
diff --git a/java/res/xml/kbd_symbols_f1.xml b/java/res/xml/kbd_symbols_f1.xml
index 0fb7136..da5b5fc 100644
--- a/java/res/xml/kbd_symbols_f1.xml
+++ b/java/res/xml/kbd_symbols_f1.xml
@@ -23,16 +23,41 @@
 >
     <switch>
         <case
-            latin:hasVoiceKey="true"
+            latin:hasSettingsKey="true"
         >
-            <Key
-                latin:keyStyle="micKeyStyle" />
+            <switch>
+                <case
+                    latin:hasVoiceKey="true"
+                >
+                    <Key
+                        latin:keyStyle="shortcutKeyStyle"
+                        latin:keyWidth="9.2%p" />
+                </case>
+                <!-- latin:hasVoiceKey="false" -->
+                <default>
+                    <Key
+                        latin:keyLabel=","
+                        latin:keyWidth="9.2%p"
+                        latin:keyStyle="settingsPopupStyle" />
+                </default>
+            </switch>
         </case>
-        <!-- latin:hasVoiceKey="false" -->
+        <!-- hasSettingsKey="false" -->
         <default>
-            <Key
-                latin:keyLabel=","
-                latin:keyStyle="settingsPopupStyle" />
+            <switch>
+                <case
+                    latin:hasVoiceKey="true"
+                >
+                    <Key
+                        latin:keyStyle="shortcutKeyStyle" />
+                </case>
+                <!-- latin:hasVoiceKey="false" -->
+                <default>
+                    <Key
+                        latin:keyLabel=","
+                        latin:keyStyle="settingsPopupStyle" />
+                </default>
+            </switch>
         </default>
     </switch>
 </merge>
diff --git a/java/res/xml/kbd_symbols_row4.xml b/java/res/xml/kbd_symbols_row4.xml
index e92e58b..9323d4f 100644
--- a/java/res/xml/kbd_symbols_row4.xml
+++ b/java/res/xml/kbd_symbols_row4.xml
@@ -27,77 +27,62 @@
     >
         <switch>
             <case
-                latin:hasSettingsKey="false"
-            >
-                <Key
-                    latin:keyStyle="toAlphaKeyStyle"
-                    latin:keyWidth="20%p"
-                    latin:keyEdgeFlags="left" />
-                <include
-                    latin:keyboardLayout="@xml/kbd_symbols_f1" />
-                <Key
-                    latin:keyStyle="spaceKeyStyle"
-                    latin:keyWidth="40%p" />
-                <Key
-                    latin:keyLabel="."
-                    latin:keyHintIcon="@drawable/hint_popup_holo"
-                    latin:popupCharacters="@string/alternates_for_punctuation"
-                    latin:maxPopupKeyboardColumn="7"
-                    latin:keyStyle="functionalKeyStyle" />
-                <switch>
-                    <case
-                        latin:mode="im"
-                    >
-                        <Key
-                            latin:keyStyle="smileyKeyStyle"
-                            latin:keyWidth="0%p"
-                            latin:keyEdgeFlags="right" />
-                    </case>
-                    <default>
-                        <Key
-                            latin:keyStyle="returnKeyStyle"
-                            latin:keyWidth="0%p"
-                            latin:keyEdgeFlags="right" />
-                    </default>
-                </switch>
-            </case>
-            <case
                 latin:hasSettingsKey="true"
             >
                 <Key
                     latin:keyStyle="toAlphaKeyStyle"
-                    latin:keyWidth="15%p"
+                    latin:keyWidth="13.75%p"
                     latin:keyEdgeFlags="left" />
                 <Key
-                    latin:keyStyle="settingsKeyStyle" />
+                    latin:keyStyle="settingsKeyStyle"
+                    latin:keyWidth="9.2%p" />
+                <include
+                    latin:keyboardLayout="@xml/kbd_qwerty_f1" />
+                <Key
+                    latin:keyStyle="spaceKeyStyle"
+                    latin:keyWidth="35.83%p" />
+                <Key
+                    latin:keyLabel="."
+                    latin:keyLabelOption="popupHint"
+                    latin:popupCharacters="@string/alternates_for_punctuation"
+                    latin:keyWidth="9.2%p"
+                    latin:maxPopupKeyboardColumn="7"
+                    latin:keyStyle="functionalKeyStyle" />
+            </case>
+            <!-- latin:hasSettingsKey="false" -->
+            <default>
+                <Key
+                    latin:keyStyle="toAlphaKeyStyle"
+                    latin:keyWidth="15%p"
+                    latin:keyEdgeFlags="left" />
                 <include
                     latin:keyboardLayout="@xml/kbd_symbols_f1" />
                 <Key
                     latin:keyStyle="spaceKeyStyle"
-                    latin:keyWidth="30%p" />
+                    latin:keyWidth="50%p" />
                 <Key
                     latin:keyLabel="."
-                    latin:keyHintIcon="@drawable/hint_popup_holo"
+                    latin:keyLabelOption="popupHint"
                     latin:popupCharacters="@string/alternates_for_punctuation"
                     latin:maxPopupKeyboardColumn="7"
                     latin:keyStyle="functionalKeyStyle" />
-                <switch>
-                    <case
-                        latin:mode="im"
-                    >
-                        <Key
-                            latin:keyStyle="smileyKeyStyle"
-                            latin:keyWidth="0%p"
-                            latin:keyEdgeFlags="right" />
-                    </case>
-                    <default>
-                        <Key
-                            latin:keyStyle="returnKeyStyle"
-                            latin:keyWidth="0%p"
-                            latin:keyEdgeFlags="right" />
-                    </default>
-                </switch>
+            </default>
+        </switch>
+        <switch>
+            <case
+                latin:mode="im"
+            >
+                <Key
+                    latin:keyStyle="smileyKeyStyle"
+                    latin:keyWidth="fillRight"
+                    latin:keyEdgeFlags="right" />
             </case>
+            <default>
+                <Key
+                    latin:keyStyle="returnKeyStyle"
+                    latin:keyWidth="fillRight"
+                    latin:keyEdgeFlags="right" />
+            </default>
         </switch>
     </Row>
 </merge>
diff --git a/java/res/xml/kbd_symbols_shift.xml b/java/res/xml/kbd_symbols_shift.xml
index 0f9ea3b..f22d45a 100644
--- a/java/res/xml/kbd_symbols_shift.xml
+++ b/java/res/xml/kbd_symbols_shift.xml
@@ -20,14 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardHeight="@dimen/keyboardHeight"
-    latin:maxKeyboardHeight="50%p"
-    latin:rowHeight="25%p"
     latin:keyWidth="10%p"
-    latin:horizontalGap="@dimen/key_horizontal_gap"
-    latin:verticalGap="@dimen/key_bottom_gap"
-    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
-    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -62,7 +55,7 @@
             latin:keyLabel="{" />
         <Key
             latin:keyLabel="}"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
@@ -94,13 +87,14 @@
             latin:keyLabel="[" />
         <Key
             latin:keyLabel="]"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="15%p"
+            latin:visualInsetsRight="1%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyStyle="nonPasswordSymbolKeyStyle"
@@ -125,7 +119,8 @@
             latin:popupCharacters="≥,»,›" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="0%p"
+            latin:keyWidth="fillRight"
+            latin:visualInsetsLeft="1%p"
             latin:keyEdgeFlags="right" />
     </Row>
     <include latin:keyboardLayout="@xml/kbd_symbols_shift_row4" />
diff --git a/java/res/xml/kbd_symbols_shift_row4.xml b/java/res/xml/kbd_symbols_shift_row4.xml
index 3ed76ea..90a96e4 100644
--- a/java/res/xml/kbd_symbols_shift_row4.xml
+++ b/java/res/xml/kbd_symbols_shift_row4.xml
@@ -27,73 +27,61 @@
     >
         <switch>
             <case
-                latin:hasSettingsKey="false"
+                latin:hasSettingsKey="true"
             >
                 <Key
                     latin:keyStyle="toAlphaKeyStyle"
-                    latin:keyWidth="20%p"
+                    latin:keyWidth="13.75%p"
                     latin:keyEdgeFlags="left" />
                 <Key
+                    latin:keyStyle="settingsKeyStyle"
+                    latin:keyWidth="9.2%p" />
+                <Key
                     latin:keyLabel="„"
+                    latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛"
+                    latin:keyWidth="9.2%p"
                     latin:keyStyle="nonPasswordFunctionalKeyStyle" />
                 <Key
                     latin:keyStyle="spaceKeyStyle"
-                    latin:keyWidth="40%p" />
+                    latin:keyWidth="35.83%p" />
                 <Key
                     latin:keyLabel="…"
+                    latin:keyWidth="9.2%p"
                     latin:keyStyle="nonPasswordFunctionalKeyStyle" />
-                <switch>
-                    <case
-                        latin:mode="im"
-                    >
-                        <Key
-                            latin:keyStyle="smileyKeyStyle"
-                            latin:keyWidth="0%p"
-                            latin:keyEdgeFlags="right" />
-                    </case>
-                    <default>
-                        <Key
-                            latin:keyStyle="returnKeyStyle"
-                            latin:keyWidth="0%p"
-                            latin:keyEdgeFlags="right" />
-                    </default>
-                </switch>
             </case>
-            <case
-                latin:hasSettingsKey="true"
-            >
+            <!-- latin:hasSettingsKey="false" -->
+            <default>
                 <Key
                     latin:keyStyle="toAlphaKeyStyle"
                     latin:keyWidth="15%p"
                     latin:keyEdgeFlags="left" />
                 <Key
-                    latin:keyStyle="settingsKeyStyle" />
-                <Key
                     latin:keyLabel="„"
+                    latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛"
                     latin:keyStyle="nonPasswordFunctionalKeyStyle" />
                 <Key
                     latin:keyStyle="spaceKeyStyle"
-                    latin:keyWidth="30%p" />
+                    latin:keyWidth="50%p" />
                 <Key
                     latin:keyLabel="…"
                     latin:keyStyle="nonPasswordFunctionalKeyStyle" />
-                <switch>
-                    <case
-                        latin:mode="im"
-                    >
-                        <Key
-                            latin:keyStyle="smileyKeyStyle"
-                            latin:keyWidth="0%p"
-                            latin:keyEdgeFlags="right" />
-                    </case>
-                    <default>
-                        <Key
-                            latin:keyStyle="returnKeyStyle"
-                            latin:keyWidth="0%p"
-                            latin:keyEdgeFlags="right" />
-                    </default>
-                </switch>
+            </default>
+        </switch>
+        <switch>
+            <case
+                latin:mode="im"
+            >
+                <Key
+                    latin:keyStyle="smileyKeyStyle"
+                    latin:keyWidth="fillRight"
+                    latin:keyEdgeFlags="right" />
             </case>
+            <default>
+                <Key
+                    latin:keyStyle="returnKeyStyle"
+                    latin:keyWidth="fillRight"
+                    latin:keyEdgeFlags="right" />
+            </default>
         </switch>
     </Row>
 </merge>
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 8dec7ab..c9c9203 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -20,10 +20,11 @@
 <!-- The attributes in this XML file provide configuration information -->
 <!-- for the Input Method Manager. -->
 
-<!-- Keyboard: en_US, en_GB, cs, da, de, es, es_US, fr, fr_CA, fr_CH, it, nb, nl, sr, sv -->
+<!-- Keyboard: en_US, en_GB, ar, cs, da, de, es, es_US, fi, fr, fr_CA, fr_CH, hr, hu, it, iw, nb, nl, pl, pt, ru, sr, sv, tr -->
 <!-- Voice: af, cs, da, de, en, es, fr, it, ja, ko, nl, pl, pt, ru, tr, yue, zh, zu -->
 <!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. -->
 <!-- TODO: use <lang>_mic icon instead of a common mic icon. -->
+<!-- TODO: remove all comment outed voice subtypes -->
 <!-- If IME doesn't have an applicable subtype, the first subtype will be used as a default
      subtype.-->
 <input-method xmlns:android="http://schemas.android.com/apk/res/android"
@@ -34,28 +35,34 @@
             android:imeSubtypeLocale="en_US"
             android:imeSubtypeMode="keyboard"
     />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_en_voice"
-            android:imeSubtypeLocale="en"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
-    />
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_en_voice" -->
+<!--             android:imeSubtypeLocale="en" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_mode_en_GB_keyboard"
             android:imeSubtypeLocale="en_GB"
             android:imeSubtypeMode="keyboard"
     />
+    <!-- The file for Arabic layout is an alpha version. It needs to be run through UX. -->
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_mode_ar_keyboard"
+            android:imeSubtypeLocale="ar"
+            android:imeSubtypeMode="keyboard"
+    />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_mode_cs_keyboard"
             android:imeSubtypeLocale="cs"
             android:imeSubtypeMode="keyboard"
     />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_cs_voice"
-            android:imeSubtypeLocale="cs"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
-    />
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_cs_voice" -->
+<!--             android:imeSubtypeLocale="cs" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_mode_da_keyboard"
             android:imeSubtypeLocale="da"
@@ -65,36 +72,45 @@
             android:label="@string/subtype_mode_de_keyboard"
             android:imeSubtypeLocale="de"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="requiresGermanUmlautProcessing"
     />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_de_voice"
-            android:imeSubtypeLocale="de"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_mode_de_qwerty_keyboard"
+            android:imeSubtypeLocale="de_ZZ"
+            android:imeSubtypeMode="keyboard"
     />
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_de_voice" -->
+<!--             android:imeSubtypeLocale="de" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_mode_es_keyboard"
             android:imeSubtypeLocale="es"
             android:imeSubtypeMode="keyboard"
     />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_es_voice"
-            android:imeSubtypeLocale="es"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_es_voice" -->
+<!--             android:imeSubtypeLocale="es" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_mode_fi_keyboard"
+            android:imeSubtypeLocale="fi"
+            android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_mode_fr_keyboard"
             android:imeSubtypeLocale="fr"
             android:imeSubtypeMode="keyboard"
     />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_fr_voice"
-            android:imeSubtypeLocale="fr"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
-    />
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_fr_voice" -->
+<!--             android:imeSubtypeLocale="fr" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_mode_fr_CA_keyboard"
             android:imeSubtypeLocale="fr_CA"
@@ -106,16 +122,33 @@
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_mode_hr_keyboard"
+            android:imeSubtypeLocale="hr"
+            android:imeSubtypeMode="keyboard"
+    />
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_mode_hu_keyboard"
+            android:imeSubtypeLocale="hu"
+            android:imeSubtypeMode="keyboard"
+    />
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_mode_it_keyboard"
             android:imeSubtypeLocale="it"
             android:imeSubtypeMode="keyboard"
     />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_it_voice"
-            android:imeSubtypeLocale="it"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
+    <!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. -->
+    <!-- The file for Hebrew layout is an alpha version. It needs to be run through UX. -->
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_mode_iw_keyboard"
+            android:imeSubtypeLocale="iw"
+            android:imeSubtypeMode="keyboard"
     />
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_it_voice" -->
+<!--             android:imeSubtypeLocale="it" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_mode_nb_keyboard"
             android:imeSubtypeLocale="nb"
@@ -126,11 +159,21 @@
             android:imeSubtypeLocale="nl"
             android:imeSubtypeMode="keyboard"
     />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_nl_voice"
-            android:imeSubtypeLocale="nl"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_nl_voice" -->
+<!--             android:imeSubtypeLocale="nl" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_mode_pl_keyboard"
+            android:imeSubtypeLocale="pl"
+            android:imeSubtypeMode="keyboard"
+    />
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_mode_pt_keyboard"
+            android:imeSubtypeLocale="pt"
+            android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_mode_ru_keyboard"
@@ -147,64 +190,69 @@
             android:imeSubtypeLocale="sv"
             android:imeSubtypeMode="keyboard"
     />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_af_voice"
-            android:imeSubtypeLocale="af"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
-    />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_ja_voice"
-            android:imeSubtypeLocale="ja"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
-    />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_ko_voice"
-            android:imeSubtypeLocale="ko"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
-    />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_pl_voice"
-            android:imeSubtypeLocale="pl"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
-    />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_pt_voice"
-            android:imeSubtypeLocale="pt"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
-    />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_ru_voice"
-            android:imeSubtypeLocale="ru"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
-    />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_tr_voice"
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_mode_tr_keyboard"
             android:imeSubtypeLocale="tr"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
+            android:imeSubtypeMode="keyboard"
     />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_yue_voice"
-            android:imeSubtypeLocale="yue"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
-    />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_zh_voice"
-            android:imeSubtypeLocale="zh"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
-    />
-    <subtype android:icon="@drawable/ic_subtype_mic"
-            android:label="@string/subtype_mode_zu_voice"
-            android:imeSubtypeLocale="zu"
-            android:imeSubtypeMode="voice"
-            android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity"
-    />
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_af_voice" -->
+<!--             android:imeSubtypeLocale="af" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_ja_voice" -->
+<!--             android:imeSubtypeLocale="ja" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_ko_voice" -->
+<!--             android:imeSubtypeLocale="ko" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_pl_voice" -->
+<!--             android:imeSubtypeLocale="pl" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_pt_voice" -->
+<!--             android:imeSubtypeLocale="pt" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_ru_voice" -->
+<!--             android:imeSubtypeLocale="ru" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_tr_voice" -->
+<!--             android:imeSubtypeLocale="tr" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_yue_voice" -->
+<!--             android:imeSubtypeLocale="yue" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_zh_voice" -->
+<!--             android:imeSubtypeLocale="zh" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
+<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
+<!--             android:label="@string/subtype_mode_zu_voice" -->
+<!--             android:imeSubtypeLocale="zu" -->
+<!--             android:imeSubtypeMode="voice" -->
+<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
+<!--     /> -->
 </input-method>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index d031415..5f4a27d 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -14,86 +14,66 @@
      limitations under the License.
 -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-        android:title="@string/english_ime_settings"
-        android:key="english_ime_settings">
-
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:title="@string/english_ime_settings"
+    android:key="english_ime_settings">
     <PreferenceCategory
-            android:title="@string/general_category"
-            android:key="general_settings">
-
+        android:title="@string/general_category"
+        android:key="general_settings">
         <CheckBoxPreference
-                android:key="auto_cap"
-                android:title="@string/auto_cap"
-                android:persistent="true"
-                android:defaultValue="true"
-                />
-
+            android:key="auto_cap"
+            android:title="@string/auto_cap"
+            android:persistent="true"
+            android:defaultValue="true" />
         <CheckBoxPreference
-                android:key="vibrate_on"
-                android:title="@string/vibrate_on_keypress"
-                android:persistent="true"
-                />
-
+            android:key="vibrate_on"
+            android:title="@string/vibrate_on_keypress"
+            android:persistent="true" />
         <CheckBoxPreference
-                android:key="sound_on"
-                android:title="@string/sound_on_keypress"
-                android:defaultValue="@bool/config_default_sound_enabled"
-                android:persistent="true"
-                />
-
+            android:key="sound_on"
+            android:title="@string/sound_on_keypress"
+            android:defaultValue="@bool/config_default_sound_enabled"
+            android:persistent="true" />
         <CheckBoxPreference
-                android:key="popup_on"
-                android:title="@string/popup_on_keypress"
-                android:persistent="true"
-                android:defaultValue="@bool/config_default_popup_preview"
-                />
-
+            android:key="popup_on"
+            android:title="@string/popup_on_keypress"
+            android:persistent="true"
+            android:defaultValue="@bool/config_default_popup_preview" />
         <CheckBoxPreference
-                android:key="recorrection_enabled"
-                android:title="@string/prefs_enable_recorrection"
-                android:summary="@string/prefs_enable_recorrection_summary"
-                android:persistent="true"
-                android:defaultValue="@bool/config_default_recorrection_enabled"
-                />
-
+            android:key="recorrection_enabled"
+            android:title="@string/prefs_enable_recorrection"
+            android:summary="@string/prefs_enable_recorrection_summary"
+            android:persistent="true"
+            android:defaultValue="@bool/config_default_recorrection_enabled" />
         <ListPreference
-                android:key="settings_key"
-                android:title="@string/prefs_settings_key"
-                android:persistent="true"
-                android:entryValues="@array/settings_key_modes_values"
-                android:entries="@array/settings_key_modes"
-                android:defaultValue="@string/settings_key_mode_auto"
-                />
-
+            android:key="settings_key"
+            android:title="@string/prefs_settings_key"
+            android:persistent="true"
+            android:entryValues="@array/settings_key_modes_values"
+            android:entries="@array/settings_key_modes"
+            android:defaultValue="@string/settings_key_mode_auto" />
         <ListPreference
-                android:key="voice_mode"
-                android:title="@string/voice_input"
-                android:persistent="true"
-                android:entryValues="@array/voice_input_modes_values"
-                android:entries="@array/voice_input_modes"
-                android:defaultValue="@string/voice_mode_main"
-                />
-
+            android:key="voice_mode"
+            android:title="@string/voice_input"
+            android:persistent="true"
+            android:entryValues="@array/voice_input_modes_values"
+            android:entries="@array/voice_input_modes"
+            android:defaultValue="@string/voice_mode_main" />
         <PreferenceScreen
-                android:key="subtype_settings"
-                android:title="@string/language_selection_title"
-                android:summary="@string/language_selection_summary" />
-
+            android:key="subtype_settings"
+            android:title="@string/language_selection_title"
+            android:summary="@string/language_selection_summary" />
     </PreferenceCategory>
-
     <PreferenceCategory
-            android:title="@string/prediction_category"
-            android:key="prediction_settings">
-
+        android:title="@string/correction_category"
+        android:key="correction_settings">
         <CheckBoxPreference
             android:key="quick_fixes"
             android:title="@string/quick_fixes"
             android:summary="@string/quick_fixes_summary"
             android:persistent="true"
-            android:defaultValue="true"
-            />
-
+            android:defaultValue="true" />
         <ListPreference
             android:key="auto_correction_threshold"
             android:title="@string/auto_correction"
@@ -101,9 +81,7 @@
             android:persistent="true"
             android:entryValues="@array/auto_correction_threshold_mode_indexes"
             android:entries="@array/auto_correction_threshold_modes"
-            android:defaultValue="@string/auto_correction_threshold_mode_index_modest"
-            />
-
+            android:defaultValue="@string/auto_correction_threshold_mode_index_modest" />
         <ListPreference
             android:key="show_suggestions_setting"
             android:summary="@string/prefs_show_suggestions_summary"
@@ -111,23 +89,68 @@
             android:persistent="true"
             android:entryValues="@array/prefs_suggestion_visibility_values"
             android:entries="@array/prefs_suggestion_visibilities"
-            android:defaultValue="@string/prefs_suggestion_visibility_default_value"
-            />
-
+            android:defaultValue="@string/prefs_suggestion_visibility_default_value" />
+    </PreferenceCategory>
+    <PreferenceCategory
+        android:title="@string/ngram_category"
+        android:key="ngram_settings">
         <CheckBoxPreference
             android:key="bigram_suggestion"
             android:title="@string/bigram_suggestion"
             android:summary="@string/bigram_suggestion_summary"
             android:persistent="true"
-            android:defaultValue="true"
-            />
-    </PreferenceCategory>
-
-    <CheckBoxPreference
-            android:key="usability_study_mode"
-            android:title="@string/prefs_usability_study_mode"
+            android:defaultValue="true" />
+        <CheckBoxPreference
+            android:key="bigram_prediction"
+            android:dependency="bigram_suggestion"
+            android:title="@string/bigram_prediction"
+            android:summary="@string/bigram_prediction_summary"
             android:persistent="true"
-            android:defaultValue="false"
-            />
-
+            android:defaultValue="false" />
+    </PreferenceCategory>
+    <PreferenceCategory
+        android:title="@string/misc_category"
+        android:key="misc_settings">
+      <CheckBoxPreference
+          android:key="usability_study_mode"
+          android:title="@string/prefs_usability_study_mode"
+          android:persistent="true"
+          android:defaultValue="false" />
+      <CheckBoxPreference
+          android:key="enable_logging"
+          android:title="@string/prefs_enable_log"
+          android:summary="@string/prefs_description_log"
+          android:persistent="true"
+          android:defaultValue="true" />
+      <ListPreference
+          android:key="pref_keyboard_layout_20100902"
+          android:title="@string/keyboard_layout"
+          android:persistent="true"
+          android:entryValues="@array/keyboard_layout_modes_values"
+          android:entries="@array/keyboard_layout_modes"
+          android:defaultValue="@string/config_default_keyboard_theme_id" />
+    </PreferenceCategory>
+    <PreferenceScreen
+          android:key="pref_advanced_settings"
+          android:title="@string/advanced_settings"
+          android:summary="@string/advanced_settings_summary">
+          <!-- Values for popup dismiss delay are added programatically -->
+          <ListPreference
+              android:key="pref_key_preview_popup_dismiss_delay"
+              android:title="@string/key_preview_popup_dismiss_delay" />
+          <CheckBoxPreference
+             android:key="pref_key_use_contacts_dict"
+             android:title="@string/use_contacts_dict"
+             android:summary="@string/use_contacts_dict_summary"
+             android:persistent="true"
+             android:defaultValue="true" />
+    </PreferenceScreen>
+    <!-- <Preference
+        android:title="Debug Settings"
+        android:key="debug_settings">
+        <intent
+            android:action="android.intent.action.MAIN"
+            android:targetPackage="com.android.inputmethod.latin"
+            android:targetClass="com.android.inputmethod.latin.DebugSettings" />
+    </Preference>-->
 </PreferenceScreen>
diff --git a/java/res/xml/prefs_for_debug.xml b/java/res/xml/prefs_for_debug.xml
index 2dad171..477461d 100644
--- a/java/res/xml/prefs_for_debug.xml
+++ b/java/res/xml/prefs_for_debug.xml
@@ -36,6 +36,13 @@
             />
 
     <CheckBoxPreference
+            android:key="use_spacebar_language_switch"
+            android:title="@string/prefs_use_spacebar_language_switch"
+            android:persistent="true"
+            android:defaultValue="false"
+            />
+
+    <CheckBoxPreference
             android:key="debug_mode"
             android:title="@string/prefs_debug_mode"
             android:persistent="true"
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
new file mode 100644
index 0000000..ae614b7
--- /dev/null
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
@@ -0,0 +1,133 @@
+/*
+ * 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.accessibility;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.inputmethodservice.InputMethodService;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.inputmethod.compat.AccessibilityEventCompatUtils;
+import com.android.inputmethod.compat.AccessibilityManagerCompatWrapper;
+import com.android.inputmethod.compat.MotionEventCompatUtils;
+
+public class AccessibilityUtils {
+    private static final String TAG = AccessibilityUtils.class.getSimpleName();
+    private static final String CLASS = AccessibilityUtils.class.getClass().getName();
+    private static final String PACKAGE = AccessibilityUtils.class.getClass().getPackage()
+            .getName();
+
+    private static final AccessibilityUtils sInstance = new AccessibilityUtils();
+
+    private AccessibilityManager mAccessibilityManager;
+    private AccessibilityManagerCompatWrapper mCompatManager;
+
+    /*
+     * Setting this constant to {@code false} will disable all keyboard
+     * accessibility code, regardless of whether Accessibility is turned on in
+     * the system settings. It should ONLY be used in the event of an emergency.
+     */
+    private static final boolean ENABLE_ACCESSIBILITY = true;
+
+    public static void init(InputMethodService inputMethod, SharedPreferences prefs) {
+        if (!ENABLE_ACCESSIBILITY)
+            return;
+
+        // These only need to be initialized if the kill switch is off.
+        sInstance.initInternal(inputMethod, prefs);
+        KeyCodeDescriptionMapper.init(inputMethod, prefs);
+        AccessibleInputMethodServiceProxy.init(inputMethod, prefs);
+        AccessibleKeyboardViewProxy.init(inputMethod, prefs);
+    }
+
+    public static AccessibilityUtils getInstance() {
+        return sInstance;
+    }
+
+    private AccessibilityUtils() {
+        // This class is not publicly instantiable.
+    }
+
+    private void initInternal(Context context, SharedPreferences prefs) {
+        mAccessibilityManager = (AccessibilityManager) context
+                .getSystemService(Context.ACCESSIBILITY_SERVICE);
+        mCompatManager = new AccessibilityManagerCompatWrapper(mAccessibilityManager);
+    }
+
+    /**
+     * Returns {@code true} if touch exploration is enabled. Currently, this
+     * means that the kill switch is off, the device supports touch exploration,
+     * and a spoken feedback service is turned on.
+     *
+     * @return {@code true} if touch exploration is enabled.
+     */
+    public boolean isTouchExplorationEnabled() {
+        return ENABLE_ACCESSIBILITY
+                && AccessibilityEventCompatUtils.supportsTouchExploration()
+                && mAccessibilityManager.isEnabled()
+                && !mCompatManager.getEnabledAccessibilityServiceList(
+                        AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty();
+    }
+
+    /**
+     * Returns {@true} if the provided event is a touch exploration (e.g. hover)
+     * event. This is used to determine whether the event should be processed by
+     * the touch exploration code within the keyboard.
+     *
+     * @param event The event to check.
+     * @return {@true} is the event is a touch exploration event
+     */
+    public boolean isTouchExplorationEvent(MotionEvent event) {
+        final int action = event.getAction();
+
+        return action == MotionEventCompatUtils.ACTION_HOVER_ENTER
+                || action == MotionEventCompatUtils.ACTION_HOVER_EXIT
+                || action == MotionEventCompatUtils.ACTION_HOVER_MOVE;
+    }
+
+    /**
+     * Sends the specified text to the {@link AccessibilityManager} to be
+     * spoken.
+     *
+     * @param text the text to speak
+     */
+    public void speak(CharSequence text) {
+        if (!mAccessibilityManager.isEnabled()) {
+            Log.e(TAG, "Attempted to speak when accessibility was disabled!");
+            return;
+        }
+
+        // The following is a hack to avoid using the heavy-weight TextToSpeech
+        // class. Instead, we're just forcing a fake AccessibilityEvent into
+        // the screen reader to make it speak.
+        final AccessibilityEvent event = AccessibilityEvent
+                .obtain(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER);
+
+        event.setPackageName(PACKAGE);
+        event.setClassName(CLASS);
+        event.setEventTime(SystemClock.uptimeMillis());
+        event.setEnabled(true);
+        event.getText().add(text);
+
+        mAccessibilityManager.sendAccessibilityEvent(event);
+    }
+}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
new file mode 100644
index 0000000..043266c
--- /dev/null
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
@@ -0,0 +1,129 @@
+/*
+ * 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.accessibility;
+
+import android.content.SharedPreferences;
+import android.inputmethodservice.InputMethodService;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.text.TextUtils;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+
+import com.android.inputmethod.latin.R;
+
+public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActionListener {
+    private static final AccessibleInputMethodServiceProxy sInstance =
+            new AccessibleInputMethodServiceProxy();
+
+    /*
+     * Delay for the handler event that's fired when Accessibility is on and the
+     * user hovers outside of any valid keys. This is used to let the user know
+     * that if they lift their finger, nothing will be typed.
+     */
+    private static final long DELAY_NO_HOVER_SELECTION = 250;
+
+    private InputMethodService mInputMethod;
+
+    private AccessibilityHandler mAccessibilityHandler;
+
+    private class AccessibilityHandler extends Handler {
+        private static final int MSG_NO_HOVER_SELECTION = 0;
+
+        public AccessibilityHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case MSG_NO_HOVER_SELECTION:
+                notifyNoHoverSelection();
+                break;
+            }
+        }
+
+        public void postNoHoverSelection() {
+            removeMessages(MSG_NO_HOVER_SELECTION);
+            sendEmptyMessageDelayed(MSG_NO_HOVER_SELECTION, DELAY_NO_HOVER_SELECTION);
+        }
+
+        public void cancelNoHoverSelection() {
+            removeMessages(MSG_NO_HOVER_SELECTION);
+        }
+    }
+
+    public static void init(InputMethodService inputMethod, SharedPreferences prefs) {
+        sInstance.initInternal(inputMethod, prefs);
+    }
+
+    public static AccessibleInputMethodServiceProxy getInstance() {
+        return sInstance;
+    }
+
+    private AccessibleInputMethodServiceProxy() {
+        // Not publicly instantiable.
+    }
+
+    private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) {
+        mInputMethod = inputMethod;
+        mAccessibilityHandler = new AccessibilityHandler(inputMethod.getMainLooper());
+    }
+
+    /**
+     * If touch exploration is enabled, cancels the event sent by
+     * {@link AccessibleInputMethodServiceProxy#onHoverExit(int)} because the
+     * user is currently hovering above a key.
+     */
+    @Override
+    public void onHoverEnter(int primaryCode) {
+        mAccessibilityHandler.cancelNoHoverSelection();
+    }
+
+    /**
+     * If touch exploration is enabled, sends a delayed event to notify the user
+     * that they are not currently hovering above a key.
+     */
+    @Override
+    public void onHoverExit(int primaryCode) {
+        mAccessibilityHandler.postNoHoverSelection();
+    }
+
+    /**
+     * When Accessibility is turned on, notifies the user that they are not
+     * currently hovering above a key. By default this will speak the currently
+     * entered text.
+     */
+    private void notifyNoHoverSelection() {
+        final ExtractedText extracted = mInputMethod.getCurrentInputConnection().getExtractedText(
+                new ExtractedTextRequest(), 0);
+
+        if (extracted == null)
+            return;
+
+        final CharSequence text;
+
+        if (TextUtils.isEmpty(extracted.text)) {
+            text = mInputMethod.getString(R.string.spoken_no_text_entered);
+        } else {
+            text = mInputMethod.getString(R.string.spoken_current_text_is, extracted.text);
+        }
+
+        AccessibilityUtils.getInstance().speak(text);
+    }
+}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java
new file mode 100644
index 0000000..12c59d0
--- /dev/null
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java
@@ -0,0 +1,37 @@
+/*
+ * 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.accessibility;
+
+public interface AccessibleKeyboardActionListener {
+    /**
+     * Called when the user hovers inside a key. This is sent only when
+     * Accessibility is turned on. For keys that repeat, this is only called
+     * once.
+     *
+     * @param primaryCode the code of the key that was hovered over
+     */
+    public void onHoverEnter(int primaryCode);
+
+    /**
+     * Called when the user hovers outside a key. This is sent only when
+     * Accessibility is turned on. For keys that repeat, this is only called
+     * once.
+     *
+     * @param primaryCode the code of the key that was hovered over
+     */
+    public void onHoverExit(int primaryCode);
+}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
new file mode 100644
index 0000000..96f7fc9
--- /dev/null
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -0,0 +1,201 @@
+/*
+ * 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.accessibility;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.inputmethod.compat.AccessibilityEventCompatUtils;
+import com.android.inputmethod.compat.MotionEventCompatUtils;
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.KeyDetector;
+import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.PointerTracker;
+
+public class AccessibleKeyboardViewProxy {
+    private static final String TAG = AccessibleKeyboardViewProxy.class.getSimpleName();
+    private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy();
+
+    // Delay in milliseconds between key press DOWN and UP events
+    private static final long DELAY_KEY_PRESS = 10;
+
+    private int mScaledEdgeSlop;
+    private KeyboardView mView;
+    private AccessibleKeyboardActionListener mListener;
+
+    private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY;
+    private int mLastX = -1;
+    private int mLastY = -1;
+
+    public static void init(Context context, SharedPreferences prefs) {
+        sInstance.initInternal(context, prefs);
+        sInstance.mListener = AccessibleInputMethodServiceProxy.getInstance();
+    }
+
+    public static AccessibleKeyboardViewProxy getInstance() {
+        return sInstance;
+    }
+
+    public static void setView(KeyboardView view) {
+        sInstance.mView = view;
+    }
+
+    private AccessibleKeyboardViewProxy() {
+        // Not publicly instantiable.
+    }
+
+    private void initInternal(Context context, SharedPreferences prefs) {
+        final Paint paint = new Paint();
+        paint.setTextAlign(Paint.Align.LEFT);
+        paint.setTextSize(14.0f);
+        paint.setAntiAlias(true);
+        paint.setColor(Color.YELLOW);
+
+        mScaledEdgeSlop = ViewConfiguration.get(context).getScaledEdgeSlop();
+    }
+
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event,
+            PointerTracker tracker) {
+        if (mView == null) {
+            Log.e(TAG, "No keyboard view set!");
+            return false;
+        }
+
+        switch (event.getEventType()) {
+        case AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER:
+            final Key key = tracker.getKey(mLastHoverKeyIndex);
+
+            if (key == null)
+                break;
+
+            final CharSequence description = KeyCodeDescriptionMapper.getInstance()
+                    .getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key);
+
+            if (description == null)
+                return false;
+
+            event.getText().add(description);
+
+            break;
+        }
+
+        return true;
+    }
+
+    /**
+     * Receives hover events when accessibility is turned on in API > 11. In
+     * earlier API levels, events are manually routed from onTouchEvent.
+     *
+     * @param event The hover event.
+     * @return {@code true} if the event is handled
+     */
+    public boolean onHoverEvent(MotionEvent event, PointerTracker tracker) {
+        return onTouchExplorationEvent(event, tracker);
+    }
+
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        // Since touch exploration translates hover double-tap to a regular
+        // single-tap, we're going to drop non-touch exploration events.
+        if (!AccessibilityUtils.getInstance().isTouchExplorationEvent(event))
+            return true;
+
+        return false;
+    }
+
+    /**
+     * Handles touch exploration events when Accessibility is turned on.
+     *
+     * @param event The touch exploration hover event.
+     * @return {@code true} if the event was handled
+     */
+    private boolean onTouchExplorationEvent(MotionEvent event, PointerTracker tracker) {
+        final int x = (int) event.getX();
+        final int y = (int) event.getY();
+
+        switch (event.getAction()) {
+        case MotionEventCompatUtils.ACTION_HOVER_ENTER:
+        case MotionEventCompatUtils.ACTION_HOVER_MOVE:
+            final int keyIndex = tracker.getKeyIndexOn(x, y);
+
+            if (keyIndex != mLastHoverKeyIndex) {
+                fireKeyHoverEvent(tracker, mLastHoverKeyIndex, false);
+                mLastHoverKeyIndex = keyIndex;
+                mLastX = x;
+                mLastY = y;
+                fireKeyHoverEvent(tracker, mLastHoverKeyIndex, true);
+            }
+
+            return true;
+        case MotionEventCompatUtils.ACTION_HOVER_EXIT:
+            final int width = mView.getWidth();
+            final int height = mView.getHeight();
+
+            if (x < mScaledEdgeSlop || y < mScaledEdgeSlop || x >= (width - mScaledEdgeSlop)
+                    || y >= (height - mScaledEdgeSlop)) {
+                fireKeyHoverEvent(tracker, mLastHoverKeyIndex, false);
+                mLastHoverKeyIndex = KeyDetector.NOT_A_KEY;
+                mLastX = -1;
+                mLastY = -1;
+            } else if (mLastHoverKeyIndex != KeyDetector.NOT_A_KEY) {
+                fireKeyPressEvent(tracker, mLastX, mLastY, event.getEventTime());
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    private void fireKeyHoverEvent(PointerTracker tracker, int keyIndex, boolean entering) {
+        if (mListener == null) {
+            Log.e(TAG, "No accessible keyboard action listener set!");
+            return;
+        }
+
+        if (mView == null) {
+            Log.e(TAG, "No keyboard view set!");
+            return;
+        }
+
+        if (keyIndex == KeyDetector.NOT_A_KEY)
+            return;
+
+        final Key key = tracker.getKey(keyIndex);
+
+        if (key == null)
+            return;
+
+        if (entering) {
+            mListener.onHoverEnter(key.mCode);
+            mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER);
+        } else {
+            mListener.onHoverExit(key.mCode);
+            mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_EXIT);
+        }
+    }
+
+    private void fireKeyPressEvent(PointerTracker tracker, int x, int y, long eventTime) {
+        tracker.onDownEvent(x, y, eventTime, null);
+        tracker.onUpEvent(x, y, eventTime + DELAY_KEY_PRESS, null);
+    }
+}
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
new file mode 100644
index 0000000..154f4af
--- /dev/null
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -0,0 +1,226 @@
+/*
+ * 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.accessibility;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.text.TextUtils;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.latin.R;
+
+import java.util.HashMap;
+
+public class KeyCodeDescriptionMapper {
+    private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper();
+
+    // Map of key labels to spoken description resource IDs
+    private final HashMap<CharSequence, Integer> mKeyLabelMap;
+
+    // Map of key codes to spoken description resource IDs
+    private final HashMap<Integer, Integer> mKeyCodeMap;
+
+    // Map of shifted key codes to spoken description resource IDs
+    private final HashMap<Integer, Integer> mShiftedKeyCodeMap;
+
+    // Map of shift-locked key codes to spoken description resource IDs
+    private final HashMap<Integer, Integer> mShiftLockedKeyCodeMap;
+
+    public static void init(Context context, SharedPreferences prefs) {
+        sInstance.initInternal(context, prefs);
+    }
+
+    public static KeyCodeDescriptionMapper getInstance() {
+        return sInstance;
+    }
+
+    private KeyCodeDescriptionMapper() {
+        mKeyLabelMap = new HashMap<CharSequence, Integer>();
+        mKeyCodeMap = new HashMap<Integer, Integer>();
+        mShiftedKeyCodeMap = new HashMap<Integer, Integer>();
+        mShiftLockedKeyCodeMap = new HashMap<Integer, Integer>();
+    }
+
+    private void initInternal(Context context, SharedPreferences prefs) {
+        // Manual label substitutions for key labels with no string resource
+        mKeyLabelMap.put(":-)", R.string.spoken_description_smiley);
+
+        // Symbols that most TTS engines can't speak
+        mKeyCodeMap.put((int) '.', R.string.spoken_description_period);
+        mKeyCodeMap.put((int) ',', R.string.spoken_description_comma);
+        mKeyCodeMap.put((int) '(', R.string.spoken_description_left_parenthesis);
+        mKeyCodeMap.put((int) ')', R.string.spoken_description_right_parenthesis);
+        mKeyCodeMap.put((int) ':', R.string.spoken_description_colon);
+        mKeyCodeMap.put((int) ';', R.string.spoken_description_semicolon);
+        mKeyCodeMap.put((int) '!', R.string.spoken_description_exclamation_mark);
+        mKeyCodeMap.put((int) '?', R.string.spoken_description_question_mark);
+        mKeyCodeMap.put((int) '\"', R.string.spoken_description_double_quote);
+        mKeyCodeMap.put((int) '\'', R.string.spoken_description_single_quote);
+        mKeyCodeMap.put((int) '*', R.string.spoken_description_star);
+        mKeyCodeMap.put((int) '#', R.string.spoken_description_pound);
+        mKeyCodeMap.put((int) ' ', R.string.spoken_description_space);
+
+        // Non-ASCII symbols (must use escape codes!)
+        mKeyCodeMap.put((int) '\u2022', R.string.spoken_description_dot);
+        mKeyCodeMap.put((int) '\u221A', R.string.spoken_description_square_root);
+        mKeyCodeMap.put((int) '\u03C0', R.string.spoken_description_pi);
+        mKeyCodeMap.put((int) '\u0394', R.string.spoken_description_delta);
+        mKeyCodeMap.put((int) '\u2122', R.string.spoken_description_trademark);
+        mKeyCodeMap.put((int) '\u2105', R.string.spoken_description_care_of);
+        mKeyCodeMap.put((int) '\u2026', R.string.spoken_description_ellipsis);
+        mKeyCodeMap.put((int) '\u201E', R.string.spoken_description_low_double_quote);
+
+        // Special non-character codes defined in Keyboard
+        mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete);
+        mKeyCodeMap.put(Keyboard.CODE_ENTER, R.string.spoken_description_return);
+        mKeyCodeMap.put(Keyboard.CODE_SETTINGS, R.string.spoken_description_settings);
+        mKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift);
+        mKeyCodeMap.put(Keyboard.CODE_SHORTCUT, R.string.spoken_description_mic);
+        mKeyCodeMap.put(Keyboard.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol);
+        mKeyCodeMap.put(Keyboard.CODE_TAB, R.string.spoken_description_tab);
+
+        // Shifted versions of non-character codes defined in Keyboard
+        mShiftedKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift_shifted);
+
+        // Shift-locked versions of non-character codes defined in Keyboard
+        mShiftLockedKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_caps_lock);
+    }
+
+    /**
+     * Returns the localized description of the action performed by a specified
+     * key based on the current keyboard state.
+     * <p>
+     * The order of precedence for key descriptions is:
+     * <ol>
+     * <li>Manually-defined based on the key label</li>
+     * <li>Automatic or manually-defined based on the key code</li>
+     * <li>Automatically based on the key label</li>
+     * <li>{code null} for keys with no label or key code defined</li>
+     * </p>
+     *
+     * @param context The package's context.
+     * @param keyboard The keyboard on which the key resides.
+     * @param key The key from which to obtain a description.
+     * @return a character sequence describing the action performed by pressing
+     *         the key
+     */
+    public CharSequence getDescriptionForKey(Context context, Keyboard keyboard, Key key) {
+        if (key.mCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
+            final CharSequence description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
+            if (description != null)
+                return description;
+        }
+
+        if (!TextUtils.isEmpty(key.mLabel)) {
+            final String label = key.mLabel.toString().trim();
+
+            if (mKeyLabelMap.containsKey(label)) {
+                return context.getString(mKeyLabelMap.get(label));
+            } else if (label.length() == 1
+                    || (keyboard.isManualTemporaryUpperCase() && !TextUtils
+                            .isEmpty(key.mHintLetter))) {
+                return getDescriptionForKeyCode(context, keyboard, key);
+            } else {
+                return label;
+            }
+        } else if (key.mCode != Keyboard.CODE_DUMMY) {
+            return getDescriptionForKeyCode(context, keyboard, key);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a context-specific description for the CODE_SWITCH_ALPHA_SYMBOL
+     * key or {@code null} if there is not a description provided for the
+     * current keyboard context.
+     *
+     * @param context The package's context.
+     * @param keyboard The keyboard on which the key resides.
+     * @return a character sequence describing the action performed by pressing
+     *         the key
+     */
+    private CharSequence getDescriptionForSwitchAlphaSymbol(Context context, Keyboard keyboard) {
+        final KeyboardId id = keyboard.mId;
+
+        if (id.isAlphabetKeyboard()) {
+            return context.getString(R.string.spoken_description_to_symbol);
+        } else if (id.isSymbolsKeyboard()) {
+            return context.getString(R.string.spoken_description_to_alpha);
+        } else if (id.isPhoneSymbolsKeyboard()) {
+            return context.getString(R.string.spoken_description_to_numeric);
+        } else if (id.isPhoneKeyboard()) {
+            return context.getString(R.string.spoken_description_to_symbol);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the keycode for the specified key given the current keyboard
+     * state.
+     *
+     * @param keyboard The keyboard on which the key resides.
+     * @param key The key from which to obtain a key code.
+     * @return the key code for the specified key
+     */
+    private int getCorrectKeyCode(Keyboard keyboard, Key key) {
+        if (keyboard.isManualTemporaryUpperCase() && !TextUtils.isEmpty(key.mHintLetter)) {
+            return key.mHintLetter.charAt(0);
+        } else {
+            return key.mCode;
+        }
+    }
+
+    /**
+     * Returns a localized character sequence describing what will happen when
+     * the specified key is pressed based on its key code.
+     * <p>
+     * The order of precedence for key code descriptions is:
+     * <ol>
+     * <li>Manually-defined shift-locked description</li>
+     * <li>Manually-defined shifted description</li>
+     * <li>Manually-defined normal description</li>
+     * <li>Automatic based on the character represented by the key code</li>
+     * <li>Fall-back for undefined or control characters</li>
+     * </ol>
+     * </p>
+     *
+     * @param context The package's context.
+     * @param keyboard The keyboard on which the key resides.
+     * @param key The key from which to obtain a description.
+     * @return a character sequence describing the action performed by pressing
+     *         the key
+     */
+    private CharSequence getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key) {
+        final int code = getCorrectKeyCode(keyboard, key);
+
+        if (keyboard.isShiftLocked() && mShiftLockedKeyCodeMap.containsKey(code)) {
+            return context.getString(mShiftLockedKeyCodeMap.get(code));
+        } else if (keyboard.isShiftedOrShiftLocked() && mShiftedKeyCodeMap.containsKey(code)) {
+            return context.getString(mShiftedKeyCodeMap.get(code));
+        } else if (mKeyCodeMap.containsKey(code)) {
+            return context.getString(mKeyCodeMap.get(code));
+        } else if (Character.isDefined(code) && !Character.isISOControl(code)) {
+            return Character.toString((char) code);
+        } else {
+            return context.getString(R.string.spoken_description_unknown, code);
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java b/java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java
new file mode 100644
index 0000000..6594935
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java
@@ -0,0 +1,39 @@
+/*
+ * 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.compat;
+
+import android.util.Log;
+
+public abstract class AbstractCompatWrapper {
+    private static final String TAG = AbstractCompatWrapper.class.getSimpleName();
+    protected final Object mObj;
+
+    public AbstractCompatWrapper(Object obj) {
+        if (obj == null) {
+            Log.e(TAG, "Invalid input to AbstructCompatWrapper");
+        }
+        mObj = obj;
+    }
+
+    public Object getOriginalObject() {
+        return mObj;
+    }
+
+    public boolean hasOriginalObject() {
+        return mObj != null;
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/AccessibilityEventCompatUtils.java b/java/src/com/android/inputmethod/compat/AccessibilityEventCompatUtils.java
new file mode 100644
index 0000000..5005772
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/AccessibilityEventCompatUtils.java
@@ -0,0 +1,39 @@
+/*
+ * 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.compat;
+
+import android.view.accessibility.AccessibilityEvent;
+
+import java.lang.reflect.Field;
+
+public class AccessibilityEventCompatUtils {
+    public static final int TYPE_VIEW_HOVER_ENTER = 0x80;
+    public static final int TYPE_VIEW_HOVER_EXIT = 0x100;
+
+    private static final Field FIELD_TYPE_VIEW_HOVER_ENTER = CompatUtils.getField(
+            AccessibilityEvent.class, "TYPE_VIEW_HOVER_ENTER");
+    private static final Field FIELD_TYPE_VIEW_HOVER_EXIT = CompatUtils.getField(
+            AccessibilityEvent.class, "TYPE_VIEW_HOVER_EXIT");
+    private static final Integer OBJ_TYPE_VIEW_HOVER_ENTER = (Integer) CompatUtils
+            .getFieldValue(null, null, FIELD_TYPE_VIEW_HOVER_ENTER);
+    private static final Integer OBJ_TYPE_VIEW_HOVER_EXIT = (Integer) CompatUtils
+            .getFieldValue(null, null, FIELD_TYPE_VIEW_HOVER_EXIT);
+
+    public static boolean supportsTouchExploration() {
+        return OBJ_TYPE_VIEW_HOVER_ENTER != null && OBJ_TYPE_VIEW_HOVER_EXIT != null;
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/AccessibilityManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/AccessibilityManagerCompatWrapper.java
new file mode 100644
index 0000000..4db1c7a
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/AccessibilityManagerCompatWrapper.java
@@ -0,0 +1,42 @@
+/*
+ * 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.compat;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.view.accessibility.AccessibilityManager;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+public class AccessibilityManagerCompatWrapper {
+    private static final Method METHOD_getEnabledAccessibilityServiceList = CompatUtils.getMethod(
+            AccessibilityManager.class, "getEnabledAccessibilityServiceList", int.class);
+
+    private final AccessibilityManager mManager;
+
+    public AccessibilityManagerCompatWrapper(AccessibilityManager manager) {
+        mManager = manager;
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
+        return (List<AccessibilityServiceInfo>) CompatUtils.invoke(mManager,
+                Collections.<AccessibilityServiceInfo>emptyList(),
+                METHOD_getEnabledAccessibilityServiceList, feedbackType);
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java b/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java
new file mode 100644
index 0000000..f6afbcf
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java
@@ -0,0 +1,50 @@
+/*
+ * 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.compat;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+public class ArraysCompatUtils {
+    private static final Method METHOD_Arrays_binarySearch = CompatUtils
+            .getMethod(Arrays.class, "binarySearch", int[].class, int.class, int.class, int.class);
+
+    public static int binarySearch(int[] array, int startIndex, int endIndex, int value) {
+        if (METHOD_Arrays_binarySearch != null) {
+            final Object index = CompatUtils.invoke(null, 0, METHOD_Arrays_binarySearch,
+                    array, startIndex, endIndex, value);
+            return (Integer)index;
+        } else {
+            return compatBinarySearch(array, startIndex, endIndex, value);
+        }
+    }
+
+    /* package */ static int compatBinarySearch(int[] array, int startIndex, int endIndex,
+            int value) {
+        if (startIndex > endIndex) throw new IllegalArgumentException();
+        if (startIndex < 0 || endIndex > array.length) throw new ArrayIndexOutOfBoundsException();
+
+        final int work[] = new int[endIndex - startIndex];
+        System.arraycopy(array, startIndex, work, 0, work.length);
+        final int index = Arrays.binarySearch(work, value);
+        if (index >= 0) {
+            return index + startIndex;
+        } else {
+            return ~(~index + startIndex);
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/CompatUtils.java b/java/src/com/android/inputmethod/compat/CompatUtils.java
new file mode 100644
index 0000000..b42633c
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/CompatUtils.java
@@ -0,0 +1,156 @@
+/*
+ * 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.compat;
+
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CompatUtils {
+    private static final String TAG = CompatUtils.class.getSimpleName();
+    private static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
+    // TODO: Can these be constants instead of literal String constants?
+    private static final String INPUT_METHOD_SUBTYPE_SETTINGS =
+            "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
+    private static final String INPUT_LANGUAGE_SELECTION =
+            "com.android.inputmethod.latin.INPUT_LANGUAGE_SELECTION";
+
+    public static Intent getInputLanguageSelectionIntent(String inputMethodId,
+            int flagsForSubtypeSettings) {
+        final String action;
+        Intent intent;
+        if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED
+                /* android.os.Build.VERSION_CODES.HONEYCOMB */
+                && android.os.Build.VERSION.SDK_INT >=  11) {
+            // Refer to android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS
+            action = INPUT_METHOD_SUBTYPE_SETTINGS;
+            intent = new Intent(action);
+            if (!TextUtils.isEmpty(inputMethodId)) {
+                intent.putExtra(EXTRA_INPUT_METHOD_ID, inputMethodId);
+            }
+            if (flagsForSubtypeSettings > 0) {
+                intent.setFlags(flagsForSubtypeSettings);
+            }
+        } else {
+            action = INPUT_LANGUAGE_SELECTION;
+            intent = new Intent(action);
+        }
+        return intent;
+    }
+
+    public static Class<?> getClass(String className) {
+        try {
+            return Class.forName(className);
+        } catch (ClassNotFoundException e) {
+            return null;
+        }
+    }
+
+    public static Method getMethod(Class<?> targetClass, String name,
+            Class<?>... parameterTypes) {
+        if (targetClass == null || TextUtils.isEmpty(name)) return null;
+        try {
+            return targetClass.getMethod(name, parameterTypes);
+        } catch (SecurityException e) {
+            // ignore
+        } catch (NoSuchMethodException e) {
+            // ignore
+        }
+        return null;
+    }
+
+    public static Field getField(Class<?> targetClass, String name) {
+        if (targetClass == null || TextUtils.isEmpty(name)) return null;
+        try {
+            return targetClass.getField(name);
+        } catch (SecurityException e) {
+            // ignore
+        } catch (NoSuchFieldException e) {
+            // ignore
+        }
+        return null;
+    }
+
+    public static Constructor<?> getConstructor(Class<?> targetClass, Class<?> ... types) {
+        if (targetClass == null || types == null) return null;
+        try {
+            return targetClass.getConstructor(types);
+        } catch (SecurityException e) {
+            // ignore
+        } catch (NoSuchMethodException e) {
+            // ignore
+        }
+        return null;
+    }
+
+    public static Object newInstance(Constructor<?> constructor, Object ... args) {
+        if (constructor == null) return null;
+        try {
+            return constructor.newInstance(args);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception in newInstance: " + e.getClass().getSimpleName());
+        }
+        return null;
+    }
+
+    public static Object invoke(
+            Object receiver, Object defaultValue, Method method, Object... args) {
+        if (method == null) return defaultValue;
+        try {
+            return method.invoke(receiver, args);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception in invoke: " + e.getClass().getSimpleName());
+        }
+        return defaultValue;
+    }
+
+    public static Object getFieldValue(Object receiver, Object defaultValue, Field field) {
+        if (field == null) return defaultValue;
+        try {
+            return field.get(receiver);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception in getFieldValue: " + e.getClass().getSimpleName());
+        }
+        return defaultValue;
+    }
+
+    public static void setFieldValue(Object receiver, Field field, Object value) {
+        if (field == null) return;
+        try {
+            field.set(receiver, value);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception in setFieldValue: " + e.getClass().getSimpleName());
+        }
+    }
+
+    public static List<InputMethodSubtypeCompatWrapper> copyInputMethodSubtypeListToWrapper(
+            Object listObject) {
+        if (!(listObject instanceof List<?>)) return null;
+        final List<InputMethodSubtypeCompatWrapper> subtypes =
+                new ArrayList<InputMethodSubtypeCompatWrapper>();
+        for (Object o: (List<?>)listObject) {
+            subtypes.add(new InputMethodSubtypeCompatWrapper(o));
+        }
+        return subtypes;
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
new file mode 100644
index 0000000..bcdcef7
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
@@ -0,0 +1,102 @@
+/*
+ * 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.compat;
+
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+
+import java.lang.reflect.Field;
+
+public class EditorInfoCompatUtils {
+    private static final Field FIELD_IME_FLAG_NAVIGATE_NEXT = CompatUtils.getField(
+            EditorInfo.class, "IME_FLAG_NAVIGATE_NEXT");
+    private static final Field FIELD_IME_FLAG_NAVIGATE_PREVIOUS = CompatUtils.getField(
+            EditorInfo.class, "IME_FLAG_NAVIGATE_PREVIOUS");
+    private static final Field FIELD_IME_ACTION_PREVIOUS = CompatUtils.getField(
+            EditorInfo.class, "IME_ACTION_PREVIOUS");
+    private static final Integer OBJ_IME_FLAG_NAVIGATE_NEXT = (Integer) CompatUtils
+            .getFieldValue(null, null, FIELD_IME_FLAG_NAVIGATE_NEXT);
+    private static final Integer OBJ_IME_FLAG_NAVIGATE_PREVIOUS = (Integer) CompatUtils
+            .getFieldValue(null, null, FIELD_IME_FLAG_NAVIGATE_PREVIOUS);
+    private static final Integer OBJ_IME_ACTION_PREVIOUS = (Integer) CompatUtils
+            .getFieldValue(null, null, FIELD_IME_ACTION_PREVIOUS);
+
+    public static boolean hasFlagNavigateNext(int imeOptions) {
+        if (OBJ_IME_FLAG_NAVIGATE_NEXT == null)
+            return false;
+        return (imeOptions & OBJ_IME_FLAG_NAVIGATE_NEXT) != 0;
+    }
+
+    public static boolean hasFlagNavigatePrevious(int imeOptions) {
+        if (OBJ_IME_FLAG_NAVIGATE_PREVIOUS == null)
+            return false;
+        return (imeOptions & OBJ_IME_FLAG_NAVIGATE_PREVIOUS) != 0;
+    }
+
+    public static void performEditorActionNext(InputConnection ic) {
+        ic.performEditorAction(EditorInfo.IME_ACTION_NEXT);
+    }
+
+    public static void performEditorActionPrevious(InputConnection ic) {
+        if (OBJ_IME_ACTION_PREVIOUS == null)
+            return;
+        ic.performEditorAction(OBJ_IME_ACTION_PREVIOUS);
+    }
+
+    public static String imeOptionsName(int imeOptions) {
+        if (imeOptions == -1)
+            return null;
+        final int actionId = imeOptions & EditorInfo.IME_MASK_ACTION;
+        final String action;
+        switch (actionId) {
+            case EditorInfo.IME_ACTION_UNSPECIFIED:
+                action = "actionUnspecified";
+                break;
+            case EditorInfo.IME_ACTION_NONE:
+                action = "actionNone";
+                break;
+            case EditorInfo.IME_ACTION_GO:
+                action = "actionGo";
+                break;
+            case EditorInfo.IME_ACTION_SEARCH:
+                action = "actionSearch";
+                break;
+            case EditorInfo.IME_ACTION_SEND:
+                action = "actionSend";
+                break;
+            case EditorInfo.IME_ACTION_NEXT:
+                action = "actionNext";
+                break;
+            case EditorInfo.IME_ACTION_DONE:
+                action = "actionDone";
+                break;
+            default: {
+                if (OBJ_IME_ACTION_PREVIOUS != null && actionId == OBJ_IME_ACTION_PREVIOUS) {
+                    action = "actionPrevious";
+                } else {
+                    action = "actionUnknown(" + actionId + ")";
+                }
+                break;
+            }
+        }
+        if ((imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
+            return "flagNoEnterAction|" + action;
+        } else {
+            return action;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/FrameLayoutCompatUtils.java b/java/src/com/android/inputmethod/compat/FrameLayoutCompatUtils.java
new file mode 100644
index 0000000..46499f1
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/FrameLayoutCompatUtils.java
@@ -0,0 +1,52 @@
+/*
+ * 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.compat;
+
+import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
+
+public class FrameLayoutCompatUtils {
+    private static final boolean NEEDS_FRAME_LAYOUT_HACK = (
+            android.os.Build.VERSION.SDK_INT < 11 /* Honeycomb */);
+
+    public static ViewGroup getPlacer(ViewGroup container) {
+        if (NEEDS_FRAME_LAYOUT_HACK) {
+            // Insert RelativeLayout to be able to setMargin because pre-Honeycomb FrameLayout
+            // could not handle setMargin properly.
+            final ViewGroup placer = new RelativeLayout(container.getContext());
+            container.addView(placer);
+            return placer;
+        } else {
+            return container;
+        }
+    }
+
+    public static MarginLayoutParams newLayoutParam(ViewGroup placer, int width, int height) {
+        if (placer instanceof FrameLayout) {
+            return new FrameLayout.LayoutParams(width, height);
+        } else if (placer instanceof RelativeLayout) {
+            return new RelativeLayout.LayoutParams(width, height);
+        } else if (placer == null) {
+            throw new NullPointerException("placer is null");
+        } else {
+            throw new IllegalArgumentException("placer is neither FrameLayout nor RelativeLayout: "
+                    + placer.getClass().getName());
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java
new file mode 100644
index 0000000..7d00b60
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java
@@ -0,0 +1,80 @@
+/*
+ * 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.compat;
+
+import com.android.inputmethod.latin.EditingUtils.SelectedWord;
+
+import android.view.inputmethod.InputConnection;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+public class InputConnectionCompatUtils {
+    private static final Class<?> CLASS_CorrectionInfo = CompatUtils
+            .getClass("android.view.inputmethod.CorrectionInfo");
+    private static final Class<?>[] INPUT_TYPE_CorrectionInfo = new Class<?>[] { int.class,
+            CharSequence.class, CharSequence.class };
+    private static final Constructor<?> CONSTRUCTOR_CorrectionInfo = CompatUtils
+            .getConstructor(CLASS_CorrectionInfo, INPUT_TYPE_CorrectionInfo);
+    private static final Method METHOD_InputConnection_commitCorrection = CompatUtils
+            .getMethod(InputConnection.class, "commitCorrection", CLASS_CorrectionInfo);
+    private static final Method METHOD_getSelectedText = CompatUtils
+            .getMethod(InputConnection.class, "getSelectedText", int.class);
+    private static final Method METHOD_setComposingRegion = CompatUtils
+            .getMethod(InputConnection.class, "setComposingRegion", int.class, int.class);
+    public static final boolean RECORRECTION_SUPPORTED;
+
+    static {
+        RECORRECTION_SUPPORTED = METHOD_getSelectedText != null
+                && METHOD_setComposingRegion != null;
+    }
+
+    public static void commitCorrection(InputConnection ic, int offset, CharSequence oldText,
+            CharSequence newText) {
+        if (ic == null || CONSTRUCTOR_CorrectionInfo == null
+                || METHOD_InputConnection_commitCorrection == null) {
+            return;
+        }
+        Object[] args = { offset, oldText, newText };
+        Object correctionInfo = CompatUtils.newInstance(CONSTRUCTOR_CorrectionInfo, args);
+        if (correctionInfo != null) {
+            CompatUtils.invoke(ic, null, METHOD_InputConnection_commitCorrection,
+                    correctionInfo);
+        }
+    }
+
+
+    /**
+     * Returns the selected text between the selStart and selEnd positions.
+     */
+    public static CharSequence getSelectedText(InputConnection ic, int selStart, int selEnd) {
+        // Use reflection, for backward compatibility
+        return (CharSequence) CompatUtils.invoke(
+                ic, null, METHOD_getSelectedText, 0);
+    }
+
+    /**
+     * Tries to set the text into composition mode if there is support for it in the framework.
+     */
+    public static void underlineWord(InputConnection ic, SelectedWord word) {
+        // Use reflection, for backward compatibility
+        // If method not found, there's nothing we can do. It still works but just wont underline
+        // the word.
+        CompatUtils.invoke(
+                ic, null, METHOD_setComposingRegion, word.mStart, word.mEnd);
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java
new file mode 100644
index 0000000..8e22bbc
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java
@@ -0,0 +1,59 @@
+/*
+ * 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.compat;
+
+import android.content.pm.ServiceInfo;
+import android.view.inputmethod.InputMethodInfo;
+
+import java.lang.reflect.Method;
+
+public class InputMethodInfoCompatWrapper {
+    private final InputMethodInfo mImi;
+    private static final Method METHOD_getSubtypeAt = CompatUtils.getMethod(
+            InputMethodInfo.class, "getSubtypeAt", int.class);
+    private static final Method METHOD_getSubtypeCount = CompatUtils.getMethod(
+            InputMethodInfo.class, "getSubtypeCount");
+
+    public InputMethodInfoCompatWrapper(InputMethodInfo imi) {
+        mImi = imi;
+    }
+
+    public InputMethodInfo getInputMethodInfo() {
+        return mImi;
+    }
+
+    public String getId() {
+        return mImi.getId();
+    }
+
+    public String getPackageName() {
+        return mImi.getPackageName();
+    }
+
+    public ServiceInfo getServiceInfo() {
+        return mImi.getServiceInfo();
+    }
+
+    public int getSubtypeCount() {
+        return (Integer) CompatUtils.invoke(mImi, 0, METHOD_getSubtypeCount);
+    }
+
+    public InputMethodSubtypeCompatWrapper getSubtypeAt(int index) {
+        return new InputMethodSubtypeCompatWrapper(CompatUtils.invoke(mImi, null,
+                METHOD_getSubtypeAt, index));
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
new file mode 100644
index 0000000..1cc13f2
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -0,0 +1,227 @@
+/*
+ * 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.compat;
+
+import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.Utils;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+// TODO: Override this class with the concrete implementation if we need to take care of the
+// performance.
+public class InputMethodManagerCompatWrapper {
+    private static final String TAG = InputMethodManagerCompatWrapper.class.getSimpleName();
+    private static final Method METHOD_getCurrentInputMethodSubtype =
+            CompatUtils.getMethod(InputMethodManager.class, "getCurrentInputMethodSubtype");
+    private static final Method METHOD_getEnabledInputMethodSubtypeList =
+            CompatUtils.getMethod(InputMethodManager.class, "getEnabledInputMethodSubtypeList",
+                    InputMethodInfo.class, boolean.class);
+    private static final Method METHOD_getShortcutInputMethodsAndSubtypes =
+            CompatUtils.getMethod(InputMethodManager.class, "getShortcutInputMethodsAndSubtypes");
+    private static final Method METHOD_setInputMethodAndSubtype =
+            CompatUtils.getMethod(
+                    InputMethodManager.class, "setInputMethodAndSubtype", IBinder.class,
+                    String.class, InputMethodSubtypeCompatWrapper.CLASS_InputMethodSubtype);
+    private static final Method METHOD_switchToLastInputMethod = CompatUtils.getMethod(
+            InputMethodManager.class, "switchToLastInputMethod", IBinder.class);
+
+    private static final InputMethodManagerCompatWrapper sInstance =
+            new InputMethodManagerCompatWrapper();
+
+    public static final boolean SUBTYPE_SUPPORTED;
+
+    static {
+        // This static initializer guarantees that METHOD_getShortcutInputMethodsAndSubtypes is
+        // already instantiated.
+        SUBTYPE_SUPPORTED = METHOD_getShortcutInputMethodsAndSubtypes != null;
+    }
+
+    // For the compatibility, IMM will create dummy subtypes if subtypes are not found.
+    // This is required to be false if the current behavior is broken. For now, it's ok to be true.
+    public static final boolean FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES =
+            !InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED;
+    private static final String VOICE_MODE = "voice";
+    private static final String KEYBOARD_MODE = "keyboard";
+
+    private InputMethodManager mImm;
+    private LanguageSwitcherProxy mLanguageSwitcherProxy;
+    private String mLatinImePackageName;
+
+    private InputMethodManagerCompatWrapper() {
+    }
+
+    public static InputMethodManagerCompatWrapper getInstance(Context context) {
+        if (sInstance.mImm == null) {
+            sInstance.init(context);
+        }
+        return sInstance;
+    }
+
+    private synchronized void init(Context context) {
+        mImm = (InputMethodManager) context.getSystemService(
+                Context.INPUT_METHOD_SERVICE);
+        if (context instanceof LatinIME) {
+            mLatinImePackageName = context.getPackageName();
+        }
+        mLanguageSwitcherProxy = LanguageSwitcherProxy.getInstance();
+    }
+
+    public InputMethodSubtypeCompatWrapper getCurrentInputMethodSubtype() {
+        if (!SUBTYPE_SUPPORTED) {
+            return new InputMethodSubtypeCompatWrapper(
+                    0, 0, mLanguageSwitcherProxy.getInputLocale().toString(), KEYBOARD_MODE, "");
+        }
+        Object o = CompatUtils.invoke(mImm, null, METHOD_getCurrentInputMethodSubtype);
+        return new InputMethodSubtypeCompatWrapper(o);
+    }
+
+    public List<InputMethodSubtypeCompatWrapper> getEnabledInputMethodSubtypeList(
+            InputMethodInfoCompatWrapper imi, boolean allowsImplicitlySelectedSubtypes) {
+        if (!SUBTYPE_SUPPORTED) {
+            String[] languages = mLanguageSwitcherProxy.getEnabledLanguages(
+                    allowsImplicitlySelectedSubtypes);
+            List<InputMethodSubtypeCompatWrapper> subtypeList =
+                    new ArrayList<InputMethodSubtypeCompatWrapper>();
+            for (String lang: languages) {
+                subtypeList.add(new InputMethodSubtypeCompatWrapper(0, 0, lang, KEYBOARD_MODE, ""));
+            }
+            return subtypeList;
+        }
+        Object retval = CompatUtils.invoke(mImm, null, METHOD_getEnabledInputMethodSubtypeList,
+                (imi != null ? imi.getInputMethodInfo() : null), allowsImplicitlySelectedSubtypes);
+        if (retval == null || !(retval instanceof List<?>) || ((List<?>)retval).isEmpty()) {
+            if (!FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES) {
+                // Returns an empty list
+                return Collections.emptyList();
+            }
+            // Creates dummy subtypes
+            @SuppressWarnings("unused")
+            List<InputMethodSubtypeCompatWrapper> subtypeList =
+                    new ArrayList<InputMethodSubtypeCompatWrapper>();
+            InputMethodSubtypeCompatWrapper keyboardSubtype = getLastResortSubtype(KEYBOARD_MODE);
+            InputMethodSubtypeCompatWrapper voiceSubtype = getLastResortSubtype(VOICE_MODE);
+            if (keyboardSubtype != null) {
+                subtypeList.add(keyboardSubtype);
+            }
+            if (voiceSubtype != null) {
+                subtypeList.add(voiceSubtype);
+            }
+            return subtypeList;
+        }
+        return CompatUtils.copyInputMethodSubtypeListToWrapper(retval);
+    }
+
+    private InputMethodInfoCompatWrapper getLatinImeInputMethodInfo() {
+        if (TextUtils.isEmpty(mLatinImePackageName))
+            return null;
+        return Utils.getInputMethodInfo(this, mLatinImePackageName);
+    }
+
+    @SuppressWarnings("unused")
+    private InputMethodSubtypeCompatWrapper getLastResortSubtype(String mode) {
+        if (VOICE_MODE.equals(mode) && !FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES)
+            return null;
+        Locale inputLocale = SubtypeSwitcher.getInstance().getInputLocale();
+        if (inputLocale == null)
+            return null;
+        return new InputMethodSubtypeCompatWrapper(0, 0, inputLocale.toString(), mode, "");
+    }
+
+    public Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>>
+            getShortcutInputMethodsAndSubtypes() {
+        Object retval = CompatUtils.invoke(mImm, null, METHOD_getShortcutInputMethodsAndSubtypes);
+        if (retval == null || !(retval instanceof Map<?, ?>) || ((Map<?, ?>)retval).isEmpty()) {
+            if (!FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES) {
+                // Returns an empty map
+                return Collections.emptyMap();
+            }
+            // Creates dummy subtypes
+            @SuppressWarnings("unused")
+            InputMethodInfoCompatWrapper imi = getLatinImeInputMethodInfo();
+            InputMethodSubtypeCompatWrapper voiceSubtype = getLastResortSubtype(VOICE_MODE);
+            if (imi != null && voiceSubtype != null) {
+                Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>>
+                        shortcutMap =
+                                new HashMap<InputMethodInfoCompatWrapper,
+                                        List<InputMethodSubtypeCompatWrapper>>();
+                List<InputMethodSubtypeCompatWrapper> subtypeList =
+                        new ArrayList<InputMethodSubtypeCompatWrapper>();
+                subtypeList.add(voiceSubtype);
+                shortcutMap.put(imi, subtypeList);
+                return shortcutMap;
+            } else {
+                return Collections.emptyMap();
+            }
+        }
+        Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>> shortcutMap =
+                new HashMap<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>>();
+        final Map<?, ?> retvalMap = (Map<?, ?>)retval;
+        for (Object key : retvalMap.keySet()) {
+            if (!(key instanceof InputMethodInfo)) {
+                Log.e(TAG, "Class type error.");
+                return null;
+            }
+            shortcutMap.put(new InputMethodInfoCompatWrapper((InputMethodInfo)key),
+                    CompatUtils.copyInputMethodSubtypeListToWrapper(retvalMap.get(key)));
+        }
+        return shortcutMap;
+    }
+
+    public void setInputMethodAndSubtype(
+            IBinder token, String id, InputMethodSubtypeCompatWrapper subtype) {
+        if (subtype != null && subtype.hasOriginalObject()) {
+            CompatUtils.invoke(mImm, null, METHOD_setInputMethodAndSubtype,
+                    token, id, subtype.getOriginalObject());
+        }
+    }
+
+    public boolean switchToLastInputMethod(IBinder token) {
+        if (SubtypeSwitcher.getInstance().isDummyVoiceMode()) {
+            return true;
+        }
+        return (Boolean)CompatUtils.invoke(mImm, false, METHOD_switchToLastInputMethod, token);
+    }
+
+    public List<InputMethodInfoCompatWrapper> getEnabledInputMethodList() {
+        if (mImm == null) return null;
+        List<InputMethodInfoCompatWrapper> imis = new ArrayList<InputMethodInfoCompatWrapper>();
+        for (InputMethodInfo imi : mImm.getEnabledInputMethodList()) {
+            imis.add(new InputMethodInfoCompatWrapper(imi));
+        }
+        return imis;
+    }
+
+    public void showInputMethodPicker() {
+        if (mImm == null) return;
+        mImm.showInputMethodPicker();
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
new file mode 100644
index 0000000..7d8c745
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
@@ -0,0 +1,84 @@
+/*
+ * 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.compat;
+
+import android.inputmethodservice.InputMethodService;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+
+public class InputMethodServiceCompatWrapper extends InputMethodService {
+    // CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED needs to be false if the API level is 10
+    // or previous. Note that InputMethodSubtype was added in the API level 11.
+    // For the API level 11 or later, LatinIME should override onCurrentInputMethodSubtypeChanged().
+    // For the API level 10 or previous, we handle the "subtype changed" events by ourselves
+    // without having support from framework -- onCurrentInputMethodSubtypeChanged().
+    public static final boolean CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED = true;
+
+    private InputMethodManagerCompatWrapper mImm;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mImm = InputMethodManagerCompatWrapper.getInstance(this);
+    }
+
+    // When the API level is 10 or previous, notifyOnCurrentInputMethodSubtypeChanged should
+    // handle the event the current subtype was changed. LatinIME calls
+    // notifyOnCurrentInputMethodSubtypeChanged every time LatinIME
+    // changes the current subtype.
+    // This call is required to let LatinIME itself know a subtype changed
+    // event when the API level is 10 or previous.
+    @SuppressWarnings("unused")
+    public void notifyOnCurrentInputMethodSubtypeChanged(InputMethodSubtypeCompatWrapper subtype) {
+        // Do nothing when the API level is 11 or later
+        // and FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES is not true
+        if (CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED && !InputMethodManagerCompatWrapper.
+                FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES) {
+            return;
+        }
+        if (subtype == null) {
+            subtype = mImm.getCurrentInputMethodSubtype();
+        }
+        if (subtype != null) {
+            if (!InputMethodManagerCompatWrapper.FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES
+                    && !subtype.isDummy()) return;
+            if (!InputMethodManagerCompatWrapper.SUBTYPE_SUPPORTED) {
+                LanguageSwitcherProxy.getInstance().setLocale(subtype.getLocale());
+            }
+            SubtypeSwitcher.getInstance().updateSubtype(subtype);
+        }
+    }
+
+    //////////////////////////////////////
+    // Functions using API v11 or later //
+    //////////////////////////////////////
+    @Override
+    public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
+        // Do nothing when the API level is 10 or previous
+        if (!CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) return;
+        SubtypeSwitcher.getInstance().updateSubtype(
+                new InputMethodSubtypeCompatWrapper(subtype));
+    }
+
+    protected static void setTouchableRegionCompat(InputMethodService.Insets outInsets,
+            int x, int y, int width, int height) {
+        outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
+        outInsets.touchableRegion.set(x, y, width, height);
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
new file mode 100644
index 0000000..667d86c
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
@@ -0,0 +1,161 @@
+/*
+ * 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.compat;
+
+import com.android.inputmethod.latin.LatinImeLogger;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+// TODO: Override this class with the concrete implementation if we need to take care of the
+// performance.
+public final class InputMethodSubtypeCompatWrapper extends AbstractCompatWrapper {
+    private static final boolean DBG = LatinImeLogger.sDBG;
+    private static final String TAG = InputMethodSubtypeCompatWrapper.class.getSimpleName();
+    private static final String DEFAULT_LOCALE = "en_US";
+    private static final String DEFAULT_MODE = "keyboard";
+
+    public static final Class<?> CLASS_InputMethodSubtype =
+            CompatUtils.getClass("android.view.inputmethod.InputMethodSubtype");
+    private static final Method METHOD_getNameResId =
+            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getNameResId");
+    private static final Method METHOD_getIconResId =
+            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getIconResId");
+    private static final Method METHOD_getLocale =
+            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getLocale");
+    private static final Method METHOD_getMode =
+            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getMode");
+    private static final Method METHOD_getExtraValue =
+            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValue");
+    private static final Method METHOD_containsExtraValueKey =
+            CompatUtils.getMethod(CLASS_InputMethodSubtype, "containsExtraValueKey", String.class);
+    private static final Method METHOD_getExtraValueOf =
+            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValueOf", String.class);
+    private static final Method METHOD_isAuxiliary =
+            CompatUtils.getMethod(CLASS_InputMethodSubtype, "isAuxiliary");
+
+    private final int mDummyNameResId;
+    private final int mDummyIconResId;
+    private final String mDummyLocale;
+    private final String mDummyMode;
+    private final String mDummyExtraValues;
+
+    public InputMethodSubtypeCompatWrapper(Object subtype) {
+        super((CLASS_InputMethodSubtype != null && CLASS_InputMethodSubtype.isInstance(subtype))
+                ? subtype : null);
+        mDummyNameResId = 0;
+        mDummyIconResId = 0;
+        mDummyLocale = DEFAULT_LOCALE;
+        mDummyMode = DEFAULT_MODE;
+        mDummyExtraValues = "";
+    }
+
+    // Constructor for creating a dummy subtype.
+    public InputMethodSubtypeCompatWrapper(int nameResId, int iconResId, String locale,
+            String mode, String extraValues) {
+        super(null);
+        if (DBG) {
+            Log.d(TAG, "CreateInputMethodSubtypeCompatWrapper");
+        }
+        mDummyNameResId = nameResId;
+        mDummyIconResId = iconResId;
+        mDummyLocale = locale != null ? locale : "";
+        mDummyMode = mode != null ? mode : "";
+        mDummyExtraValues = extraValues != null ? extraValues : "";
+    }
+
+    public int getNameResId() {
+        if (mObj == null) return mDummyNameResId;
+        return (Integer)CompatUtils.invoke(mObj, 0, METHOD_getNameResId);
+    }
+
+    public int getIconResId() {
+        if (mObj == null) return mDummyIconResId;
+        return (Integer)CompatUtils.invoke(mObj, 0, METHOD_getIconResId);
+    }
+
+    public String getLocale() {
+        if (mObj == null) return mDummyLocale;
+        final String s = (String)CompatUtils.invoke(mObj, null, METHOD_getLocale);
+        if (TextUtils.isEmpty(s)) return DEFAULT_LOCALE;
+        return s;
+    }
+
+    public String getMode() {
+        if (mObj == null) return mDummyMode;
+        String s = (String)CompatUtils.invoke(mObj, null, METHOD_getMode);
+        if (TextUtils.isEmpty(s)) return DEFAULT_MODE;
+        return s;
+    }
+
+    public String getExtraValue() {
+        if (mObj == null) return mDummyExtraValues;
+        return (String)CompatUtils.invoke(mObj, null, METHOD_getExtraValue);
+    }
+
+    public boolean containsExtraValueKey(String key) {
+        return (Boolean)CompatUtils.invoke(mObj, false, METHOD_containsExtraValueKey, key);
+    }
+
+    public String getExtraValueOf(String key) {
+        return (String)CompatUtils.invoke(mObj, null, METHOD_getExtraValueOf, key);
+    }
+
+    public boolean isAuxiliary() {
+        return (Boolean)CompatUtils.invoke(mObj, false, METHOD_isAuxiliary);
+    }
+
+    public boolean isDummy() {
+        return !hasOriginalObject();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof InputMethodSubtypeCompatWrapper) {
+            InputMethodSubtypeCompatWrapper subtype = (InputMethodSubtypeCompatWrapper)o;
+            if (mObj == null) {
+                // easy check of dummy subtypes
+                return (mDummyNameResId == subtype.mDummyNameResId
+                        && mDummyIconResId == subtype.mDummyIconResId
+                        && mDummyLocale.equals(subtype.mDummyLocale)
+                        && mDummyMode.equals(subtype.mDummyMode)
+                        && mDummyExtraValues.equals(subtype.mDummyExtraValues));
+            }
+            return mObj.equals(subtype.getOriginalObject());
+        } else {
+            return mObj.equals(o);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        if (mObj == null) {
+            return hashCodeInternal(mDummyNameResId, mDummyIconResId, mDummyLocale,
+                    mDummyMode, mDummyExtraValues);
+        }
+        return mObj.hashCode();
+    }
+
+    private static int hashCodeInternal(int nameResId, int iconResId, String locale,
+            String mode, String extraValue) {
+        return Arrays
+                .hashCode(new Object[] { nameResId, iconResId, locale, mode, extraValue });
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java
new file mode 100644
index 0000000..6c2f0f7
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java
@@ -0,0 +1,118 @@
+/*
+ * 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.compat;
+
+import android.text.InputType;
+
+import java.lang.reflect.Field;
+
+public class InputTypeCompatUtils {
+    private static final Field FIELD_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS =
+            CompatUtils.getField(InputType.class, "TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS");
+    private static final Field FIELD_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD = CompatUtils
+            .getField(InputType.class, "TYPE_TEXT_VARIATION_WEB_PASSWORD");
+    private static final Field FIELD_InputType_TYPE_NUMBER_VARIATION_PASSWORD = CompatUtils
+            .getField(InputType.class, "TYPE_NUMBER_VARIATION_PASSWORD");
+    private static final Integer OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS =
+            (Integer) CompatUtils.getFieldValue(null, null,
+                    FIELD_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS);
+    private static final Integer OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD =
+            (Integer) CompatUtils.getFieldValue(null, null,
+                    FIELD_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD);
+    private static final Integer OBJ_InputType_TYPE_NUMBER_VARIATION_PASSWORD =
+            (Integer) CompatUtils.getFieldValue(null, null,
+                    FIELD_InputType_TYPE_NUMBER_VARIATION_PASSWORD);
+    private static final int WEB_TEXT_PASSWORD_INPUT_TYPE;
+    private static final int WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE;
+    private static final int NUMBER_PASSWORD_INPUT_TYPE;
+    private static final int TEXT_PASSWORD_INPUT_TYPE =
+            InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD;
+    private static final int TEXT_VISIBLE_PASSWORD_INPUT_TYPE =
+            InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
+
+    static {
+        WEB_TEXT_PASSWORD_INPUT_TYPE =
+            OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD != null
+                    ? InputType.TYPE_CLASS_TEXT | OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD
+                    : 0;
+        WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE =
+            OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS != null
+                    ? InputType.TYPE_CLASS_TEXT
+                            | OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS
+                    : 0;
+        NUMBER_PASSWORD_INPUT_TYPE =
+                OBJ_InputType_TYPE_NUMBER_VARIATION_PASSWORD != null
+                        ? InputType.TYPE_CLASS_NUMBER | OBJ_InputType_TYPE_NUMBER_VARIATION_PASSWORD
+                        : 0;
+    }
+
+    private static boolean isWebEditTextInputType(int inputType) {
+        return inputType == (InputType.TYPE_CLASS_TEXT
+                | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
+    }
+
+    private static boolean isWebPasswordInputType(int inputType) {
+        return WEB_TEXT_PASSWORD_INPUT_TYPE != 0
+                && inputType == WEB_TEXT_PASSWORD_INPUT_TYPE;
+    }
+
+    private static boolean isWebEmailAddressInputType(int inputType) {
+        return WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE != 0
+                && inputType == WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE;
+    }
+
+    private static boolean isNumberPasswordInputType(int inputType) {
+        return NUMBER_PASSWORD_INPUT_TYPE != 0
+                && inputType == NUMBER_PASSWORD_INPUT_TYPE;
+    }
+
+    private static boolean isTextPasswordInputType(int inputType) {
+        return inputType == TEXT_PASSWORD_INPUT_TYPE;
+    }
+
+    private static boolean isWebEmailAddressVariation(int variation) {
+        return OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS != null
+                && variation == OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
+    }
+
+    public static boolean isEmailVariation(int variation) {
+        return variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+                || isWebEmailAddressVariation(variation);
+    }
+
+    public static boolean isWebInputType(int inputType) {
+        final int maskedInputType =
+                inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION);
+        return isWebEditTextInputType(maskedInputType) || isWebPasswordInputType(maskedInputType)
+                || isWebEmailAddressInputType(maskedInputType);
+    }
+
+    // Please refer to TextView.isPasswordInputType
+    public static boolean isPasswordInputType(int inputType) {
+        final int maskedInputType =
+                inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION);
+        return isTextPasswordInputType(maskedInputType) || isWebPasswordInputType(maskedInputType)
+                || isNumberPasswordInputType(maskedInputType);
+    }
+
+    // Please refer to TextView.isVisiblePasswordInputType
+    public static boolean isVisiblePasswordInputType(int inputType) {
+        final int maskedInputType =
+                inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION);
+        return maskedInputType == TEXT_VISIBLE_PASSWORD_INPUT_TYPE;
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/LinearLayoutCompatUtils.java b/java/src/com/android/inputmethod/compat/LinearLayoutCompatUtils.java
new file mode 100644
index 0000000..674cbe7
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/LinearLayoutCompatUtils.java
@@ -0,0 +1,55 @@
+/*
+ * 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.compat;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+
+public class LinearLayoutCompatUtils {
+    private static final String TAG = LinearLayoutCompatUtils.class.getSimpleName();
+
+    private static final Class<?> CLASS_R_STYLEABLE = CompatUtils.getClass(
+            "com.android.internal.R$styleable");
+    private static final Field STYLEABLE_VIEW = CompatUtils.getField(
+            CLASS_R_STYLEABLE, "View");
+    private static final Field STYLEABLE_VIEW_BACKGROUND = CompatUtils.getField(
+            CLASS_R_STYLEABLE, "View_background");
+    private static final Object VALUE_STYLEABLE_VIEW = CompatUtils.getFieldValue(
+            null, null, STYLEABLE_VIEW);
+    private static final Integer VALUE_STYLEABLE_VIEW_BACKGROUND =
+            (Integer)CompatUtils.getFieldValue(null, null, STYLEABLE_VIEW_BACKGROUND);
+
+    public static Drawable getBackgroundDrawable(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        if (!(VALUE_STYLEABLE_VIEW instanceof int[]) || VALUE_STYLEABLE_VIEW_BACKGROUND == null) {
+            Log.w(TAG, "Can't get View background attribute using reflection");
+            return null;
+        }
+
+        final int[] styleableView = (int[])VALUE_STYLEABLE_VIEW;
+        final TypedArray a = context.obtainStyledAttributes(
+                attrs, styleableView, defStyleAttr, defStyleRes);
+        final Drawable background = a.getDrawable(VALUE_STYLEABLE_VIEW_BACKGROUND);
+        a.recycle();
+        return background;
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/MotionEventCompatUtils.java b/java/src/com/android/inputmethod/compat/MotionEventCompatUtils.java
new file mode 100644
index 0000000..8518a4a
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/MotionEventCompatUtils.java
@@ -0,0 +1,23 @@
+/*
+ * 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.compat;
+
+public class MotionEventCompatUtils {
+    public static final int ACTION_HOVER_MOVE = 0x7;
+    public static final int ACTION_HOVER_ENTER = 0x9;
+    public static final int ACTION_HOVER_EXIT = 0xA;
+}
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
new file mode 100644
index 0000000..4929dd9
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -0,0 +1,87 @@
+/*
+ * 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.compat;
+
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver;
+
+import android.content.Context;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.TextUtils;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Locale;
+
+public class SuggestionSpanUtils {
+    // TODO: Use reflection to get field values
+    public static final String ACTION_SUGGESTION_PICKED =
+            "android.text.style.SUGGESTION_PICKED";
+    public static final String SUGGESTION_SPAN_PICKED_AFTER = "after";
+    public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before";
+    public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode";
+    public static final int SUGGESTION_MAX_SIZE = 5;
+    public static final boolean SUGGESTION_SPAN_IS_SUPPORTED;
+
+    private static final Class<?> CLASS_SuggestionSpan = CompatUtils
+            .getClass("android.text.style.SuggestionSpan");
+    private static final Class<?>[] INPUT_TYPE_SuggestionSpan = new Class<?>[] {
+            Context.class, Locale.class, String[].class, int.class, Class.class };
+    private static final Constructor<?> CONSTRUCTOR_SuggestionSpan = CompatUtils
+            .getConstructor(CLASS_SuggestionSpan, INPUT_TYPE_SuggestionSpan);
+    static {
+        SUGGESTION_SPAN_IS_SUPPORTED =
+                CLASS_SuggestionSpan != null && CONSTRUCTOR_SuggestionSpan != null;
+    }
+
+    public static CharSequence getTextWithSuggestionSpan(Context context,
+            CharSequence pickedWord, SuggestedWords suggestedWords) {
+        if (TextUtils.isEmpty(pickedWord) || CONSTRUCTOR_SuggestionSpan == null
+                || suggestedWords == null || suggestedWords.size() == 0) {
+            return pickedWord;
+        }
+
+        final Spannable spannable;
+        if (pickedWord instanceof Spannable) {
+            spannable = (Spannable) pickedWord;
+        } else {
+            spannable = new SpannableString(pickedWord);
+        }
+        final ArrayList<String> suggestionsList = new ArrayList<String>();
+        for (int i = 0; i < suggestedWords.size(); ++i) {
+            if (suggestionsList.size() >= SUGGESTION_MAX_SIZE) {
+                break;
+            }
+            final CharSequence word = suggestedWords.getWord(i);
+            if (!TextUtils.equals(pickedWord, word)) {
+                suggestionsList.add(word.toString());
+            }
+        }
+
+        final Object[] args =
+                { context, null, suggestionsList.toArray(new String[suggestionsList.size()]), 0,
+                        (Class<?>) SuggestionSpanPickedNotificationReceiver.class };
+        final Object ss = CompatUtils.newInstance(CONSTRUCTOR_SuggestionSpan, args);
+        if (ss == null) {
+            return pickedWord;
+        }
+        spannable.setSpan(ss, 0, pickedWord.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        return spannable;
+    }
+}
diff --git a/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
new file mode 100644
index 0000000..8e2a2e0
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
@@ -0,0 +1,47 @@
+/*
+ * 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.compat;
+
+import android.content.Context;
+import android.os.Vibrator;
+
+import java.lang.reflect.Method;
+
+public class VibratorCompatWrapper {
+    private static final Method METHOD_hasVibrator = CompatUtils.getMethod(Vibrator.class,
+            "hasVibrator", int.class);
+
+    private static final VibratorCompatWrapper sInstance = new VibratorCompatWrapper();
+    private Vibrator mVibrator;
+
+    private VibratorCompatWrapper() {
+    }
+
+    public static VibratorCompatWrapper getInstance(Context context) {
+        if (sInstance.mVibrator == null) {
+            sInstance.mVibrator =
+                    (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+        }
+        return sInstance;
+    }
+
+    public boolean hasVibrator() {
+        if (mVibrator == null)
+            return false;
+        return (Boolean) CompatUtils.invoke(mVibrator, true, METHOD_hasVibrator);
+    }
+}
diff --git a/java/src/com/android/inputmethod/deprecated/LanguageSwitcherProxy.java b/java/src/com/android/inputmethod/deprecated/LanguageSwitcherProxy.java
new file mode 100644
index 0000000..290e6b5
--- /dev/null
+++ b/java/src/com/android/inputmethod/deprecated/LanguageSwitcherProxy.java
@@ -0,0 +1,90 @@
+/*
+ * 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.deprecated;
+
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.deprecated.languageswitcher.LanguageSwitcher;
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.Settings;
+
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+
+import java.util.Locale;
+
+// This class is used only when the IME doesn't use method.xml for language switching.
+public class LanguageSwitcherProxy implements SharedPreferences.OnSharedPreferenceChangeListener {
+    private static final LanguageSwitcherProxy sInstance = new LanguageSwitcherProxy();
+    private LatinIME mService;
+    private LanguageSwitcher mLanguageSwitcher;
+    private SharedPreferences mPrefs;
+
+    public static LanguageSwitcherProxy getInstance() {
+        if (InputMethodManagerCompatWrapper.SUBTYPE_SUPPORTED) return null;
+        return sInstance;
+    }
+
+    public static void init(LatinIME service, SharedPreferences prefs) {
+        if (InputMethodManagerCompatWrapper.SUBTYPE_SUPPORTED) return;
+        final Configuration conf = service.getResources().getConfiguration();
+        sInstance.mLanguageSwitcher = new LanguageSwitcher(service);
+        sInstance.mLanguageSwitcher.loadLocales(prefs, conf.locale);
+        sInstance.mPrefs = prefs;
+        sInstance.mService = service;
+        prefs.registerOnSharedPreferenceChangeListener(sInstance);
+    }
+
+    public static void onConfigurationChanged(Configuration conf) {
+        if (InputMethodManagerCompatWrapper.SUBTYPE_SUPPORTED) return;
+        sInstance.mLanguageSwitcher.onConfigurationChanged(conf, sInstance.mPrefs);
+    }
+
+    public static void loadSettings() {
+        if (InputMethodManagerCompatWrapper.SUBTYPE_SUPPORTED) return;
+        sInstance.mLanguageSwitcher.loadLocales(sInstance.mPrefs, null);
+    }
+
+    public int getLocaleCount() {
+        return mLanguageSwitcher.getLocaleCount();
+    }
+
+    public String[] getEnabledLanguages(boolean allowImplicitlySelectedLanguages) {
+        return mLanguageSwitcher.getEnabledLanguages(allowImplicitlySelectedLanguages);
+    }
+
+    public Locale getInputLocale() {
+        return mLanguageSwitcher.getInputLocale();
+    }
+
+    public void setLocale(String localeStr) {
+        mLanguageSwitcher.setLocale(localeStr);
+        mLanguageSwitcher.persist(mPrefs);
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+        // PREF_SELECTED_LANGUAGES: enabled input subtypes
+        // PREF_INPUT_LANGUAGE: current input subtype
+        if (key.equals(Settings.PREF_SELECTED_LANGUAGES)
+                || key.equals(Settings.PREF_INPUT_LANGUAGE)) {
+            mLanguageSwitcher.loadLocales(prefs, null);
+            if (mService != null) {
+                mService.onRefreshKeyboard();
+            }
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
similarity index 82%
rename from java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
rename to java/src/com/android/inputmethod/deprecated/VoiceProxy.java
index a244c9e..85993ea 100644
--- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
+++ b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
@@ -14,8 +14,14 @@
  * the License.
  */
 
-package com.android.inputmethod.voice;
+package com.android.inputmethod.deprecated;
 
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.deprecated.voice.FieldContext;
+import com.android.inputmethod.deprecated.voice.Hints;
+import com.android.inputmethod.deprecated.voice.SettingsUtil;
+import com.android.inputmethod.deprecated.voice.VoiceInput;
+import com.android.inputmethod.deprecated.voice.VoiceInputLogger;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.latin.EditingUtils;
 import com.android.inputmethod.latin.LatinIME;
@@ -28,6 +34,7 @@
 import com.android.inputmethod.latin.Utils;
 
 import android.app.AlertDialog;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -54,7 +61,6 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
 import android.widget.TextView;
 
 import java.util.ArrayList;
@@ -62,8 +68,8 @@
 import java.util.List;
 import java.util.Map;
 
-public class VoiceIMEConnector implements VoiceInput.UiListener {
-    private static final VoiceIMEConnector sInstance = new VoiceIMEConnector();
+public class VoiceProxy implements VoiceInput.UiListener {
+    private static final VoiceProxy sInstance = new VoiceProxy();
 
     public static final boolean VOICE_INSTALLED = true;
     private static final boolean ENABLE_VOICE_BUTTON = true;
@@ -76,8 +82,10 @@
     private static final String PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE =
             "has_used_voice_input_unsupported_locale";
     private static final int RECOGNITIONVIEW_HEIGHT_THRESHOLD_RATIO = 6;
+    // TODO: Adjusted on phones for now
+    private static final int RECOGNITIONVIEW_MINIMUM_HEIGHT_DIP = 244;
 
-    private static final String TAG = VoiceIMEConnector.class.getSimpleName();
+    private static final String TAG = VoiceProxy.class.getSimpleName();
     private static final boolean DEBUG = LatinImeLogger.sDBG;
 
     private boolean mAfterVoiceInput;
@@ -93,7 +101,8 @@
     private boolean mVoiceButtonOnPrimary;
     private boolean mVoiceInputHighlighted;
 
-    private InputMethodManager mImm;
+    private int mMinimumVoiceRecognitionViewHeightPixel;
+    private InputMethodManagerCompatWrapper mImm;
     private LatinIME mService;
     private AlertDialog mVoiceWarningDialog;
     private VoiceInput mVoiceInput;
@@ -101,23 +110,26 @@
     private Hints mHints;
     private UIHandler mHandler;
     private SubtypeSwitcher mSubtypeSwitcher;
+
     // For each word, a list of potential replacements, usually from voice.
     private final Map<String, List<CharSequence>> mWordToSuggestions =
             new HashMap<String, List<CharSequence>>();
 
-    public static VoiceIMEConnector init(LatinIME context, SharedPreferences prefs, UIHandler h) {
+    public static VoiceProxy init(LatinIME context, SharedPreferences prefs, UIHandler h) {
         sInstance.initInternal(context, prefs, h);
         return sInstance;
     }
 
-    public static VoiceIMEConnector getInstance() {
+    public static VoiceProxy getInstance() {
         return sInstance;
     }
 
     private void initInternal(LatinIME service, SharedPreferences prefs, UIHandler h) {
         mService = service;
         mHandler = h;
-        mImm = (InputMethodManager) service.getSystemService(Context.INPUT_METHOD_SERVICE);
+        mMinimumVoiceRecognitionViewHeightPixel = Utils.dipToPixel(
+                Utils.getDipScale(service), RECOGNITIONVIEW_MINIMUM_HEIGHT_DIP);
+        mImm = InputMethodManagerCompatWrapper.getInstance(service);
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         if (VOICE_INSTALLED) {
             mVoiceInput = new VoiceInput(service, this);
@@ -125,15 +137,15 @@
                 @Override
                 public void showHint(int viewResource) {
                     View view = LayoutInflater.from(mService).inflate(viewResource, null);
-                    mService.setCandidatesView(view);
-                    mService.setCandidatesViewShown(true);
+//                    mService.setCandidatesView(view);
+//                    mService.setCandidatesViewShown(true);
                     mIsShowingHint = true;
                 }
               });
         }
     }
 
-    private VoiceIMEConnector() {
+    private VoiceProxy() {
         // Intentional empty constructor for singleton.
     }
 
@@ -429,7 +441,7 @@
             }
             builder.setTypedWordValid(true).setHasMinimalSuggestion(true);
             mService.setSuggestions(builder.build());
-            mService.setCandidatesViewShown(true);
+//            mService.setCandidatesViewShown(true);
             return true;
         }
         return false;
@@ -514,7 +526,7 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                mService.setCandidatesViewShown(false);
+//                mService.setCandidatesViewShown(false);
                 mRecognizing = true;
                 mVoiceInput.newView();
                 View v = mVoiceInput.getView();
@@ -524,7 +536,7 @@
                     ((ViewGroup) p).removeView(v);
                 }
 
-                View keyboardView = KeyboardSwitcher.getInstance().getInputView();
+                View keyboardView = KeyboardSwitcher.getInstance().getKeyboardView();
 
                 // The full height of the keyboard is difficult to calculate
                 // as the dimension is expressed in "mm" and not in "pixel"
@@ -536,7 +548,11 @@
                             mService.getResources().getDisplayMetrics().heightPixels;
                     final int currentHeight = popupLayout.getLayoutParams().height;
                     final int keyboardHeight = keyboardView.getHeight();
-                    if (keyboardHeight > currentHeight || keyboardHeight
+                    if (mMinimumVoiceRecognitionViewHeightPixel > keyboardHeight
+                            || mMinimumVoiceRecognitionViewHeightPixel > currentHeight) {
+                        popupLayout.getLayoutParams().height =
+                            mMinimumVoiceRecognitionViewHeightPixel;
+                    } else if (keyboardHeight > currentHeight || keyboardHeight
                             > (displayHeight / RECOGNITIONVIEW_HEIGHT_THRESHOLD_RATIO)) {
                         popupLayout.getLayoutParams().height = keyboardHeight;
                     }
@@ -560,14 +576,24 @@
 
             @Override
             protected void onPostExecute(Boolean result) {
+                // Calls in this method need to be done in the same thread as the thread which
+                // called switchToLastInputMethod()
                 if (!result) {
                     if (DEBUG) {
                         Log.d(TAG, "Couldn't switch back to last IME.");
                     }
-                    // Needs to reset here because LatinIME failed to back to any IME and
-                    // the same voice subtype will be triggered in the next time.
+                    // Because the current IME and subtype failed to switch to any other IME and
+                    // subtype by switchToLastInputMethod, the current IME and subtype should keep
+                    // being LatinIME and voice subtype in the next time. And for re-showing voice
+                    // mode, the state of voice input should be reset and the voice view should be
+                    // hidden.
                     mVoiceInput.reset();
                     mService.requestHideSelf(0);
+                } else {
+                    // Notify an event that the current subtype was changed. This event will be
+                    // handled if "onCurrentInputMethodSubtypeChanged" can't be implemented
+                    // when the API level is 10 or previous.
+                    mService.notifyOnCurrentInputMethodSubtypeChanged(null);
                 }
             }
         }.execute();
@@ -624,6 +650,7 @@
     }
 
     private boolean shouldShowVoiceButton(FieldContext fieldContext, EditorInfo attribute) {
+        @SuppressWarnings("deprecation")
         final boolean noMic = Utils.inPrivateImeOptions(null,
                 LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, attribute)
                 || Utils.inPrivateImeOptions(mService.getPackageName(),
@@ -664,7 +691,7 @@
         if (mSubtypeSwitcher.isVoiceMode() && windowToken != null) {
             // Close keyboard view if it is been shown.
             if (KeyboardSwitcher.getInstance().isInputViewShown())
-                KeyboardSwitcher.getInstance().getInputView().purgeKeyboardAndClosing();
+                KeyboardSwitcher.getInstance().getKeyboardView().purgeKeyboardAndClosing();
             startListening(false, windowToken);
         }
         // If we have no token, onAttachedToWindow will take care of showing dialog and start
@@ -674,7 +701,7 @@
     public void onAttachedToWindow() {
         // After onAttachedToWindow, we can show the voice warning dialog. See startListening()
         // above.
-        mSubtypeSwitcher.setVoiceInput(mVoiceInput);
+        VoiceInputWrapper.getInstance().setVoiceInput(mVoiceInput, mSubtypeSwitcher);
     }
 
     public void onConfigurationChanged(Configuration configuration) {
@@ -725,4 +752,91 @@
         List<String> candidates;
         Map<String, List<CharSequence>> alternatives;
     }
+
+    public static class VoiceLoggerWrapper {
+        private static final VoiceLoggerWrapper sLoggerWrapperInstance = new VoiceLoggerWrapper();
+        private VoiceInputLogger mLogger;
+
+        public static VoiceLoggerWrapper getInstance(Context context) {
+            if (sLoggerWrapperInstance.mLogger == null) {
+                // Not thread safe, but it's ok.
+                sLoggerWrapperInstance.mLogger = VoiceInputLogger.getLogger(context);
+            }
+            return sLoggerWrapperInstance;
+        }
+
+        // private for the singleton
+        private VoiceLoggerWrapper() {
+        }
+
+        public void settingsWarningDialogCancel() {
+            mLogger.settingsWarningDialogCancel();
+        }
+
+        public void settingsWarningDialogOk() {
+            mLogger.settingsWarningDialogOk();
+        }
+
+        public void settingsWarningDialogShown() {
+            mLogger.settingsWarningDialogShown();
+        }
+
+        public void settingsWarningDialogDismissed() {
+            mLogger.settingsWarningDialogDismissed();
+        }
+
+        public void voiceInputSettingEnabled(boolean enabled) {
+            if (enabled) {
+                mLogger.voiceInputSettingEnabled();
+            } else {
+                mLogger.voiceInputSettingDisabled();
+            }
+        }
+    }
+
+    public static class VoiceInputWrapper {
+        private static final VoiceInputWrapper sInputWrapperInstance = new VoiceInputWrapper();
+        private VoiceInput mVoiceInput;
+        public static VoiceInputWrapper getInstance() {
+            return sInputWrapperInstance;
+        }
+        public void setVoiceInput(VoiceInput voiceInput, SubtypeSwitcher switcher) {
+            if (mVoiceInput == null && voiceInput != null) {
+                mVoiceInput = voiceInput;
+            }
+            switcher.setVoiceInputWrapper(this);
+        }
+
+        private VoiceInputWrapper() {
+        }
+
+        public void cancel() {
+            if (mVoiceInput != null) mVoiceInput.cancel();
+        }
+
+        public void reset() {
+            if (mVoiceInput != null) mVoiceInput.reset();
+        }
+    }
+
+    // A list of locales which are supported by default for voice input, unless we get a
+    // different list from Gservices.
+    private static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES =
+            "en " +
+            "en_US " +
+            "en_GB " +
+            "en_AU " +
+            "en_CA " +
+            "en_IE " +
+            "en_IN " +
+            "en_NZ " +
+            "en_SG " +
+            "en_ZA ";
+
+    public static String getSupportedLocalesString (ContentResolver resolver) {
+        return SettingsUtil.getSettingsString(
+                resolver,
+                SettingsUtil.LATIN_IME_VOICE_INPUT_SUPPORTED_LOCALES,
+                DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES);
+    }
 }
diff --git a/java/src/com/android/inputmethod/deprecated/compat/VoiceInputLoggerCompatUtils.java b/java/src/com/android/inputmethod/deprecated/compat/VoiceInputLoggerCompatUtils.java
new file mode 100644
index 0000000..488390f
--- /dev/null
+++ b/java/src/com/android/inputmethod/deprecated/compat/VoiceInputLoggerCompatUtils.java
@@ -0,0 +1,36 @@
+/*
+ * 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.deprecated.compat;
+
+import com.android.common.userhappiness.UserHappinessSignals;
+import com.android.inputmethod.compat.CompatUtils;
+
+import java.lang.reflect.Method;
+
+public class VoiceInputLoggerCompatUtils {
+    public static final String EXTRA_TEXT_REPLACED_LENGTH = "length";
+    public static final String EXTRA_BEFORE_N_BEST_CHOOSE = "before";
+    public static final String EXTRA_AFTER_N_BEST_CHOOSE = "after";
+    private static final Method METHOD_UserHappinessSignals_setHasVoiceLoggingInfo =
+            CompatUtils.getMethod(UserHappinessSignals.class, "setHasVoiceLoggingInfo",
+                    boolean.class);
+
+    public static void setHasVoiceLoggingInfoCompat(boolean hasLoggingInfo) {
+        CompatUtils.invoke(null, null, METHOD_UserHappinessSignals_setHasVoiceLoggingInfo,
+                hasLoggingInfo);
+    }
+}
diff --git a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java
new file mode 100644
index 0000000..cf6cd0f
--- /dev/null
+++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2008-2009 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.deprecated.languageswitcher;
+
+import com.android.inputmethod.keyboard.internal.KeyboardParser;
+import com.android.inputmethod.latin.DictionaryFactory;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.Settings;
+import com.android.inputmethod.latin.SharedPreferencesCompat;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.Utils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.util.Pair;
+
+import java.io.IOException;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+public class InputLanguageSelection extends PreferenceActivity {
+
+    private SharedPreferences mPrefs;
+    private String mSelectedLanguages;
+    private HashMap<CheckBoxPreference, Locale> mLocaleMap =
+            new HashMap<CheckBoxPreference, Locale>();
+
+    private static class LocaleEntry implements Comparable<Object> {
+        private static Collator sCollator = Collator.getInstance();
+
+        private String mLabel;
+        public final Locale mLocale;
+
+        public LocaleEntry(String label, Locale locale) {
+            this.mLabel = label;
+            this.mLocale = locale;
+        }
+
+        @Override
+        public String toString() {
+            return this.mLabel;
+        }
+
+        @Override
+        public int compareTo(Object o) {
+            return sCollator.compare(this.mLabel, ((LocaleEntry) o).mLabel);
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        addPreferencesFromResource(R.xml.language_prefs);
+        // Get the settings preferences
+        mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+        mSelectedLanguages = mPrefs.getString(Settings.PREF_SELECTED_LANGUAGES, "");
+        String[] languageList = mSelectedLanguages.split(",");
+        ArrayList<LocaleEntry> availableLanguages = getUniqueLocales();
+        PreferenceGroup parent = getPreferenceScreen();
+        final HashMap<Long, LocaleEntry> dictionaryIdLocaleMap = new HashMap<Long, LocaleEntry>();
+        final TreeMap<LocaleEntry, Boolean> localeHasDictionaryMap =
+                new TreeMap<LocaleEntry, Boolean>();
+        for (int i = 0; i < availableLanguages.size(); i++) {
+            LocaleEntry loc = availableLanguages.get(i);
+            Locale locale = loc.mLocale;
+            final Pair<Long, Boolean> hasDictionaryOrLayout = hasDictionaryOrLayout(locale);
+            final Long dictionaryId = hasDictionaryOrLayout.first;
+            final boolean hasLayout = hasDictionaryOrLayout.second;
+            final boolean hasDictionary = dictionaryId != null;
+            // Add this locale to the supported list if:
+            // 1) this locale has a layout/ 2) this locale has a dictionary
+            // If some locales have no layout but have a same dictionary, the shortest locale
+            // will be added to the supported list.
+            if (!hasLayout && !hasDictionary) {
+                continue;
+            }
+            if (hasLayout) {
+                localeHasDictionaryMap.put(loc, hasDictionary);
+            }
+            if (!hasDictionary) {
+                continue;
+            }
+            if (dictionaryIdLocaleMap.containsKey(dictionaryId)) {
+                final String newLocale = locale.toString();
+                final String oldLocale =
+                        dictionaryIdLocaleMap.get(dictionaryId).mLocale.toString();
+                // Check if this locale is more appropriate to be the candidate of the input locale.
+                if (oldLocale.length() <= newLocale.length() && !hasLayout) {
+                    // Don't add this new locale to the map<dictionary id, locale> if:
+                    // 1) the new locale's name is longer than the existing one, and
+                    // 2) the new locale doesn't have its layout
+                    continue;
+                }
+            }
+            dictionaryIdLocaleMap.put(dictionaryId, loc);
+        }
+
+        for (LocaleEntry localeEntry : dictionaryIdLocaleMap.values()) {
+            if (!localeHasDictionaryMap.containsKey(localeEntry)) {
+                localeHasDictionaryMap.put(localeEntry, true);
+            }
+        }
+
+        for (Entry<LocaleEntry, Boolean> entry : localeHasDictionaryMap.entrySet()) {
+            final LocaleEntry localeEntry = entry.getKey();
+            final Locale locale = localeEntry.mLocale;
+            final Boolean hasDictionary = entry.getValue();
+            CheckBoxPreference pref = new CheckBoxPreference(this);
+            pref.setTitle(localeEntry.mLabel);
+            boolean checked = isLocaleIn(locale, languageList);
+            pref.setChecked(checked);
+            if (hasDictionary) {
+                pref.setSummary(R.string.has_dictionary);
+            }
+            mLocaleMap.put(pref, locale);
+            parent.addPreference(pref);
+        }
+    }
+
+    private boolean isLocaleIn(Locale locale, String[] list) {
+        String lang = get5Code(locale);
+        for (int i = 0; i < list.length; i++) {
+            if (lang.equalsIgnoreCase(list[i])) return true;
+        }
+        return false;
+    }
+
+    private Pair<Long, Boolean> hasDictionaryOrLayout(Locale locale) {
+        if (locale == null) return new Pair<Long, Boolean>(null, false);
+        final Resources res = getResources();
+        final Locale saveLocale = Utils.setSystemLocale(res, locale);
+        final Long dictionaryId = DictionaryFactory.getDictionaryId(this, locale);
+        boolean hasLayout = false;
+
+        try {
+            final String localeStr = locale.toString();
+            final String[] layoutCountryCodes = KeyboardParser.parseKeyboardLocale(
+                    this, R.xml.kbd_qwerty).split(",", -1);
+            if (!TextUtils.isEmpty(localeStr) && layoutCountryCodes.length > 0) {
+                for (String s : layoutCountryCodes) {
+                    if (s.equals(localeStr)) {
+                        hasLayout = true;
+                        break;
+                    }
+                }
+            }
+        } catch (XmlPullParserException e) {
+        } catch (IOException e) {
+        }
+        Utils.setSystemLocale(res, saveLocale);
+        return new Pair<Long, Boolean>(dictionaryId, hasLayout);
+    }
+
+    private String get5Code(Locale locale) {
+        String country = locale.getCountry();
+        return locale.getLanguage()
+                + (TextUtils.isEmpty(country) ? "" : "_" + country);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        // Save the selected languages
+        String checkedLanguages = "";
+        PreferenceGroup parent = getPreferenceScreen();
+        int count = parent.getPreferenceCount();
+        for (int i = 0; i < count; i++) {
+            CheckBoxPreference pref = (CheckBoxPreference) parent.getPreference(i);
+            if (pref.isChecked()) {
+                checkedLanguages += get5Code(mLocaleMap.get(pref)) + ",";
+            }
+        }
+        if (checkedLanguages.length() < 1) checkedLanguages = null; // Save null
+        Editor editor = mPrefs.edit();
+        editor.putString(Settings.PREF_SELECTED_LANGUAGES, checkedLanguages);
+        SharedPreferencesCompat.apply(editor);
+    }
+
+    public ArrayList<LocaleEntry> getUniqueLocales() {
+        String[] locales = getAssets().getLocales();
+        Arrays.sort(locales);
+        ArrayList<LocaleEntry> uniqueLocales = new ArrayList<LocaleEntry>();
+
+        final int origSize = locales.length;
+        LocaleEntry[] preprocess = new LocaleEntry[origSize];
+        int finalSize = 0;
+        for (int i = 0 ; i < origSize; i++ ) {
+            String s = locales[i];
+            int len = s.length();
+            String language = "";
+            String country = "";
+            if (len == 5) {
+                language = s.substring(0, 2);
+                country = s.substring(3, 5);
+            } else if (len < 5) {
+                language = s;
+            }
+            Locale l = new Locale(language, country);
+
+            // Exclude languages that are not relevant to LatinIME
+            if (TextUtils.isEmpty(language)) {
+                continue;
+            }
+
+            if (finalSize == 0) {
+                preprocess[finalSize++] =
+                        new LocaleEntry(SubtypeSwitcher.getFullDisplayName(l, false), l);
+            } else {
+                if (s.equals("zz_ZZ")) {
+                    // ignore this locale
+                } else {
+                    final String displayName = SubtypeSwitcher.getFullDisplayName(l, false);
+                    preprocess[finalSize++] = new LocaleEntry(displayName, l);
+                }
+            }
+        }
+        for (int i = 0; i < finalSize ; i++) {
+            uniqueLocales.add(preprocess[i]);
+        }
+        return uniqueLocales;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LanguageSwitcher.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java
similarity index 69%
rename from java/src/com/android/inputmethod/latin/LanguageSwitcher.java
rename to java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java
index 2dd3038..1eedb5e 100644
--- a/java/src/com/android/inputmethod/latin/LanguageSwitcher.java
+++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java
@@ -14,11 +14,19 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.deprecated.languageswitcher;
+
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.Settings;
+import com.android.inputmethod.latin.SharedPreferencesCompat;
+import com.android.inputmethod.latin.Utils;
 
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.Editor;
+import android.content.res.Configuration;
 import android.text.TextUtils;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Locale;
@@ -28,10 +36,15 @@
  * input language that the user has selected.
  */
 public class LanguageSwitcher {
+    private static final String TAG = LanguageSwitcher.class.getSimpleName();
+
+    @SuppressWarnings("unused")
+    private static final String KEYBOARD_MODE = "keyboard";
+    private static final String[] EMPTY_STIRNG_ARRAY = new String[0];
 
     private final ArrayList<Locale> mLocales = new ArrayList<Locale>();
     private final LatinIME mIme;
-    private String[] mSelectedLanguageArray;
+    private String[] mSelectedLanguageArray = EMPTY_STIRNG_ARRAY;
     private String   mSelectedLanguages;
     private int      mCurrentIndex = 0;
     private String   mDefaultInputLanguage;
@@ -46,15 +59,32 @@
         return mLocales.size();
     }
 
+    public void onConfigurationChanged(Configuration conf, SharedPreferences prefs) {
+        final Locale newLocale = conf.locale;
+        if (!getSystemLocale().toString().equals(newLocale.toString())) {
+            loadLocales(prefs, newLocale);
+        }
+    }
+
     /**
      * Loads the currently selected input languages from shared preferences.
-     * @param sp
+     * @param sp shared preference for getting the current input language and enabled languages
+     * @param systemLocale the current system locale, stored for changing the current input language
+     * based on the system current system locale.
      * @return whether there was any change
      */
-    public boolean loadLocales(SharedPreferences sp) {
+    public boolean loadLocales(SharedPreferences sp, Locale systemLocale) {
+        if (LatinImeLogger.sDBG) {
+            Log.d(TAG, "load locales");
+        }
+        if (systemLocale != null) {
+            setSystemLocale(systemLocale);
+        }
         String selectedLanguages = sp.getString(Settings.PREF_SELECTED_LANGUAGES, null);
         String currentLanguage   = sp.getString(Settings.PREF_INPUT_LANGUAGE, null);
-        if (selectedLanguages == null || selectedLanguages.length() < 1) {
+        if (TextUtils.isEmpty(selectedLanguages)) {
+            mSelectedLanguageArray = EMPTY_STIRNG_ARRAY;
+            mSelectedLanguages = null;
             loadDefaults();
             if (mLocales.size() == 0) {
                 return false;
@@ -84,6 +114,9 @@
     }
 
     private void loadDefaults() {
+        if (LatinImeLogger.sDBG) {
+            Log.d(TAG, "load default locales:");
+        }
         mDefaultInputLocale = mIme.getResources().getConfiguration().locale;
         String country = mDefaultInputLocale.getCountry();
         mDefaultInputLanguage = mDefaultInputLocale.getLanguage() +
@@ -93,8 +126,7 @@
     private void constructLocales() {
         mLocales.clear();
         for (final String lang : mSelectedLanguageArray) {
-            final Locale locale = new Locale(lang.substring(0, 2),
-                    lang.length() > 4 ? lang.substring(3, 5) : "");
+            final Locale locale = Utils.constructLocaleFromString(lang);
             mLocales.add(locale);
         }
     }
@@ -112,14 +144,16 @@
     /**
      * Returns the list of enabled language codes.
      */
-    public String[] getEnabledLanguages() {
+    public String[] getEnabledLanguages(boolean allowImplicitlySelectedLanguages) {
+        if (mSelectedLanguageArray.length == 0 && allowImplicitlySelectedLanguages) {
+            return new String[] { mDefaultInputLanguage };
+        }
         return mSelectedLanguageArray;
     }
 
     /**
      * Returns the currently selected input locale, or the display locale if no specific
      * locale was selected for input.
-     * @return
      */
     public Locale getInputLocale() {
         if (getLocaleCount() == 0) return mDefaultInputLocale;
@@ -140,7 +174,6 @@
     /**
      * Returns the next input locale in the list. Wraps around to the beginning of the
      * list if we're at the end of the list.
-     * @return
      */
     public Locale getNextInputLocale() {
         if (getLocaleCount() == 0) return mDefaultInputLocale;
@@ -151,7 +184,7 @@
      * Sets the system locale (display UI) used for comparing with the input language.
      * @param locale the locale of the system
      */
-    public void setSystemLocale(Locale locale) {
+    private void setSystemLocale(Locale locale) {
         mSystemLocale = locale;
     }
 
@@ -159,14 +192,13 @@
      * Returns the system locale.
      * @return the system locale
      */
-    public Locale getSystemLocale() {
+    private Locale getSystemLocale() {
         return mSystemLocale;
     }
 
     /**
      * Returns the previous input locale in the list. Wraps around to the end of the
      * list if we're at the beginning of the list.
-     * @return
      */
     public Locale getPrevInputLocale() {
         if (getLocaleCount() == 0) return mDefaultInputLocale;
@@ -185,6 +217,15 @@
         mCurrentIndex = prevLocaleIndex();
     }
 
+    public void setLocale(String localeStr) {
+        final int N = mLocales.size();
+        for (int i = 0; i < N; ++i) {
+            if (mLocales.get(i).toString().equals(localeStr)) {
+                mCurrentIndex = i;
+            }
+        }
+    }
+
     public void persist(SharedPreferences prefs) {
         Editor editor = prefs.edit();
         editor.putString(Settings.PREF_INPUT_LANGUAGE, getInputLanguage());
diff --git a/java/src/com/android/inputmethod/deprecated/recorrection/Recorrection.java b/java/src/com/android/inputmethod/deprecated/recorrection/Recorrection.java
new file mode 100644
index 0000000..d40728d
--- /dev/null
+++ b/java/src/com/android/inputmethod/deprecated/recorrection/Recorrection.java
@@ -0,0 +1,287 @@
+/*
+ * 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.deprecated.recorrection;
+
+import com.android.inputmethod.compat.InputConnectionCompatUtils;
+import com.android.inputmethod.compat.SuggestionSpanUtils;
+import com.android.inputmethod.deprecated.VoiceProxy;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.latin.AutoCorrection;
+import com.android.inputmethod.latin.CandidateView;
+import com.android.inputmethod.latin.EditingUtils;
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.Settings;
+import com.android.inputmethod.latin.Suggest;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.TextEntryState;
+import com.android.inputmethod.latin.WordComposer;
+
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.text.TextUtils;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+
+import java.util.ArrayList;
+
+/**
+ * Manager of re-correction functionalities
+ */
+public class Recorrection implements SharedPreferences.OnSharedPreferenceChangeListener {
+    private static final Recorrection sInstance = new Recorrection();
+
+    private LatinIME mService;
+    private boolean mRecorrectionEnabled = false;
+    private final ArrayList<RecorrectionSuggestionEntries> mRecorrectionSuggestionsList =
+            new ArrayList<RecorrectionSuggestionEntries>();
+
+    public static Recorrection getInstance() {
+        return sInstance;
+    }
+
+    public static void init(LatinIME context, SharedPreferences prefs) {
+        if (context == null || prefs == null) {
+            return;
+        }
+        sInstance.initInternal(context, prefs);
+    }
+
+    private Recorrection() {
+    }
+
+    public boolean isRecorrectionEnabled() {
+        return mRecorrectionEnabled;
+    }
+
+    private void initInternal(LatinIME context, SharedPreferences prefs) {
+        if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED) {
+            mRecorrectionEnabled = false;
+            return;
+        }
+        updateRecorrectionEnabled(context.getResources(), prefs);
+        mService = context;
+        prefs.registerOnSharedPreferenceChangeListener(this);
+    }
+
+    public void checkRecorrectionOnStart() {
+        if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED || !mRecorrectionEnabled) return;
+
+        final InputConnection ic = mService.getCurrentInputConnection();
+        if (ic == null) return;
+        // There could be a pending composing span.  Clean it up first.
+        ic.finishComposingText();
+
+        if (mService.isShowingSuggestionsStrip() && mService.isSuggestionsRequested()) {
+            // First get the cursor position. This is required by setOldSuggestions(), so that
+            // it can pass the correct range to setComposingRegion(). At this point, we don't
+            // have valid values for mLastSelectionStart/End because onUpdateSelection() has
+            // not been called yet.
+            ExtractedTextRequest etr = new ExtractedTextRequest();
+            etr.token = 0; // anything is fine here
+            ExtractedText et = ic.getExtractedText(etr, 0);
+            if (et == null) return;
+            mService.setLastSelection(
+                    et.startOffset + et.selectionStart, et.startOffset + et.selectionEnd);
+
+            // Then look for possible corrections in a delayed fashion
+            if (!TextUtils.isEmpty(et.text) && mService.isCursorTouchingWord()) {
+                mService.mHandler.postUpdateOldSuggestions();
+            }
+        }
+    }
+
+    public void updateRecorrectionSelection(KeyboardSwitcher keyboardSwitcher,
+            CandidateView candidateView, int candidatesStart, int candidatesEnd,
+            int newSelStart, int newSelEnd, int oldSelStart, int lastSelectionStart,
+            int lastSelectionEnd, boolean hasUncommittedTypedChars) {
+        if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED || !mRecorrectionEnabled) return;
+        if (!mService.isShowingSuggestionsStrip()) return;
+        if (!keyboardSwitcher.isInputViewShown()) return;
+        if (!mService.isSuggestionsRequested()) return;
+        // Don't look for corrections if the keyboard is not visible
+        // Check if we should go in or out of correction mode.
+        if ((candidatesStart == candidatesEnd || newSelStart != oldSelStart || TextEntryState
+                .isRecorrecting())
+                && (newSelStart < newSelEnd - 1 || !hasUncommittedTypedChars)) {
+            if (mService.isCursorTouchingWord() || lastSelectionStart < lastSelectionEnd) {
+                mService.mHandler.cancelUpdateBigramPredictions();
+                mService.mHandler.postUpdateOldSuggestions();
+            } else {
+                abortRecorrection(false);
+                // If showing the "touch again to save" hint, do not replace it. Else,
+                // show the bigrams if we are at the end of the text, punctuation
+                // otherwise.
+                if (candidateView != null && !candidateView.isShowingAddToDictionaryHint()) {
+                    InputConnection ic = mService.getCurrentInputConnection();
+                    if (null == ic || !TextUtils.isEmpty(ic.getTextAfterCursor(1, 0))) {
+                        if (!mService.isShowingPunctuationList()) {
+                            mService.setPunctuationSuggestions();
+                        }
+                    } else {
+                        mService.mHandler.postUpdateBigramPredictions();
+                    }
+                }
+            }
+        }
+    }
+
+    public void saveRecorrectionSuggestion(WordComposer word, CharSequence result) {
+        if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED || !mRecorrectionEnabled) return;
+        if (word.size() <= 1) {
+            return;
+        }
+        // Skip if result is null. It happens in some edge case.
+        if (TextUtils.isEmpty(result)) {
+            return;
+        }
+
+        // Make a copy of the CharSequence, since it is/could be a mutable CharSequence
+        final String resultCopy = result.toString();
+        RecorrectionSuggestionEntries entry = new RecorrectionSuggestionEntries(
+                resultCopy, new WordComposer(word));
+        mRecorrectionSuggestionsList.add(entry);
+    }
+
+    public void clearWordsInHistory() {
+        mRecorrectionSuggestionsList.clear();
+    }
+
+    /**
+     * Tries to apply any typed alternatives for the word if we have any cached alternatives,
+     * otherwise tries to find new corrections and completions for the word.
+     * @param touching The word that the cursor is touching, with position information
+     * @return true if an alternative was found, false otherwise.
+     */
+    public boolean applyTypedAlternatives(WordComposer word, Suggest suggest,
+            KeyboardSwitcher keyboardSwitcher, EditingUtils.SelectedWord touching) {
+        if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED || !mRecorrectionEnabled) return false;
+        // If we didn't find a match, search for result in typed word history
+        WordComposer foundWord = null;
+        RecorrectionSuggestionEntries alternatives = null;
+        // Search old suggestions to suggest re-corrected suggestions.
+        for (RecorrectionSuggestionEntries entry : mRecorrectionSuggestionsList) {
+            if (TextUtils.equals(entry.getChosenWord(), touching.mWord)) {
+                foundWord = entry.mWordComposer;
+                alternatives = entry;
+                break;
+            }
+        }
+        // If we didn't find a match, at least suggest corrections as re-corrected suggestions.
+        if (foundWord == null
+                && (AutoCorrection.isValidWord(suggest.getUnigramDictionaries(),
+                        touching.mWord, true))) {
+            foundWord = new WordComposer();
+            for (int i = 0; i < touching.mWord.length(); i++) {
+                foundWord.add(touching.mWord.charAt(i),
+                        new int[] { touching.mWord.charAt(i) }, WordComposer.NOT_A_COORDINATE,
+                        WordComposer.NOT_A_COORDINATE);
+            }
+            foundWord.setFirstCharCapitalized(Character.isUpperCase(touching.mWord.charAt(0)));
+        }
+        // Found a match, show suggestions
+        if (foundWord != null || alternatives != null) {
+            if (alternatives == null) {
+                alternatives = new RecorrectionSuggestionEntries(touching.mWord, foundWord);
+            }
+            showRecorrections(suggest, keyboardSwitcher, alternatives);
+            if (foundWord != null) {
+                word.init(foundWord);
+            } else {
+                word.reset();
+            }
+            return true;
+        }
+        return false;
+    }
+
+
+    private void showRecorrections(Suggest suggest, KeyboardSwitcher keyboardSwitcher,
+            RecorrectionSuggestionEntries entries) {
+        SuggestedWords.Builder builder = entries.getAlternatives(suggest, keyboardSwitcher);
+        builder.setTypedWordValid(false).setHasMinimalSuggestion(false);
+        mService.showSuggestions(builder.build(), entries.getOriginalWord());
+    }
+
+    public void fetchAndDisplayRecorrectionSuggestions(VoiceProxy voiceProxy,
+            CandidateView candidateView, Suggest suggest, KeyboardSwitcher keyboardSwitcher,
+            WordComposer word, boolean hasUncommittedTypedChars, int lastSelectionStart,
+            int lastSelectionEnd, String wordSeparators) {
+        if (!InputConnectionCompatUtils.RECORRECTION_SUPPORTED) return;
+        if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED || !mRecorrectionEnabled) return;
+        voiceProxy.setShowingVoiceSuggestions(false);
+        if (candidateView != null && candidateView.isShowingAddToDictionaryHint()) {
+            return;
+        }
+        InputConnection ic = mService.getCurrentInputConnection();
+        if (ic == null) return;
+        if (!hasUncommittedTypedChars) {
+            // Extract the selected or touching text
+            EditingUtils.SelectedWord touching = EditingUtils.getWordAtCursorOrSelection(ic,
+                    lastSelectionStart, lastSelectionEnd, wordSeparators);
+
+            if (touching != null && touching.mWord.length() > 1) {
+                ic.beginBatchEdit();
+
+                if (applyTypedAlternatives(word, suggest, keyboardSwitcher, touching)
+                        || voiceProxy.applyVoiceAlternatives(touching)) {
+                    TextEntryState.selectedForRecorrection();
+                    InputConnectionCompatUtils.underlineWord(ic, touching);
+                } else {
+                    abortRecorrection(true);
+                }
+
+                ic.endBatchEdit();
+            } else {
+                abortRecorrection(true);
+                mService.updateBigramPredictions();
+            }
+        } else {
+            abortRecorrection(true);
+        }
+    }
+
+    public void abortRecorrection(boolean force) {
+        if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED) return;
+        if (force || TextEntryState.isRecorrecting()) {
+            TextEntryState.onAbortRecorrection();
+            mService.setCandidatesViewShown(mService.isCandidateStripVisible());
+            mService.getCurrentInputConnection().finishComposingText();
+            mService.clearSuggestions();
+        }
+    }
+
+    public void updateRecorrectionEnabled(Resources res, SharedPreferences prefs) {
+        // If the option should not be shown, do not read the re-correction preference
+        // but always use the default setting defined in the resources.
+        if (res.getBoolean(R.bool.config_enable_show_recorrection_option)) {
+            mRecorrectionEnabled = prefs.getBoolean(Settings.PREF_RECORRECTION_ENABLED,
+                    res.getBoolean(R.bool.config_default_recorrection_enabled));
+        } else {
+            mRecorrectionEnabled = res.getBoolean(R.bool.config_default_recorrection_enabled);
+        }
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+        if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED) return;
+        if (key.equals(Settings.PREF_RECORRECTION_ENABLED)) {
+            updateRecorrectionEnabled(mService.getResources(), prefs);
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java
new file mode 100644
index 0000000..5e6c870
--- /dev/null
+++ b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java
@@ -0,0 +1,62 @@
+/*
+ * 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.deprecated.recorrection;
+
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.latin.Suggest;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.WordComposer;
+
+import android.text.TextUtils;
+
+public class RecorrectionSuggestionEntries {
+    public final CharSequence mChosenWord;
+    public final WordComposer mWordComposer;
+
+    public RecorrectionSuggestionEntries(CharSequence chosenWord, WordComposer wordComposer) {
+        mChosenWord = chosenWord;
+        mWordComposer = wordComposer;
+    }
+
+    public CharSequence getChosenWord() {
+        return mChosenWord;
+    }
+
+    public CharSequence getOriginalWord() {
+        return mWordComposer.getTypedWord();
+    }
+
+    public SuggestedWords.Builder getAlternatives(
+            Suggest suggest, KeyboardSwitcher keyboardSwitcher) {
+        return getTypedSuggestions(suggest, keyboardSwitcher, mWordComposer);
+    }
+
+    @Override
+    public int hashCode() {
+        return mChosenWord.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof CharSequence && TextUtils.equals(mChosenWord, (CharSequence)o);
+    }
+
+    private static SuggestedWords.Builder getTypedSuggestions(
+            Suggest suggest, KeyboardSwitcher keyboardSwitcher, WordComposer word) {
+        return suggest.getSuggestedWordBuilder(keyboardSwitcher.getKeyboardView(), word, null);
+    }
+}
diff --git a/java/src/com/android/inputmethod/voice/FieldContext.java b/java/src/com/android/inputmethod/deprecated/voice/FieldContext.java
similarity index 98%
rename from java/src/com/android/inputmethod/voice/FieldContext.java
rename to java/src/com/android/inputmethod/deprecated/voice/FieldContext.java
index 95c7f5b..3c79cc2 100644
--- a/java/src/com/android/inputmethod/voice/FieldContext.java
+++ b/java/src/com/android/inputmethod/deprecated/voice/FieldContext.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.voice;
+package com.android.inputmethod.deprecated.voice;
 
 import android.os.Bundle;
 import android.util.Log;
diff --git a/java/src/com/android/inputmethod/voice/Hints.java b/java/src/com/android/inputmethod/deprecated/voice/Hints.java
similarity index 99%
rename from java/src/com/android/inputmethod/voice/Hints.java
rename to java/src/com/android/inputmethod/deprecated/voice/Hints.java
index f568909..06b2343 100644
--- a/java/src/com/android/inputmethod/voice/Hints.java
+++ b/java/src/com/android/inputmethod/deprecated/voice/Hints.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.voice;
+package com.android.inputmethod.deprecated.voice;
 
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SharedPreferencesCompat;
diff --git a/java/src/com/android/inputmethod/voice/RecognitionView.java b/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
similarity index 99%
rename from java/src/com/android/inputmethod/voice/RecognitionView.java
rename to java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
index d26a877..dcb826e 100644
--- a/java/src/com/android/inputmethod/voice/RecognitionView.java
+++ b/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.voice;
+package com.android.inputmethod.deprecated.voice;
 
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SubtypeSwitcher;
@@ -50,7 +50,6 @@
  * plays beeps, shows errors, etc.
  */
 public class RecognitionView {
-    @SuppressWarnings("unused")
     private static final String TAG = "RecognitionView";
 
     private Handler mUiHandler;  // Reference to UI thread
diff --git a/java/src/com/android/inputmethod/voice/SettingsUtil.java b/java/src/com/android/inputmethod/deprecated/voice/SettingsUtil.java
similarity index 98%
rename from java/src/com/android/inputmethod/voice/SettingsUtil.java
rename to java/src/com/android/inputmethod/deprecated/voice/SettingsUtil.java
index 372e8d6..855a09a 100644
--- a/java/src/com/android/inputmethod/voice/SettingsUtil.java
+++ b/java/src/com/android/inputmethod/deprecated/voice/SettingsUtil.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.voice;
+package com.android.inputmethod.deprecated.voice;
 
 import android.content.ContentResolver;
 import android.provider.Settings;
diff --git a/java/src/com/android/inputmethod/voice/SoundIndicator.java b/java/src/com/android/inputmethod/deprecated/voice/SoundIndicator.java
similarity index 98%
rename from java/src/com/android/inputmethod/voice/SoundIndicator.java
rename to java/src/com/android/inputmethod/deprecated/voice/SoundIndicator.java
index 40b6899..25b3140 100644
--- a/java/src/com/android/inputmethod/voice/SoundIndicator.java
+++ b/java/src/com/android/inputmethod/deprecated/voice/SoundIndicator.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.voice;
+package com.android.inputmethod.deprecated.voice;
 
 import android.content.Context;
 import android.graphics.Bitmap;
diff --git a/java/src/com/android/inputmethod/voice/VoiceInput.java b/java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java
similarity index 99%
rename from java/src/com/android/inputmethod/voice/VoiceInput.java
rename to java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java
index 392eea3..b718ebb 100644
--- a/java/src/com/android/inputmethod/voice/VoiceInput.java
+++ b/java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.voice;
+package com.android.inputmethod.deprecated.voice;
 
 import com.android.inputmethod.latin.EditingUtils;
 import com.android.inputmethod.latin.LatinImeLogger;
@@ -129,7 +129,7 @@
     private int mAfterVoiceInputSelectionSpan = 0;
 
     private int mState = DEFAULT;
-    
+
     private final static int MSG_RESET = 1;
 
     private final Handler mHandler = new Handler() {
@@ -192,7 +192,7 @@
         }
 
         mBlacklist = new Whitelist();
-        mBlacklist.addApp("com.android.setupwizard");
+        mBlacklist.addApp("com.google.android.setupwizard");
     }
 
     public void setCursorPos(int pos) {
diff --git a/java/src/com/android/inputmethod/voice/VoiceInputLogger.java b/java/src/com/android/inputmethod/deprecated/voice/VoiceInputLogger.java
similarity index 95%
rename from java/src/com/android/inputmethod/voice/VoiceInputLogger.java
rename to java/src/com/android/inputmethod/deprecated/voice/VoiceInputLogger.java
index 07f2bd1..22e8207 100644
--- a/java/src/com/android/inputmethod/voice/VoiceInputLogger.java
+++ b/java/src/com/android/inputmethod/deprecated/voice/VoiceInputLogger.java
@@ -14,10 +14,10 @@
  * the License.
  */
 
-package com.android.inputmethod.voice;
+package com.android.inputmethod.deprecated.voice;
 
 import com.android.common.speech.LoggingEvents;
-import com.android.common.userhappiness.UserHappinessSignals;
+import com.android.inputmethod.deprecated.compat.VoiceInputLoggerCompatUtils;
 
 import android.content.Context;
 import android.content.Intent;
@@ -212,13 +212,12 @@
         setHasLoggingInfo(true);
         Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.TEXT_MODIFIED);
         i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_LENGTH, suggestionLength);
-        i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_REPLACED_LENGTH, replacedPhraseLength);
+        i.putExtra(VoiceInputLoggerCompatUtils.EXTRA_TEXT_REPLACED_LENGTH, replacedPhraseLength);
         i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_TYPE,
                 LoggingEvents.VoiceIme.TEXT_MODIFIED_TYPE_CHOOSE_SUGGESTION);
-
         i.putExtra(LoggingEvents.VoiceIme.EXTRA_N_BEST_CHOOSE_INDEX, index);
-        i.putExtra(LoggingEvents.VoiceIme.EXTRA_BEFORE_N_BEST_CHOOSE, before);
-        i.putExtra(LoggingEvents.VoiceIme.EXTRA_AFTER_N_BEST_CHOOSE, after);
+        i.putExtra(VoiceInputLoggerCompatUtils.EXTRA_BEFORE_N_BEST_CHOOSE, before);
+        i.putExtra(VoiceInputLoggerCompatUtils.EXTRA_AFTER_N_BEST_CHOOSE, after);
         mContext.sendBroadcast(i);
     }
 
@@ -257,7 +256,7 @@
         // 2. type subject in subject field
         // 3. speak message in message field
         // 4. press send
-        UserHappinessSignals.setHasVoiceLoggingInfo(hasLoggingInfo);
+        VoiceInputLoggerCompatUtils.setHasVoiceLoggingInfoCompat(hasLoggingInfo);
     }
 
     private boolean hasLoggingInfo(){
diff --git a/java/src/com/android/inputmethod/voice/WaveformImage.java b/java/src/com/android/inputmethod/deprecated/voice/WaveformImage.java
similarity index 98%
rename from java/src/com/android/inputmethod/voice/WaveformImage.java
rename to java/src/com/android/inputmethod/deprecated/voice/WaveformImage.java
index b0ea99b..8ed279f 100644
--- a/java/src/com/android/inputmethod/voice/WaveformImage.java
+++ b/java/src/com/android/inputmethod/deprecated/voice/WaveformImage.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.voice;
+package com.android.inputmethod.deprecated.voice;
 
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
diff --git a/java/src/com/android/inputmethod/voice/Whitelist.java b/java/src/com/android/inputmethod/deprecated/voice/Whitelist.java
similarity index 97%
rename from java/src/com/android/inputmethod/voice/Whitelist.java
rename to java/src/com/android/inputmethod/deprecated/voice/Whitelist.java
index adbd591..6c5f52a 100644
--- a/java/src/com/android/inputmethod/voice/Whitelist.java
+++ b/java/src/com/android/inputmethod/deprecated/voice/Whitelist.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.voice;
+package com.android.inputmethod.deprecated.voice;
 
 import android.os.Bundle;
 
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 98a7f98..2850c95 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -23,8 +23,13 @@
 import android.text.TextUtils;
 import android.util.Xml;
 
-import com.android.inputmethod.keyboard.KeyStyles.KeyStyle;
-import com.android.inputmethod.keyboard.KeyboardParser.ParseException;
+import com.android.inputmethod.keyboard.internal.KeyStyles;
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.internal.KeyboardParser;
+import com.android.inputmethod.keyboard.internal.PopupCharactersParser;
+import com.android.inputmethod.keyboard.internal.Row;
+import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle;
+import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException;
 import com.android.inputmethod.latin.R;
 
 import java.util.ArrayList;
@@ -37,32 +42,36 @@
      * The key code (unicode or custom code) that this key generates.
      */
     public final int mCode;
-    /** The unicode that this key generates in manual temporary upper case mode. */
-    public final int mManualTemporaryUpperCaseCode;
 
     /** Label to display */
     public final CharSequence mLabel;
+    /** Hint letter to display on the key in conjunction with the label */
+    public final CharSequence mHintLetter;
     /** Option of the label */
     public final int mLabelOption;
+    public static final int LABEL_OPTION_ALIGN_LEFT = 0x01;
+    public static final int LABEL_OPTION_ALIGN_RIGHT = 0x02;
+    public static final int LABEL_OPTION_ALIGN_BOTTOM = 0x08;
+    public static final int LABEL_OPTION_FONT_NORMAL = 0x10;
+    public static final int LABEL_OPTION_FONT_FIXED_WIDTH = 0x20;
+    public static final int LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO = 0x40;
+    private static final int LABEL_OPTION_POPUP_HINT = 0x80;
+    private static final int LABEL_OPTION_HAS_UPPERCASE_LETTER = 0x100;
 
     /** Icon to display instead of a label. Icon takes precedence over a label */
     private Drawable mIcon;
     /** Preview version of the icon, for the preview popup */
     private Drawable mPreviewIcon;
-    /** Hint icon to display on the key in conjunction with the label */
-    public final Drawable mHintIcon;
-    /**
-     * The hint icon to display on the key when keyboard is in manual temporary upper case
-     * mode.
-     */
-    public final Drawable mManualTemporaryUpperCaseHintIcon;
 
     /** Width of the key, not including the gap */
     public final int mWidth;
     /** Height of the key, not including the gap */
     public final int mHeight;
-    /** The horizontal gap before this key */
+    /** The horizontal gap around this key */
     public final int mGap;
+    /** The visual insets */
+    public final int mVisualInsetsLeft;
+    public final int mVisualInsetsRight;
     /** Whether this key is sticky, i.e., a toggle key */
     public final boolean mSticky;
     /** X coordinate of the key in the keyboard layout */
@@ -83,8 +92,8 @@
      * {@link Keyboard#EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM}.
      */
     public final int mEdgeFlags;
-    /** Whether this is a modifier key, such as Shift or Alt */
-    public final boolean mModifier;
+    /** Whether this is a functional key which has different key top than normal key */
+    public final boolean mFunctional;
     /** Whether this key repeats itself when held down */
     public final boolean mRepeatable;
 
@@ -92,11 +101,15 @@
     private final Keyboard mKeyboard;
 
     /** The current pressed state of this key */
-    public boolean mPressed;
-    /** If this is a sticky key, is it on? */
-    public boolean mOn;
+    private boolean mPressed;
+    /** If this is a sticky key, is its highlight on? */
+    private boolean mHighlightOn;
     /** Key is enabled and responds on press */
-    public boolean mEnabled = true;
+    private boolean mEnabled = true;
+
+    // keyWidth constants
+    private static final int KEYWIDTH_FILL_RIGHT = 0;
+    private static final int KEYWIDTH_FILL_BOTH = -1;
 
     private final static int[] KEY_STATE_NORMAL_ON = {
         android.R.attr.state_checkable,
@@ -144,13 +157,12 @@
         mKeyboard = keyboard;
         mHeight = height - keyboard.getVerticalGap();
         mGap = keyboard.getHorizontalGap();
+        mVisualInsetsLeft = mVisualInsetsRight = 0;
         mWidth = width - mGap;
         mEdgeFlags = edgeFlags;
-        mHintIcon = null;
-        mManualTemporaryUpperCaseHintIcon = null;
-        mManualTemporaryUpperCaseCode = Keyboard.CODE_DUMMY;
+        mHintLetter = null;
         mLabelOption = 0;
-        mModifier = false;
+        mFunctional = false;
         mSticky = false;
         mRepeatable = false;
         mPopupCharacters = null;
@@ -159,7 +171,7 @@
         mLabel = PopupCharactersParser.getLabel(popupSpecification);
         mOutputText = PopupCharactersParser.getOutputText(popupSpecification);
         mCode = PopupCharactersParser.getCode(res, popupSpecification);
-        mIcon = PopupCharactersParser.getIcon(res, popupSpecification);
+        mIcon = keyboard.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification));
         // Horizontal gap is divided equally to both sides of the key.
         mX = x + mGap / 2;
         mY = y;
@@ -216,17 +228,17 @@
             if (keyXPos < 0) {
                 // If keyXPos is negative, the actual x-coordinate will be k + 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 (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 (keyWidth == 0) {
+            if (keyWidth == 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 (keyWidth < 0) {
+            } else if (keyWidth <= 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.
@@ -251,26 +263,29 @@
                     mKeyboard.getMaxPopupKeyboardColumn());
 
             mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false);
-            mModifier = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isModifier, false);
+            mFunctional = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isFunctional, false);
             mSticky = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isSticky, false);
             mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true);
             mEdgeFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyEdgeFlags, 0)
                     | row.mRowEdgeFlags;
 
-            mPreviewIcon = style.getDrawable(keyAttr, R.styleable.Keyboard_Key_iconPreview);
+            final KeyboardIconsSet iconsSet = mKeyboard.mIconsSet;
+            mVisualInsetsLeft = KeyboardParser.getDimensionOrFraction(keyAttr,
+                    R.styleable.Keyboard_Key_visualInsetsLeft, mKeyboard.getDisplayHeight(), 0);
+            mVisualInsetsRight = KeyboardParser.getDimensionOrFraction(keyAttr,
+                    R.styleable.Keyboard_Key_visualInsetsRight, mKeyboard.getDisplayHeight(), 0);
+            mPreviewIcon = iconsSet.getIcon(style.getInt(
+                    keyAttr, R.styleable.Keyboard_Key_keyIconPreview,
+                    KeyboardIconsSet.ICON_UNDEFINED));
             Keyboard.setDefaultBounds(mPreviewIcon);
-            mIcon = style.getDrawable(keyAttr, R.styleable.Keyboard_Key_keyIcon);
+            mIcon = iconsSet.getIcon(style.getInt(
+                    keyAttr, R.styleable.Keyboard_Key_keyIcon,
+                    KeyboardIconsSet.ICON_UNDEFINED));
             Keyboard.setDefaultBounds(mIcon);
-            mHintIcon = style.getDrawable(keyAttr, R.styleable.Keyboard_Key_keyHintIcon);
-            Keyboard.setDefaultBounds(mHintIcon);
-            mManualTemporaryUpperCaseHintIcon = style.getDrawable(keyAttr,
-                    R.styleable.Keyboard_Key_manualTemporaryUpperCaseHintIcon);
-            Keyboard.setDefaultBounds(mManualTemporaryUpperCaseHintIcon);
+            mHintLetter = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLetter);
 
             mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel);
             mLabelOption = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption, 0);
-            mManualTemporaryUpperCaseCode = style.getInt(keyAttr,
-                    R.styleable.Keyboard_Key_manualTemporaryUpperCaseCode, Keyboard.CODE_DUMMY);
             mOutputText = style.getText(keyAttr, R.styleable.Keyboard_Key_keyOutputText);
             // Choose the first letter of the label as primary code if not
             // specified.
@@ -284,8 +299,9 @@
                 mCode = Keyboard.CODE_DUMMY;
             }
 
-            final Drawable shiftedIcon = style.getDrawable(keyAttr,
-                    R.styleable.Keyboard_Key_shiftedIcon);
+            final Drawable shiftedIcon = iconsSet.getIcon(style.getInt(
+                    keyAttr, R.styleable.Keyboard_Key_keyIconShifted,
+                    KeyboardIconsSet.ICON_UNDEFINED));
             if (shiftedIcon != null)
                 mKeyboard.getShiftedIcons().put(this, shiftedIcon);
         } finally {
@@ -293,6 +309,14 @@
         }
     }
 
+    public boolean hasPopupHint() {
+        return (mLabelOption & LABEL_OPTION_POPUP_HINT) != 0;
+    }
+
+    public boolean hasUppercaseLetter() {
+        return (mLabelOption & LABEL_OPTION_HAS_UPPERCASE_LETTER) != 0;
+    }
+
     private static boolean isDigitPopupCharacter(CharSequence label) {
         return label != null && label.length() == 1 && Character.isDigit(label.charAt(0));
     }
@@ -342,26 +366,31 @@
     /**
      * Informs the key that it has been pressed, in case it needs to change its appearance or
      * state.
-     * @see #onReleased(boolean)
+     * @see #onReleased()
      */
     public void onPressed() {
-        mPressed = !mPressed;
+        mPressed = true;
     }
 
     /**
-     * Changes the pressed state of the key. If it is a sticky key, it will also change the
-     * toggled state of the key if the finger was release inside.
-     * @param inside whether the finger was released inside the key
+     * Informs the key that it has been released, in case it needs to change its appearance or
+     * state.
      * @see #onPressed()
      */
-    public void onReleased(boolean inside) {
-        mPressed = !mPressed;
-        if (mSticky && !mKeyboard.isShiftLockEnabled(this))
-            mOn = !mOn;
+    public void onReleased() {
+        mPressed = false;
     }
 
-    public boolean isInside(int x, int y) {
-        return mKeyboard.isInside(this, x, y);
+    public void setHighlightOn(boolean highlightOn) {
+        mHighlightOn = highlightOn;
+    }
+
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        mEnabled = enabled;
     }
 
     /**
@@ -404,20 +433,14 @@
         return dx * dx + dy * dy;
     }
 
-    // sticky is used for shift key.  If a key is not sticky and is modifier,
-    // the key will be treated as functional.
-    private boolean isFunctionalKey() {
-        return !mSticky && mModifier;
-    }
-
     /**
      * Returns the drawable state for the key, based on the current state and type of the key.
      * @return the drawable state of the key.
      * @see android.graphics.drawable.StateListDrawable#setState(int[])
      */
     public int[] getCurrentDrawableState() {
-        final boolean pressed = mEnabled && mPressed;
-        if (isFunctionalKey()) {
+        final boolean pressed = mPressed;
+        if (!mSticky && mFunctional) {
             if (pressed) {
                 return KEY_STATE_FUNCTIONAL_PRESSED;
             } else {
@@ -427,7 +450,7 @@
 
         int[] states = KEY_STATE_NORMAL;
 
-        if (mOn) {
+        if (mHighlightOn) {
             if (pressed) {
                 states = KEY_STATE_PRESSED_ON;
             } else {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index a8346c5..7add43a 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -16,36 +16,35 @@
 
 package com.android.inputmethod.keyboard;
 
+import android.util.Log;
+
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 
-public abstract class KeyDetector {
-    public static final int NOT_A_KEY = -1;
+public class KeyDetector {
+    private static final String TAG = KeyDetector.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
     public static final int NOT_A_CODE = -1;
+    public static final int NOT_A_KEY = -1;
 
-    protected Keyboard mKeyboard;
+    private Keyboard mKeyboard;
+    private int mCorrectionX;
+    private int mCorrectionY;
+    private boolean mProximityCorrectOn;
+    private int mProximityThresholdSquare;
 
-    private Key[] mKeys;
+    // working area
+    private static final int MAX_NEARBY_KEYS = 12;
+    private final int[] mDistances = new int[MAX_NEARBY_KEYS];
+    private final int[] mIndices = new int[MAX_NEARBY_KEYS];
 
-    protected int mCorrectionX;
-
-    protected int mCorrectionY;
-
-    protected boolean mProximityCorrectOn;
-
-    protected int mProximityThresholdSquare;
-
-    public Key[] setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
+    public void setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
         if (keyboard == null)
             throw new NullPointerException();
         mCorrectionX = (int)correctionX;
         mCorrectionY = (int)correctionY;
         mKeyboard = keyboard;
-        List<Key> keys = mKeyboard.getKeys();
-        Key[] array = keys.toArray(new Key[keys.size()]);
-        mKeys = array;
-        return array;
     }
 
     protected int getTouchX(int x) {
@@ -56,11 +55,11 @@
         return y + mCorrectionY;
     }
 
-    protected Key[] getKeys() {
-        if (mKeys == null)
+    protected List<Key> getKeys() {
+        if (mKeyboard == null)
             throw new IllegalStateException("keyboard isn't set");
         // mKeyboard is guaranteed not to be null at setKeybaord() method if mKeys is not null
-        return mKeys;
+        return mKeyboard.getKeys();
     }
 
     public void setProximityCorrectionEnabled(boolean enabled) {
@@ -76,6 +75,17 @@
     }
 
     /**
+     * Computes maximum size of the array that can contain all nearby key indices returned by
+     * {@link #getKeyIndexAndNearbyCodes}.
+     *
+     * @return Returns maximum size of the array that can contain all nearby key indices returned
+     *         by {@link #getKeyIndexAndNearbyCodes}.
+     */
+    protected int getMaxNearbyKeys() {
+        return MAX_NEARBY_KEYS;
+    }
+
+    /**
      * Allocates array that can hold all key indices returned by {@link #getKeyIndexAndNearbyCodes}
      * method. The maximum size of the array should be computed by {@link #getMaxNearbyKeys}.
      *
@@ -89,14 +99,64 @@
         return codes;
     }
 
+    private void initializeNearbyKeys() {
+        Arrays.fill(mDistances, Integer.MAX_VALUE);
+        Arrays.fill(mIndices, NOT_A_KEY);
+    }
+
     /**
-     * Computes maximum size of the array that can contain all nearby key indices returned by
-     * {@link #getKeyIndexAndNearbyCodes}.
+     * Insert the key into nearby keys buffer and sort nearby keys by ascending order of distance.
+     * If the distance of two keys are the same, the key which the point is on should be considered
+     * as a closer one.
      *
-     * @return Returns maximum size of the array that can contain all nearby key indices returned
-     *         by {@link #getKeyIndexAndNearbyCodes}.
+     * @param keyIndex index of the key.
+     * @param distance distance between the key's edge and user touched point.
+     * @param isOnKey true if the point is on the key.
+     * @return order of the key in the nearby buffer, 0 if it is the nearest key.
      */
-    abstract protected int getMaxNearbyKeys();
+    private int sortNearbyKeys(int keyIndex, int distance, boolean isOnKey) {
+        final int[] distances = mDistances;
+        final int[] indices = mIndices;
+        for (int insertPos = 0; insertPos < distances.length; insertPos++) {
+            final int comparingDistance = distances[insertPos];
+            if (distance < comparingDistance || (distance == comparingDistance && isOnKey)) {
+                final int nextPos = insertPos + 1;
+                if (nextPos < distances.length) {
+                    System.arraycopy(distances, insertPos, distances, nextPos,
+                            distances.length - nextPos);
+                    System.arraycopy(indices, insertPos, indices, nextPos,
+                            indices.length - nextPos);
+                }
+                distances[insertPos] = distance;
+                indices[insertPos] = keyIndex;
+                return insertPos;
+            }
+        }
+        return distances.length;
+    }
+
+    private void getNearbyKeyCodes(final int[] allCodes) {
+        final List<Key> keys = getKeys();
+        final int[] indices = mIndices;
+
+        // allCodes[0] should always have the key code even if it is a non-letter key.
+        if (indices[0] == NOT_A_KEY) {
+            allCodes[0] = NOT_A_CODE;
+            return;
+        }
+
+        int numCodes = 0;
+        for (int j = 0; j < indices.length && numCodes < allCodes.length; j++) {
+            final int index = indices[j];
+            if (index == NOT_A_KEY)
+                break;
+            final int code = keys.get(index).mCode;
+            // filter out a non-letter key from nearby keys
+            if (code < Keyboard.CODE_SPACE)
+                continue;
+            allCodes[numCodes++] = code;
+        }
+    }
 
     /**
      * Finds all possible nearby key indices around a touch event point and returns the nearest key
@@ -109,32 +169,34 @@
      * @param allCodes All nearby key code except functional key are returned in this array
      * @return The nearest key index
      */
-    abstract public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes);
+    public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
+        final List<Key> keys = getKeys();
+        final int touchX = getTouchX(x);
+        final int touchY = getTouchY(y);
 
-    /**
-     * Compute the most common key width in order to use it as proximity key detection threshold.
-     *
-     * @param keyboard The keyboard to compute the most common key width
-     * @return The most common key width in the keyboard
-     */
-    public static int getMostCommonKeyWidth(final Keyboard keyboard) {
-        if (keyboard == null) return 0;
-        final List<Key> keys = keyboard.getKeys();
-        if (keys == null || keys.size() == 0) return 0;
-        final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
-        int maxCount = 0;
-        int mostCommonWidth = 0;
-        for (final Key key : keys) {
-            final Integer width = key.mWidth + key.mGap;
-            Integer count = histogram.get(width);
-            if (count == null)
-                count = 0;
-            histogram.put(width, ++count);
-            if (count > maxCount) {
-                maxCount = count;
-                mostCommonWidth = width;
+        initializeNearbyKeys();
+        int primaryIndex = NOT_A_KEY;
+        for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) {
+            final Key key = keys.get(index);
+            final boolean isOnKey = key.isOnKey(touchX, touchY);
+            final int distance = key.squaredDistanceToEdge(touchX, touchY);
+            if (isOnKey || (mProximityCorrectOn && distance < mProximityThresholdSquare)) {
+                final int insertedPosition = sortNearbyKeys(index, distance, isOnKey);
+                if (insertedPosition == 0 && isOnKey)
+                    primaryIndex = index;
             }
         }
-        return mostCommonWidth;
+
+        if (allCodes != null && allCodes.length > 0) {
+            getNearbyKeyCodes(allCodes);
+            if (DEBUG) {
+                Log.d(TAG, "x=" + x + " y=" + y
+                        + " primary="
+                        + (primaryIndex == NOT_A_KEY ? "none" : keys.get(primaryIndex).mCode)
+                        + " codes=" + Arrays.toString(allCodes));
+            }
+        }
+
+        return primaryIndex;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 0b545d9..20327c5 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -16,15 +16,18 @@
 
 package com.android.inputmethod.keyboard;
 
-import com.android.inputmethod.latin.R;
-
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.util.Log;
 
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.internal.KeyboardParser;
+import com.android.inputmethod.keyboard.internal.KeyboardShiftState;
+import com.android.inputmethod.latin.R;
+
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -51,7 +54,7 @@
  * </pre>
  */
 public class Keyboard {
-    private static final String TAG = "Keyboard";
+    private static final String TAG = Keyboard.class.getSimpleName();
 
     public static final int EDGE_LEFT = 0x01;
     public static final int EDGE_RIGHT = 0x02;
@@ -63,24 +66,22 @@
     public static final int CODE_TAB = '\t';
     public static final int CODE_SPACE = ' ';
     public static final int CODE_PERIOD = '.';
+    public static final int CODE_DASH = '-';
+    public static final int CODE_SINGLE_QUOTE = '\'';
+    public static final int CODE_DOUBLE_QUOTE = '"';
 
     /** Special keys code.  These should be aligned with values/keycodes.xml */
     public static final int CODE_DUMMY = 0;
     public static final int CODE_SHIFT = -1;
     public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
-    public static final int CODE_CANCEL = -3;
-    public static final int CODE_DONE = -4;
+    public static final int CODE_CAPSLOCK = -3;
+    public static final int CODE_CANCEL = -4;
     public static final int CODE_DELETE = -5;
-    public static final int CODE_ALT = -6;
+    public static final int CODE_SETTINGS = -6;
+    public static final int CODE_SETTINGS_LONGPRESS = -7;
+    public static final int CODE_SHORTCUT = -8;
     // Code value representing the code is not specified.
     public static final int CODE_UNSPECIFIED = -99;
-    public static final int CODE_SETTINGS = -100;
-    public static final int CODE_SETTINGS_LONGPRESS = -101;
-    // TODO: remove this once LatinIME stops referring to this.
-    public static final int CODE_VOICE = -102;
-    public static final int CODE_CAPSLOCK = -103;
-    public static final int CODE_NEXT_LANGUAGE = -104;
-    public static final int CODE_PREV_LANGUAGE = -105;
 
     /** Horizontal gap default for all rows */
     private int mDefaultHorizontalGap;
@@ -128,8 +129,12 @@
     /** Height of keyboard */
     private int mKeyboardHeight;
 
+    private int mMostCommonKeyWidth = 0;
+
     public final KeyboardId mId;
 
+    public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
+
     // Variables for pre-computing nearest keys.
 
     // TODO: Change GRID_WIDTH and GRID_HEIGHT to private.
@@ -151,30 +156,28 @@
      * @param context the application or service context
      * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
      * @param id keyboard identifier
+     * @param width keyboard width
      */
-    public Keyboard(Context context, int xmlLayoutResId, KeyboardId id) {
-        this(context, xmlLayoutResId, id,
-                context.getResources().getDisplayMetrics().widthPixels,
-                context.getResources().getDisplayMetrics().heightPixels);
-    }
 
-    private Keyboard(Context context, int xmlLayoutResId, KeyboardId id, int width,
-            int height) {
-        Resources res = context.getResources();
+    public Keyboard(Context context, int xmlLayoutResId, KeyboardId id, int width) {
+        final Resources res = context.getResources();
         GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
         GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
         GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;
 
-        mDisplayWidth = width;
-        mDisplayHeight = height;
+        final int horizontalEdgesPadding = (int)res.getDimension(
+                R.dimen.keyboard_horizontal_edges_padding);
+        mDisplayWidth = width - horizontalEdgesPadding * 2;
+        // TODO: Adjust the height by referring to the height of area available for drawing as well.
+        mDisplayHeight = res.getDisplayMetrics().heightPixels;
 
         mDefaultHorizontalGap = 0;
         setKeyWidth(mDisplayWidth / 10);
         mDefaultVerticalGap = 0;
         mDefaultHeight = mDefaultWidth;
         mId = id;
-        loadKeyboard(context, xmlLayoutResId);
         mProximityInfo = new ProximityInfo(GRID_WIDTH, GRID_HEIGHT);
+        loadKeyboard(context, xmlLayoutResId);
     }
 
     public int getProximityInfo() {
@@ -293,7 +296,7 @@
     public boolean setShiftLocked(boolean newShiftLockState) {
         final Map<Key, Drawable> shiftedIcons = getShiftedIcons();
         for (final Key key : getShiftKeys()) {
-            key.mOn = newShiftLockState;
+            key.setHighlightOn(newShiftLockState);
             key.setIcon(newShiftLockState ? shiftedIcons.get(key) : mNormalShiftIcons.get(key));
         }
         mShiftState.setShiftLocked(newShiftLockState);
@@ -381,10 +384,6 @@
         mProximityInfo.setProximityInfo(mGridNeighbors, getMinWidth(), getHeight(), mKeys);
     }
 
-    public boolean isInside(Key key, int x, int y) {
-        return key.isOnKey(x, y);
-    }
-
     /**
      * Returns the indices of the keys that are closest to the given point.
      * @param x the x-coordinate of the point
@@ -403,9 +402,44 @@
         return EMPTY_INT_ARRAY;
     }
 
+    /**
+     * Compute the most common key width in order to use it as proximity key detection threshold.
+     *
+     * @return The most common key width in the keyboard
+     */
+    public int getMostCommonKeyWidth() {
+        if (mMostCommonKeyWidth == 0) {
+            final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
+            int maxCount = 0;
+            int mostCommonWidth = 0;
+            for (final Key key : mKeys) {
+                final Integer width = key.mWidth + key.mGap;
+                Integer count = histogram.get(width);
+                if (count == null)
+                    count = 0;
+                histogram.put(width, ++count);
+                if (count > maxCount) {
+                    maxCount = count;
+                    mostCommonWidth = width;
+                }
+            }
+            mMostCommonKeyWidth = mostCommonWidth;
+        }
+        return mMostCommonKeyWidth;
+    }
+
+    /**
+     * Return true if spacebar needs showing preview even when "popup on keypress" is off.
+     * @param keyIndex index of the pressing key
+     * @return true if spacebar needs showing preview
+     */
+    public boolean needSpacebarPreview(int keyIndex) {
+        return false;
+    }
+
     private void loadKeyboard(Context context, int xmlLayoutResId) {
         try {
-            KeyboardParser parser = new KeyboardParser(this, context.getResources());
+            KeyboardParser parser = new KeyboardParser(this, context);
             parser.parseKeyboard(xmlLayoutResId);
             // mMinWidth is the width of this keyboard which is maximum width of row.
             mMinWidth = parser.getMaxRowWidth();
@@ -419,7 +453,7 @@
         }
     }
 
-    protected static void setDefaultBounds(Drawable drawable)  {
+    public static void setDefaultBounds(Drawable drawable)  {
         if (drawable != null)
             drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
                     drawable.getIntrinsicHeight());
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index b256a89..b91134d 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -16,11 +16,12 @@
 
 package com.android.inputmethod.keyboard;
 
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.Utils;
-
 import android.view.inputmethod.EditorInfo;
 
+import com.android.inputmethod.compat.EditorInfoCompatUtils;
+import com.android.inputmethod.compat.InputTypeCompatUtils;
+import com.android.inputmethod.latin.R;
+
 import java.util.Arrays;
 import java.util.Locale;
 
@@ -36,47 +37,41 @@
     public static final int MODE_PHONE = 4;
     public static final int MODE_NUMBER = 5;
 
-    public static final int F2KEY_MODE_NONE = 0;
-    public static final int F2KEY_MODE_SETTINGS = 1;
-    public static final int F2KEY_MODE_SHORTCUT_IME = 2;
-    public static final int F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS = 3;
-
     public final Locale mLocale;
     public final int mOrientation;
+    public final int mWidth;
     public final int mMode;
     public final int mXmlId;
-    public final int mColorScheme;
-    public final boolean mWebInput;
+    public final boolean mNavigateAction;
     public final boolean mPasswordInput;
-    // TODO: Clean up these booleans and modes.
     public final boolean mHasSettingsKey;
-    public final int mF2KeyMode;
-    public final boolean mClobberSettingsKey;
     public final boolean mVoiceKeyEnabled;
     public final boolean mHasVoiceKey;
     public final int mImeAction;
     public final boolean mEnableShiftLock;
+
     public final String mXmlName;
+    public final EditorInfo mAttribute;
 
     private final int mHashCode;
 
-    public KeyboardId(String xmlName, int xmlId, int colorScheme, Locale locale, int orientation,
-            int mode, EditorInfo attribute, boolean hasSettingsKey, int f2KeyMode,
-            boolean clobberSettingsKey, boolean voiceKeyEnabled, boolean hasVoiceKey,
-            boolean enableShiftLock) {
+    public KeyboardId(String xmlName, int xmlId, Locale locale, int orientation, int width,
+            int mode, EditorInfo attribute, boolean hasSettingsKey, boolean voiceKeyEnabled,
+            boolean hasVoiceKey, boolean enableShiftLock) {
         final int inputType = (attribute != null) ? attribute.inputType : 0;
         final int imeOptions = (attribute != null) ? attribute.imeOptions : 0;
         this.mLocale = locale;
         this.mOrientation = orientation;
+        this.mWidth = width;
         this.mMode = mode;
         this.mXmlId = xmlId;
-        this.mColorScheme = colorScheme;
-        this.mWebInput = Utils.isWebInputType(inputType);
-        this.mPasswordInput = Utils.isPasswordInputType(inputType)
-                || Utils.isVisiblePasswordInputType(inputType);
+        // Note: Turn off checking navigation flag to show TAB key for now.
+        this.mNavigateAction = InputTypeCompatUtils.isWebInputType(inputType);
+//                || EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions)
+//                || EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions);
+        this.mPasswordInput = InputTypeCompatUtils.isPasswordInputType(inputType)
+                || InputTypeCompatUtils.isVisiblePasswordInputType(inputType);
         this.mHasSettingsKey = hasSettingsKey;
-        this.mF2KeyMode = f2KeyMode;
-        this.mClobberSettingsKey = clobberSettingsKey;
         this.mVoiceKeyEnabled = voiceKeyEnabled;
         this.mHasVoiceKey = hasVoiceKey;
         // We are interested only in {@link EditorInfo#IME_MASK_ACTION} enum value and
@@ -84,19 +79,19 @@
         this.mImeAction = imeOptions & (
                 EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION);
         this.mEnableShiftLock = enableShiftLock;
+
         this.mXmlName = xmlName;
+        this.mAttribute = attribute;
 
         this.mHashCode = Arrays.hashCode(new Object[] {
                 locale,
                 orientation,
+                width,
                 mode,
                 xmlId,
-                colorScheme,
-                mWebInput,
+                mNavigateAction,
                 mPasswordInput,
                 hasSettingsKey,
-                f2KeyMode,
-                clobberSettingsKey,
                 voiceKeyEnabled,
                 hasVoiceKey,
                 mImeAction,
@@ -104,6 +99,18 @@
         });
     }
 
+    public KeyboardId cloneWithNewLayout(String xmlName, int xmlId) {
+        return new KeyboardId(xmlName, xmlId, mLocale, mOrientation, mWidth, mMode, mAttribute,
+                mHasSettingsKey, mVoiceKeyEnabled, mHasVoiceKey, mEnableShiftLock);
+    }
+
+    public KeyboardId cloneWithNewGeometry(int width) {
+        if (mWidth == width)
+            return this;
+        return new KeyboardId(mXmlName, mXmlId, mLocale, mOrientation, width, mMode, mAttribute,
+                mHasSettingsKey, mVoiceKeyEnabled, mHasVoiceKey, mEnableShiftLock);
+    }
+
     public int getXmlId() {
         return mXmlId;
     }
@@ -113,13 +120,17 @@
     }
 
     public boolean isSymbolsKeyboard() {
-        return mXmlId == R.xml.kbd_symbols;
+        return mXmlId == R.xml.kbd_symbols || mXmlId == R.xml.kbd_symbols_shift;
     }
 
     public boolean isPhoneKeyboard() {
         return mMode == MODE_PHONE;
     }
 
+    public boolean isPhoneSymbolsKeyboard() {
+        return mXmlId == R.xml.kbd_phone_symbols;
+    }
+
     public boolean isNumberKeyboard() {
         return mMode == MODE_NUMBER;
     }
@@ -132,14 +143,12 @@
     boolean equals(KeyboardId other) {
         return other.mLocale.equals(this.mLocale)
             && other.mOrientation == this.mOrientation
+            && other.mWidth == this.mWidth
             && other.mMode == this.mMode
             && other.mXmlId == this.mXmlId
-            && other.mColorScheme == this.mColorScheme
-            && other.mWebInput == this.mWebInput
+            && other.mNavigateAction == this.mNavigateAction
             && other.mPasswordInput == this.mPasswordInput
             && other.mHasSettingsKey == this.mHasSettingsKey
-            && other.mF2KeyMode == this.mF2KeyMode
-            && other.mClobberSettingsKey == this.mClobberSettingsKey
             && other.mVoiceKeyEnabled == this.mVoiceKeyEnabled
             && other.mHasVoiceKey == this.mHasVoiceKey
             && other.mImeAction == this.mImeAction
@@ -153,16 +162,13 @@
 
     @Override
     public String toString() {
-        return String.format("[%s.xml %s %s %s %s %s %s%s%s%s%s%s%s%s]",
+        return String.format("[%s.xml %s %s%d %s %s %s%s%s%s%s%s]",
                 mXmlName,
                 mLocale,
-                (mOrientation == 1 ? "port" : "land"),
+                (mOrientation == 1 ? "port" : "land"), mWidth,
                 modeName(mMode),
-                imeOptionsName(mImeAction),
-                colorSchemeName(mColorScheme),
-                f2KeyModeName(mF2KeyMode),
-                (mClobberSettingsKey ? " clobberSettingsKey" : ""),
-                (mWebInput ? " webInput" : ""),
+                EditorInfoCompatUtils.imeOptionsName(mImeAction),
+                (mNavigateAction ? " navigateAction" : ""),
                 (mPasswordInput ? " passwordInput" : ""),
                 (mHasSettingsKey ? " hasSettingsKey" : ""),
                 (mVoiceKeyEnabled ? " voiceKeyEnabled" : ""),
@@ -182,45 +188,4 @@
         }
         return null;
     }
-
-    public static String colorSchemeName(int colorScheme) {
-        switch (colorScheme) {
-        case KeyboardView.COLOR_SCHEME_WHITE: return "white";
-        case KeyboardView.COLOR_SCHEME_BLACK: return "black";
-        }
-        return null;
-    }
-
-    public static String imeOptionsName(int imeOptions) {
-        if (imeOptions == -1) return null;
-        final int actionNo = imeOptions & EditorInfo.IME_MASK_ACTION;
-        final String action;
-        switch (actionNo) {
-        case EditorInfo.IME_ACTION_UNSPECIFIED: action = "actionUnspecified"; break;
-        case EditorInfo.IME_ACTION_NONE: action = "actionNone"; break;
-        case EditorInfo.IME_ACTION_GO: action = "actionGo"; break;
-        case EditorInfo.IME_ACTION_SEARCH: action = "actionSearch"; break;
-        case EditorInfo.IME_ACTION_SEND: action = "actionSend"; break;
-        case EditorInfo.IME_ACTION_DONE: action = "actionDone"; break;
-        case EditorInfo.IME_ACTION_NEXT: action = "actionNext"; break;
-        case EditorInfo.IME_ACTION_PREVIOUS: action = "actionPrevious"; break;
-        default: action = "actionUnknown(" + actionNo + ")"; break;
-        }
-        if ((imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
-            return "flagNoEnterAction|" + action;
-        } else {
-            return action;
-        }
-    }
-
-    public static String f2KeyModeName(int f2KeyMode) {
-        switch (f2KeyMode) {
-        case F2KEY_MODE_NONE: return "none";
-        case F2KEY_MODE_SETTINGS: return "settings";
-        case F2KEY_MODE_SHORTCUT_IME: return "shortcutIme";
-        case F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS: return "shortcutImeOrSettings";
-        }
-        return null;
-    }
 }
-
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 5d3ec52..eef01a6 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -16,6 +16,20 @@
 
 package com.android.inputmethod.keyboard;
 
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.InflateException;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+
+import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.keyboard.internal.ModifierKeyState;
+import com.android.inputmethod.keyboard.internal.ShiftKeyState;
 import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.R;
@@ -23,40 +37,33 @@
 import com.android.inputmethod.latin.SubtypeSwitcher;
 import com.android.inputmethod.latin.Utils;
 
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.util.Log;
-import android.view.InflateException;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-
 import java.lang.ref.SoftReference;
 import java.util.HashMap;
 import java.util.Locale;
 
 public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener {
     private static final String TAG = KeyboardSwitcher.class.getSimpleName();
-    private static final boolean DEBUG = LatinImeLogger.sDBG;
+    private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG;
     public static final boolean DEBUG_STATE = false;
 
-    private static String sConfigDefaultKeyboardThemeId;
     public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902";
     private static final int[] KEYBOARD_THEMES = {
-        R.layout.input_basic,
-        R.layout.input_basic_highcontrast,
-        R.layout.input_stone_normal,
-        R.layout.input_stone_bold,
-        R.layout.input_gingerbread,
-        R.layout.input_honeycomb,
+        R.style.KeyboardTheme,
+        R.style.KeyboardTheme_HighContrast,
+        R.style.KeyboardTheme_Stone,
+        R.style.KeyboardTheme_Stone_Bold,
+        R.style.KeyboardTheme_Gingerbread,
+        R.style.KeyboardTheme_IceCreamSandwich,
     };
 
     private SubtypeSwitcher mSubtypeSwitcher;
     private SharedPreferences mPrefs;
 
-    private LatinKeyboardView mInputView;
+    private View mCurrentInputView;
+    private LatinKeyboardView mKeyboardView;
     private LatinIME mInputMethodService;
 
+    // TODO: Combine these key state objects with auto mode switch state.
     private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
     private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol");
 
@@ -75,13 +82,17 @@
     private boolean mVoiceKeyEnabled;
     private boolean mVoiceButtonOnPrimary;
 
-    private static final int AUTO_MODE_SWITCH_STATE_ALPHA = 0;
-    private static final int AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN = 1;
-    private static final int AUTO_MODE_SWITCH_STATE_SYMBOL = 2;
+    // TODO: Encapsulate these state handling to separate class and combine with ShiftKeyState
+    // and ModifierKeyState.
+    private static final int SWITCH_STATE_ALPHA = 0;
+    private static final int SWITCH_STATE_SYMBOL_BEGIN = 1;
+    private static final int SWITCH_STATE_SYMBOL = 2;
     // The following states are used only on the distinct multi-touch panel devices.
-    private static final int AUTO_MODE_SWITCH_STATE_MOMENTARY = 3;
-    private static final int AUTO_MODE_SWITCH_STATE_CHORDING = 4;
-    private int mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
+    private static final int SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL = 3;
+    private static final int SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE = 4;
+    private static final int SWITCH_STATE_CHORDING_ALPHA = 5;
+    private static final int SWITCH_STATE_CHORDING_SYMBOL = 6;
+    private int mSwitchState = SWITCH_STATE_ALPHA;
 
     // Indicates whether or not we have the settings key in option of settings
     private boolean mSettingsKeyEnabledInSettings;
@@ -93,7 +104,9 @@
     // Default is SETTINGS_KEY_MODE_AUTO.
     private static final int DEFAULT_SETTINGS_KEY_MODE = SETTINGS_KEY_MODE_AUTO;
 
-    private int mLayoutId;
+    private int mThemeIndex = -1;
+    private Context mThemeContext;
+    private int mKeyboardWidth;
 
     private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
 
@@ -109,22 +122,35 @@
         sInstance.mInputMethodService = ims;
         sInstance.mPrefs = prefs;
         sInstance.mSubtypeSwitcher = SubtypeSwitcher.getInstance();
-
-        try {
-            sConfigDefaultKeyboardThemeId = ims.getString(
-                    R.string.config_default_keyboard_theme_id);
-            sInstance.mLayoutId = Integer.valueOf(
-                    prefs.getString(PREF_KEYBOARD_LAYOUT, sConfigDefaultKeyboardThemeId));
-        } catch (NumberFormatException e) {
-            sConfigDefaultKeyboardThemeId = "0";
-            sInstance.mLayoutId = 0;
-        }
+        sInstance.setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs));
         prefs.registerOnSharedPreferenceChangeListener(sInstance);
     }
 
+    private static int getKeyboardThemeIndex(Context context, SharedPreferences prefs) {
+        final String defaultThemeId = context.getString(R.string.config_default_keyboard_theme_id);
+        final String themeId = prefs.getString(PREF_KEYBOARD_LAYOUT, defaultThemeId);
+        try {
+            final int themeIndex = Integer.valueOf(themeId);
+            if (themeIndex >= 0 && themeIndex < KEYBOARD_THEMES.length)
+                return themeIndex;
+        } catch (NumberFormatException e) {
+            // Format error, keyboard theme is default to 0.
+        }
+        Log.w(TAG, "Illegal keyboard theme in preference: " + themeId + ", default to 0");
+        return 0;
+    }
+
+    private void setContextThemeWrapper(Context context, int themeIndex) {
+        if (mThemeIndex != themeIndex) {
+            mThemeIndex = themeIndex;
+            mThemeContext = new ContextThemeWrapper(context, KEYBOARD_THEMES[themeIndex]);
+            mKeyboardCache.clear();
+        }
+    }
+
     public void loadKeyboard(EditorInfo attribute, boolean voiceKeyEnabled,
             boolean voiceButtonOnPrimary) {
-        mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
+        mSwitchState = SWITCH_STATE_ALPHA;
         try {
             loadKeyboardInternal(attribute, voiceKeyEnabled, voiceButtonOnPrimary, false);
         } catch (RuntimeException e) {
@@ -137,7 +163,7 @@
 
     private void loadKeyboardInternal(EditorInfo attribute, boolean voiceButtonEnabled,
             boolean voiceButtonOnPrimary, boolean isSymbols) {
-        if (mInputView == null) return;
+        if (mKeyboardView == null) return;
 
         mAttribute = attribute;
         mVoiceKeyEnabled = voiceButtonEnabled;
@@ -147,19 +173,38 @@
         mSettingsKeyEnabledInSettings = getSettingsKeyMode(mPrefs, mInputMethodService);
         final KeyboardId id = getKeyboardId(attribute, isSymbols);
 
-        final Keyboard oldKeyboard = mInputView.getKeyboard();
-        if (oldKeyboard != null && oldKeyboard.mId.equals(id))
-            return;
+        // Note: This comment is only applied for phone number keyboard layout.
+        // On non-xlarge device, "@integer/key_switch_alpha_symbol" key code is used to switch
+        // between "phone keyboard" and "phone symbols keyboard".  But on xlarge device,
+        // "@integer/key_shift" key code is used for that purpose in order to properly display
+        // "more" and "locked more" key labels.  To achieve these behavior, we should initialize
+        // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard"
+        // respectively here for xlarge device's layout switching.
+        mSymbolsId = makeSiblingKeyboardId(id, R.xml.kbd_symbols, R.xml.kbd_phone);
+        mSymbolsShiftedId = makeSiblingKeyboardId(
+                id, R.xml.kbd_symbols_shift, R.xml.kbd_phone_symbols);
 
-        makeSymbolsKeyboardIds(id.mMode, attribute);
-        mCurrentId = id;
-        mInputView.setPreviewEnabled(mInputMethodService.getPopupOn());
         setKeyboard(getKeyboard(id));
     }
 
+    public void onSizeChanged() {
+        final int width = mInputMethodService.getWindow().getWindow().getDecorView().getWidth();
+        if (width == 0 || mCurrentId == null)
+            return;
+        mKeyboardWidth = width;
+        // Set keyboard with new width.
+        final KeyboardId newId = mCurrentId.cloneWithNewGeometry(width);
+        setKeyboard(getKeyboard(newId));
+    }
+
     private void setKeyboard(final Keyboard newKeyboard) {
-        final Keyboard oldKeyboard = mInputView.getKeyboard();
-        mInputView.setKeyboard(newKeyboard);
+        final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
+        mKeyboardView.setKeyboard(newKeyboard);
+        mCurrentId = newKeyboard.mId;
+        final Resources res = mInputMethodService.getResources();
+        mKeyboardView.setKeyPreviewPopupEnabled(
+                Settings.Values.isKeyPreviewPopupEnabled(mPrefs, res),
+                Settings.Values.getKeyPreviewPopupDismissDelay(mPrefs, res));
         final boolean localeChanged = (oldKeyboard == null)
                 || !newKeyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
         mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged);
@@ -169,22 +214,23 @@
         final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id);
         LatinKeyboard keyboard = (ref == null) ? null : ref.get();
         if (keyboard == null) {
-            final Locale savedLocale =  mSubtypeSwitcher.changeSystemLocale(
+            final Resources res = mInputMethodService.getResources();
+            final Locale savedLocale = Utils.setSystemLocale(res,
                     mSubtypeSwitcher.getInputLocale());
 
-            keyboard = new LatinKeyboard(mInputMethodService, id);
+            keyboard = new LatinKeyboard(mThemeContext, id, id.mWidth);
 
             if (id.mEnableShiftLock) {
                 keyboard.enableShiftLock();
             }
 
             mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard));
-            if (DEBUG)
+            if (DEBUG_CACHE)
                 Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
                         + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
 
-            mSubtypeSwitcher.changeSystemLocale(savedLocale);
-        } else if (DEBUG) {
+            Utils.setSystemLocale(res, savedLocale);
+        } else if (DEBUG_CACHE) {
             Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id);
         }
 
@@ -195,6 +241,7 @@
         // we should reset the text fade factor. It is also applicable to shortcut key.
         keyboard.setSpacebarTextFadeFactor(0.0f, null);
         keyboard.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady(), null);
+        keyboard.setSpacebarSlidingLanguageSwitchDiff(0);
         return keyboard;
     }
 
@@ -211,7 +258,6 @@
     private KeyboardId getKeyboardId(EditorInfo attribute, boolean isSymbols) {
         final int mode = Utils.getKeyboardMode(attribute);
         final boolean hasVoiceKey = hasVoiceKey(isSymbols);
-        final int charColorId = getColorScheme();
         final int xmlId;
         final boolean enableShiftLock;
 
@@ -238,46 +284,20 @@
             }
         }
         final boolean hasSettingsKey = hasSettingsKey(attribute);
-        final int f2KeyMode = getF2KeyMode(mPrefs, mInputMethodService, attribute);
-        final boolean clobberSettingsKey = Utils.inPrivateImeOptions(
-                mInputMethodService.getPackageName(), LatinIME.IME_OPTION_NO_SETTINGS_KEY,
-                attribute);
         final Resources res = mInputMethodService.getResources();
         final int orientation = res.getConfiguration().orientation;
+        if (mKeyboardWidth == 0)
+            mKeyboardWidth = res.getDisplayMetrics().widthPixels;
         final Locale locale = mSubtypeSwitcher.getInputLocale();
         return new KeyboardId(
-                res.getResourceEntryName(xmlId), xmlId, charColorId, locale, orientation, mode,
-                attribute, hasSettingsKey, f2KeyMode, clobberSettingsKey, mVoiceKeyEnabled,
-                hasVoiceKey, enableShiftLock);
+                res.getResourceEntryName(xmlId), xmlId, locale, orientation, mKeyboardWidth,
+                mode, attribute, hasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, enableShiftLock);
     }
 
-    private void makeSymbolsKeyboardIds(final int mode, EditorInfo attribute) {
-        final Locale locale = mSubtypeSwitcher.getInputLocale();
-        final Resources res = mInputMethodService.getResources();
-        final int orientation = res.getConfiguration().orientation;
-        final int colorScheme = getColorScheme();
-        final boolean hasVoiceKey = mVoiceKeyEnabled && !mVoiceButtonOnPrimary;
-        final boolean hasSettingsKey = hasSettingsKey(attribute);
-        final int f2KeyMode = getF2KeyMode(mPrefs, mInputMethodService, attribute);
-        final boolean clobberSettingsKey = Utils.inPrivateImeOptions(
-                mInputMethodService.getPackageName(), LatinIME.IME_OPTION_NO_SETTINGS_KEY,
-                attribute);
-        // Note: This comment is only applied for phone number keyboard layout.
-        // On non-xlarge device, "@integer/key_switch_alpha_symbol" key code is used to switch
-        // between "phone keyboard" and "phone symbols keyboard".  But on xlarge device,
-        // "@integer/key_shift" key code is used for that purpose in order to properly display
-        // "more" and "locked more" key labels.  To achieve these behavior, we should initialize
-        // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard"
-        // respectively here for xlarge device's layout switching.
-        int xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols;
-        final String xmlName = res.getResourceEntryName(xmlId);
-        mSymbolsId = new KeyboardId(xmlName, xmlId, colorScheme, locale, orientation, mode,
-                attribute, hasSettingsKey, f2KeyMode, clobberSettingsKey, mVoiceKeyEnabled,
-                hasVoiceKey, true);
-        xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift;
-        mSymbolsShiftedId = new KeyboardId(xmlName, xmlId, colorScheme, locale, orientation, mode,
-                attribute, hasSettingsKey, f2KeyMode, clobberSettingsKey, mVoiceKeyEnabled,
-                hasVoiceKey, true);
+    private KeyboardId makeSiblingKeyboardId(KeyboardId base, int alphabet, int phone) {
+        final int xmlId = base.mMode == KeyboardId.MODE_PHONE ? phone : alphabet;
+        final String xmlName = mInputMethodService.getResources().getResourceEntryName(xmlId);
+        return base.cloneWithNewLayout(xmlName, xmlId);
     }
 
     public int getKeyboardMode() {
@@ -289,30 +309,24 @@
     }
 
     public boolean isInputViewShown() {
-        return mInputView != null && mInputView.isShown();
+        return mCurrentInputView != null && mCurrentInputView.isShown();
     }
 
     public boolean isKeyboardAvailable() {
-        if (mInputView != null)
-            return mInputView.getKeyboard() != null;
+        if (mKeyboardView != null)
+            return mKeyboardView.getKeyboard() != null;
         return false;
     }
 
     public LatinKeyboard getLatinKeyboard() {
-        if (mInputView != null) {
-            final Keyboard keyboard = mInputView.getKeyboard();
+        if (mKeyboardView != null) {
+            final Keyboard keyboard = mKeyboardView.getKeyboard();
             if (keyboard instanceof LatinKeyboard)
                 return (LatinKeyboard)keyboard;
         }
         return null;
     }
 
-    public void keyReleased() {
-        LatinKeyboard latinKeyboard = getLatinKeyboard();
-        if (latinKeyboard != null)
-            latinKeyboard.keyReleased();
-    }
-
     public boolean isShiftedOrShiftLocked() {
         LatinKeyboard latinKeyboard = getLatinKeyboard();
         if (latinKeyboard != null)
@@ -355,12 +369,11 @@
             // state when shift key is pressed to go to normal mode.
             // On the other hand, on distinct multi touch panel device, turning off the shift locked
             // state with shift key pressing is handled by onReleaseShift().
-            if ((!hasDistinctMultitouch() || isAccessibilityEnabled())
-                    && !shifted && latinKeyboard.isShiftLocked()) {
+            if (!hasDistinctMultitouch() && !shifted && latinKeyboard.isShiftLocked()) {
                 latinKeyboard.setShiftLocked(false);
             }
             if (latinKeyboard.setShifted(shifted)) {
-                mInputView.invalidateAllKeys();
+                mKeyboardView.invalidateAllKeys();
             }
         }
     }
@@ -368,7 +381,7 @@
     private void setShiftLocked(boolean shiftLocked) {
         LatinKeyboard latinKeyboard = getLatinKeyboard();
         if (latinKeyboard != null && latinKeyboard.setShiftLocked(shiftLocked)) {
-            mInputView.invalidateAllKeys();
+            mKeyboardView.invalidateAllKeys();
         }
     }
 
@@ -410,7 +423,7 @@
         LatinKeyboard latinKeyboard = getLatinKeyboard();
         if (latinKeyboard != null) {
             latinKeyboard.setAutomaticTemporaryUpperCase();
-            mInputView.invalidateAllKeys();
+            mKeyboardView.invalidateAllKeys();
         }
     }
 
@@ -454,9 +467,6 @@
     public void onPressShift(boolean withSliding) {
         if (!isKeyboardAvailable())
             return;
-        // If accessibility is enabled, disable momentary shift lock.
-        if (isAccessibilityEnabled())
-            return;
         ShiftKeyState shiftKeyState = mShiftKeyState;
         if (DEBUG_STATE)
             Log.d(TAG, "onPressShift:"
@@ -486,15 +496,13 @@
             // In symbol mode, just toggle symbol and symbol more keyboard.
             shiftKeyState.onPress();
             toggleShift();
+            mSwitchState = SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
         }
     }
 
     public void onReleaseShift(boolean withSliding) {
         if (!isKeyboardAvailable())
             return;
-        // If accessibility is enabled, disable momentary shift lock.
-        if (isAccessibilityEnabled())
-            return;
         ShiftKeyState shiftKeyState = mShiftKeyState;
         if (DEBUG_STATE)
             Log.d(TAG, "onReleaseShift:"
@@ -510,7 +518,7 @@
                 // To be able to turn off caps lock by "double tap" on shift key, we should ignore
                 // the second tap of the "double tap" from now for a while because we just have
                 // already turned off caps lock above.
-                mInputView.startIgnoringDoubleTap();
+                mKeyboardView.startIgnoringDoubleTap();
             } else if (isShiftedOrShiftLocked() && shiftKeyState.isPressingOnShifted()
                     && !withSliding) {
                 // Shift has been pressed without chording while shifted state.
@@ -521,42 +529,40 @@
                 // transited from automatic temporary upper case.
                 toggleShift();
             }
+        } else {
+            // In symbol mode, snap back to the previous keyboard mode if the user chords the shift
+            // key and another key, then releases the shift key.
+            if (mSwitchState == SWITCH_STATE_CHORDING_SYMBOL) {
+                toggleShift();
+            }
         }
         shiftKeyState.onRelease();
     }
 
     public void onPressSymbol() {
-        // If accessibility is enabled, disable momentary symbol lock.
-        if (isAccessibilityEnabled())
-            return;
         if (DEBUG_STATE)
             Log.d(TAG, "onPressSymbol:"
                     + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
                     + " symbolKeyState=" + mSymbolKeyState);
         changeKeyboardMode();
         mSymbolKeyState.onPress();
-        mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_MOMENTARY;
+        mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL;
     }
 
     public void onReleaseSymbol() {
-        // If accessibility is enabled, disable momentary symbol lock.
-        if (isAccessibilityEnabled())
-            return;
         if (DEBUG_STATE)
             Log.d(TAG, "onReleaseSymbol:"
                     + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
                     + " symbolKeyState=" + mSymbolKeyState);
         // Snap back to the previous keyboard mode if the user chords the mode change key and
-        // other key, then released the mode change key.
-        if (mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_CHORDING)
+        // another key, then releases the mode change key.
+        if (mSwitchState == SWITCH_STATE_CHORDING_ALPHA) {
             changeKeyboardMode();
+        }
         mSymbolKeyState.onRelease();
     }
 
     public void onOtherKeyPressed() {
-        // If accessibility is enabled, disable momentary mode locking.
-        if (isAccessibilityEnabled())
-            return;
         if (DEBUG_STATE)
             Log.d(TAG, "onOtherKeyPressed:"
                     + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
@@ -568,8 +574,13 @@
 
     public void onCancelInput() {
         // Snap back to the previous keyboard mode if the user cancels sliding input.
-        if (mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY && getPointerCount() == 1)
-            changeKeyboardMode();
+        if (getPointerCount() == 1) {
+            if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) {
+                changeKeyboardMode();
+            } else if (mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE) {
+                toggleShift();
+            }
+        }
     }
 
     private void toggleShiftInSymbol() {
@@ -577,74 +588,91 @@
             return;
         final LatinKeyboard keyboard;
         if (mCurrentId.equals(mSymbolsId) || !mCurrentId.equals(mSymbolsShiftedId)) {
-            mCurrentId = mSymbolsShiftedId;
-            keyboard = getKeyboard(mCurrentId);
+            keyboard = getKeyboard(mSymbolsShiftedId);
             // Symbol shifted keyboard has an ALT key that has a caps lock style indicator. To
-            // enable the indicator, we need to call enableShiftLock() and setShiftLocked(true).
-            // Thus we can keep the ALT key's Key.on value true while LatinKey.onRelease() is
-            // called.
+            // enable the indicator, we need to call setShiftLocked(true).
             keyboard.setShiftLocked(true);
         } else {
-            mCurrentId = mSymbolsId;
-            keyboard = getKeyboard(mCurrentId);
+            keyboard = getKeyboard(mSymbolsId);
             // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
-            // indicator, we need to call enableShiftLock() and setShiftLocked(false).
-            keyboard.setShifted(false);
+            // indicator, we need to call setShiftLocked(false).
+            keyboard.setShiftLocked(false);
         }
         setKeyboard(keyboard);
     }
 
-    public boolean isInMomentaryAutoModeSwitchState() {
-        return mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY;
+    public boolean isInMomentarySwitchState() {
+        return mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL
+                || mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
     }
 
     public boolean isVibrateAndSoundFeedbackRequired() {
-        return mInputView == null || !mInputView.isInSlidingKeyInput();
+        return mKeyboardView == null || !mKeyboardView.isInSlidingKeyInput();
     }
 
     private int getPointerCount() {
-        return mInputView == null ? 0 : mInputView.getPointerCount();
+        return mKeyboardView == null ? 0 : mKeyboardView.getPointerCount();
     }
 
     private void toggleKeyboardMode() {
         loadKeyboardInternal(mAttribute, mVoiceKeyEnabled, mVoiceButtonOnPrimary, !mIsSymbols);
         if (mIsSymbols) {
-            mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN;
+            mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
         } else {
-            mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
+            mSwitchState = SWITCH_STATE_ALPHA;
         }
     }
 
-    public boolean isAccessibilityEnabled() {
-        return mInputView != null && mInputView.isAccessibilityEnabled();
+    public boolean hasDistinctMultitouch() {
+        return mKeyboardView != null && mKeyboardView.hasDistinctMultitouch();
     }
 
-    public boolean hasDistinctMultitouch() {
-        return mInputView != null && mInputView.hasDistinctMultitouch();
+    private static boolean isSpaceCharacter(int c) {
+        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;
+        return false;
     }
 
     /**
      * Updates state machine to figure out when to automatically snap back to the previous mode.
      */
-    public void onKey(int key) {
+    public void onKey(int code) {
         if (DEBUG_STATE)
-            Log.d(TAG, "onKey: code=" + key + " autoModeSwitchState=" + mAutoModeSwitchState
+            Log.d(TAG, "onKey: code=" + code + " switchState=" + mSwitchState
                     + " pointers=" + getPointerCount());
-        switch (mAutoModeSwitchState) {
-        case AUTO_MODE_SWITCH_STATE_MOMENTARY:
+        switch (mSwitchState) {
+        case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL:
             // Only distinct multi touch devices can be in this state.
             // On non-distinct multi touch devices, mode change key is handled by
             // {@link LatinIME#onCodeInput}, not by {@link LatinIME#onPress} and
-            // {@link LatinIME#onRelease}. So, on such devices, {@link #mAutoModeSwitchState} starts
-            // from {@link #AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN}, or
-            // {@link #AUTO_MODE_SWITCH_STATE_ALPHA}, not from
-            // {@link #AUTO_MODE_SWITCH_STATE_MOMENTARY}.
-            if (key == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
+            // {@link LatinIME#onRelease}. So, on such devices, {@link #mSwitchState} starts
+            // from {@link #SWITCH_STATE_SYMBOL_BEGIN}, or {@link #SWITCH_STATE_ALPHA}, not from
+            // {@link #SWITCH_STATE_MOMENTARY}.
+            if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
                 // Detected only the mode change key has been pressed, and then released.
                 if (mIsSymbols) {
-                    mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN;
+                    mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
                 } else {
-                    mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
+                    mSwitchState = SWITCH_STATE_ALPHA;
                 }
             } else if (getPointerCount() == 1) {
                 // Snap back to the previous keyboard mode if the user pressed the mode change key
@@ -655,71 +683,96 @@
             } else {
                 // Chording input is being started. The keyboard mode will be snapped back to the
                 // previous mode in {@link onReleaseSymbol} when the mode change key is released.
-                mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_CHORDING;
+                mSwitchState = SWITCH_STATE_CHORDING_ALPHA;
             }
             break;
-        case AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN:
-            if (key != Keyboard.CODE_SPACE && key != Keyboard.CODE_ENTER && key >= 0) {
-                mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL;
+        case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE:
+            if (code == Keyboard.CODE_SHIFT) {
+                // Detected only the shift key has been pressed on symbol layout, and then released.
+                mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
+            } else if (getPointerCount() == 1) {
+                // Snap back to the previous keyboard mode if the user pressed the shift key on
+                // symbol mode and slid to other key, then released the finger.
+                toggleShift();
+                mSwitchState = SWITCH_STATE_SYMBOL;
+            } else {
+                // Chording input is being started. The keyboard mode will be snapped back to the
+                // previous mode in {@link onReleaseShift} when the shift key is released.
+                mSwitchState = SWITCH_STATE_CHORDING_SYMBOL;
             }
             break;
-        case AUTO_MODE_SWITCH_STATE_SYMBOL:
+        case SWITCH_STATE_SYMBOL_BEGIN:
+            if (!isSpaceCharacter(code) && code >= 0) {
+                mSwitchState = SWITCH_STATE_SYMBOL;
+            }
+            // Snap back to alpha keyboard mode immediately if user types a quote character.
+            if (isQuoteCharacter(code)) {
+                changeKeyboardMode();
+            }
+            break;
+        case SWITCH_STATE_SYMBOL:
+        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.
-            if (key == Keyboard.CODE_ENTER || key == Keyboard.CODE_SPACE) {
+            // characters followed by a space/enter or a quote character.
+            if (isSpaceCharacter(code) || isQuoteCharacter(code)) {
                 changeKeyboardMode();
             }
             break;
         }
     }
 
-    public LatinKeyboardView getInputView() {
-        return mInputView;
+    public LatinKeyboardView getKeyboardView() {
+        return mKeyboardView;
     }
 
-    public LatinKeyboardView onCreateInputView() {
-        createInputViewInternal(mLayoutId, true);
-        return mInputView;
+    public View onCreateInputView() {
+        return createInputView(mThemeIndex, true);
     }
 
-    private void createInputViewInternal(int newLayout, boolean forceReset) {
-        int layoutId = newLayout;
-        if (mLayoutId != layoutId || mInputView == null || forceReset) {
-            if (mInputView != null) {
-                mInputView.closing();
-            }
-            if (KEYBOARD_THEMES.length <= layoutId) {
-                layoutId = Integer.valueOf(sConfigDefaultKeyboardThemeId);
-            }
+    private View createInputView(final int newThemeIndex, final boolean forceRecreate) {
+        if (mCurrentInputView != null && mThemeIndex == newThemeIndex && !forceRecreate)
+            return mCurrentInputView;
 
-            Utils.GCUtils.getInstance().reset();
-            boolean tryGC = true;
-            for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
-                try {
-                    mInputView = (LatinKeyboardView) mInputMethodService.getLayoutInflater(
-                            ).inflate(KEYBOARD_THEMES[layoutId], null);
-                    tryGC = false;
-                } catch (OutOfMemoryError e) {
-                    Log.w(TAG, "load keyboard failed: " + e);
-                    tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
-                            mLayoutId + "," + layoutId, e);
-                } catch (InflateException e) {
-                    Log.w(TAG, "load keyboard failed: " + e);
-                    tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
-                            mLayoutId + "," + layoutId, e);
-                }
-            }
-            mInputView.setOnKeyboardActionListener(mInputMethodService);
-            mLayoutId = layoutId;
+        if (mKeyboardView != null) {
+            mKeyboardView.closing();
         }
+
+        final int oldThemeIndex = mThemeIndex;
+        Utils.GCUtils.getInstance().reset();
+        boolean tryGC = true;
+        for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
+            try {
+                setContextThemeWrapper(mInputMethodService, newThemeIndex);
+                mCurrentInputView = LayoutInflater.from(mThemeContext).inflate(
+                        R.layout.input_view, null);
+                tryGC = false;
+            } catch (OutOfMemoryError e) {
+                Log.w(TAG, "load keyboard failed: " + e);
+                tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
+                        oldThemeIndex + "," + newThemeIndex, e);
+            } catch (InflateException e) {
+                Log.w(TAG, "load keyboard failed: " + e);
+                tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
+                        oldThemeIndex + "," + newThemeIndex, e);
+            }
+        }
+
+        mKeyboardView = (LatinKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
+        mKeyboardView.setOnKeyboardActionListener(mInputMethodService);
+
+        // This always needs to be set since the accessibility state can
+        // potentially change without the input view being re-created.
+        AccessibleKeyboardViewProxy.setView(mKeyboardView);
+
+        return mCurrentInputView;
     }
 
-    private void postSetInputView() {
+    private void postSetInputView(final View newInputView) {
         mInputMethodService.mHandler.post(new Runnable() {
             @Override
             public void run() {
-                if (mInputView != null) {
-                    mInputMethodService.setInputView(mInputView);
+                if (newInputView != null) {
+                    mInputMethodService.setInputView(newInputView);
                 }
                 mInputMethodService.updateInputViewShown();
             }
@@ -729,46 +782,41 @@
     @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
         if (PREF_KEYBOARD_LAYOUT.equals(key)) {
-            final int layoutId = Integer.valueOf(
-                    sharedPreferences.getString(key, sConfigDefaultKeyboardThemeId));
-            createInputViewInternal(layoutId, false);
-            postSetInputView();
+            final int layoutId = getKeyboardThemeIndex(mInputMethodService, sharedPreferences);
+            postSetInputView(createInputView(layoutId, false));
         } else if (Settings.PREF_SETTINGS_KEY.equals(key)) {
             mSettingsKeyEnabledInSettings = getSettingsKeyMode(sharedPreferences,
                     mInputMethodService);
-            createInputViewInternal(mLayoutId, true);
-            postSetInputView();
+            postSetInputView(createInputView(mThemeIndex, true));
         }
     }
 
-    private int getColorScheme() {
-        return (mInputView != null)
-                ? mInputView.getColorScheme() : KeyboardView.COLOR_SCHEME_WHITE;
-    }
-
     public void onAutoCorrectionStateChanged(boolean isAutoCorrection) {
-        if (isAutoCorrection != mIsAutoCorrectionActive) {
-            LatinKeyboardView keyboardView = getInputView();
+        if (mIsAutoCorrectionActive != isAutoCorrection) {
             mIsAutoCorrectionActive = isAutoCorrection;
-            keyboardView.invalidateKey(((LatinKeyboard) keyboardView.getKeyboard())
-                    .onAutoCorrectionStateChanged(isAutoCorrection));
+            final LatinKeyboard keyboard = getLatinKeyboard();
+            if (keyboard != null && keyboard.needsAutoCorrectionSpacebarLed()) {
+                final Key invalidatedKey = keyboard.onAutoCorrectionStateChanged(isAutoCorrection);
+                final LatinKeyboardView keyboardView = getKeyboardView();
+                if (keyboardView != null)
+                    keyboardView.invalidateKey(invalidatedKey);
+            }
         }
     }
 
     private static boolean getSettingsKeyMode(SharedPreferences prefs, Context context) {
-        final Resources res = context.getResources();
-        final boolean showSettingsKeyOption = res.getBoolean(
+        Resources resources = context.getResources();
+        final boolean showSettingsKeyOption = resources.getBoolean(
                 R.bool.config_enable_show_settings_key_option);
         if (showSettingsKeyOption) {
             final String settingsKeyMode = prefs.getString(Settings.PREF_SETTINGS_KEY,
-                    res.getString(DEFAULT_SETTINGS_KEY_MODE));
+                    resources.getString(DEFAULT_SETTINGS_KEY_MODE));
             // We show the settings key when 1) SETTINGS_KEY_MODE_ALWAYS_SHOW or
             // 2) SETTINGS_KEY_MODE_AUTO and there are two or more enabled IMEs on the system
-            if (settingsKeyMode.equals(res.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW))
-                    || (settingsKeyMode.equals(res.getString(SETTINGS_KEY_MODE_AUTO))
+            if (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW))
+                    || (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_AUTO))
                             && Utils.hasMultipleEnabledIMEsOrSubtypes(
-                                    ((InputMethodManager) context.getSystemService(
-                                            Context.INPUT_METHOD_SERVICE))))) {
+                                    (InputMethodManagerCompatWrapper.getInstance(context))))) {
                 return true;
             }
             return false;
@@ -776,21 +824,4 @@
         // If the show settings key option is disabled, we always try showing the settings key.
         return true;
     }
-
-    private static int getF2KeyMode(SharedPreferences prefs, Context context,
-            EditorInfo attribute) {
-        final boolean clobberSettingsKey = Utils.inPrivateImeOptions(context.getPackageName(),
-                LatinIME.IME_OPTION_NO_SETTINGS_KEY, attribute);
-        final Resources res = context.getResources();
-        final String settingsKeyMode = prefs.getString(Settings.PREF_SETTINGS_KEY,
-                res.getString(DEFAULT_SETTINGS_KEY_MODE));
-        if (settingsKeyMode.equals(res.getString(SETTINGS_KEY_MODE_AUTO))) {
-            return clobberSettingsKey ? KeyboardId.F2KEY_MODE_SHORTCUT_IME
-                    : KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS;
-        } else if (settingsKeyMode.equals(res.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW))) {
-            return clobberSettingsKey ? KeyboardId.F2KEY_MODE_NONE : KeyboardId.F2KEY_MODE_SETTINGS;
-        } else { // SETTINGS_KEY_MODE_ALWAYS_HIDE
-            return KeyboardId.F2KEY_MODE_SHORTCUT_IME;
-        }
-    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index ac094e0..3d62303 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -32,24 +32,28 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Message;
-import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.GestureDetector;
-import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
+import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.PopupWindow;
 import android.widget.TextView;
 
+import com.android.inputmethod.accessibility.AccessibilityUtils;
+import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
+import com.android.inputmethod.compat.FrameLayoutCompatUtils;
+import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder;
+import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
+import com.android.inputmethod.keyboard.internal.SwipeTracker;
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SubtypeSwitcher;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -59,14 +63,26 @@
  * A view that renders a virtual {@link Keyboard}. It handles rendering of keys and detecting key
  * presses and touch movements.
  *
+ * @attr ref R.styleable#KeyboardView_backgroundDimAmount
  * @attr ref R.styleable#KeyboardView_keyBackground
+ * @attr ref R.styleable#KeyboardView_keyHysteresisDistance
+ * @attr ref R.styleable#KeyboardView_keyLetterRatio
+ * @attr ref R.styleable#KeyboardView_keyLabelRatio
+ * @attr ref R.styleable#KeyboardView_keyHintLetterRatio
+ * @attr ref R.styleable#KeyboardView_keyUppercaseLetterRatio
+ * @attr ref R.styleable#KeyboardView_keyTextStyle
  * @attr ref R.styleable#KeyboardView_keyPreviewLayout
  * @attr ref R.styleable#KeyboardView_keyPreviewOffset
- * @attr ref R.styleable#KeyboardView_labelTextSize
- * @attr ref R.styleable#KeyboardView_keyTextSize
+ * @attr ref R.styleable#KeyboardView_keyPreviewHeight
  * @attr ref R.styleable#KeyboardView_keyTextColor
+ * @attr ref R.styleable#KeyboardView_keyTextColorDisabled
+ * @attr ref R.styleable#KeyboardView_keyHintLetterColor
+ * @attr ref R.styleable#KeyboardView_keyUppercaseLetterInactivatedColor
+ * @attr ref R.styleable#KeyboardView_keyUppercaseLetterActivatedColor
  * @attr ref R.styleable#KeyboardView_verticalCorrection
  * @attr ref R.styleable#KeyboardView_popupLayout
+ * @attr ref R.styleable#KeyboardView_shadowColor
+ * @attr ref R.styleable#KeyboardView_shadowRadius
  */
 public class KeyboardView extends View implements PointerTracker.UIProxy {
     private static final String TAG = KeyboardView.class.getSimpleName();
@@ -76,9 +92,6 @@
     private static final boolean ENABLE_CAPSLOCK_BY_LONGPRESS = true;
     private static final boolean ENABLE_CAPSLOCK_BY_DOUBLETAP = true;
 
-    public static final int COLOR_SCHEME_WHITE = 0;
-    public static final int COLOR_SCHEME_BLACK = 1;
-
     // Timing constants
     private final int mKeyRepeatInterval;
 
@@ -87,53 +100,50 @@
     private static final int HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL = -1;
 
     // XML attribute
-    private int mKeyLetterSize;
-    private int mKeyTextColor;
-    private int mKeyTextColorDisabled;
-    private Typeface mKeyLetterStyle = Typeface.DEFAULT;
-    private int mLabelTextSize;
-    private int mColorScheme = COLOR_SCHEME_WHITE;
-    private int mShadowColor;
-    private float mShadowRadius;
-    private Drawable mKeyBackground;
-    private float mBackgroundDimAmount;
-    private float mKeyHysteresisDistance;
-    private float mVerticalCorrection;
-    private int mPreviewOffset;
-    private int mPreviewHeight;
-    private int mPopupLayout;
+    private final float mKeyLetterRatio;
+    private final int mKeyTextColor;
+    private final int mKeyTextInactivatedColor;
+    private final Typeface mKeyTextStyle;
+    private final float mKeyLabelRatio;
+    private final float mKeyHintLetterRatio;
+    private final float mKeyUppercaseLetterRatio;
+    private final int mShadowColor;
+    private final float mShadowRadius;
+    private final Drawable mKeyBackground;
+    private final float mBackgroundDimAmount;
+    private final float mKeyHysteresisDistance;
+    private final float mVerticalCorrection;
+    private final int mPreviewOffset;
+    private final int mPreviewHeight;
+    private final int mPopupLayout;
+    private final Drawable mKeyPopupHintIcon;
+    private final int mKeyHintLetterColor;
+    private final int mKeyUppercaseLetterInactivatedColor;
+    private final int mKeyUppercaseLetterActivatedColor;
 
     // Main keyboard
     private Keyboard mKeyboard;
-    private Key[] mKeys;
+    private int mKeyLetterSize;
+    private int mKeyLabelSize;
+    private int mKeyHintLetterSize;
+    private int mKeyUppercaseLetterSize;
 
-    // Key preview popup
+    // Key preview
     private boolean mInForeground;
     private TextView mPreviewText;
-    private PopupWindow mPreviewPopup;
-    private int mPreviewTextSizeLarge;
-    private int[] mOffsetInWindow;
-    private int mOldPreviewKeyIndex = KeyDetector.NOT_A_KEY;
-    private boolean mShowPreview = true;
-    private int mPopupPreviewOffsetX;
-    private int mPopupPreviewOffsetY;
-    private int mWindowY;
-    private int mPopupPreviewDisplayedY;
+    private float mPreviewTextRatio;
+    private int mPreviewTextSize;
+    private boolean mShowKeyPreviewPopup = true;
     private final int mDelayBeforePreview;
-    private final int mDelayAfterPreview;
+    private int mDelayAfterPreview;
+    private ViewGroup mPreviewPlacer;
+    private final int[] mCoordinates = new int[2];
 
-    // Popup mini keyboard
-    private PopupWindow mMiniKeyboardPopup;
-    private KeyboardView mMiniKeyboardView;
-    private View mMiniKeyboardParent;
-    private final WeakHashMap<Key, View> mMiniKeyboardCache = new WeakHashMap<Key, View>();
-    private int mMiniKeyboardOriginX;
-    private int mMiniKeyboardOriginY;
-    private long mMiniKeyboardPopupTime;
-    private int[] mWindowOffset;
-    private final float mMiniKeyboardSlideAllowance;
-    private int mMiniKeyboardTrackerId;
-    private final boolean mConfigShowMiniKeyboardAtTouchedPoint;
+    // Mini keyboard
+    private PopupWindow mPopupWindow;
+    private PopupPanel mPopupMiniKeyboardPanel;
+    private final WeakHashMap<Key, PopupPanel> mPopupPanelCache =
+            new WeakHashMap<Key, PopupPanel>();
 
     /** Listener for {@link KeyboardActionListener}. */
     private KeyboardActionListener mKeyboardActionListener;
@@ -145,14 +155,12 @@
 
     private final boolean mHasDistinctMultitouch;
     private int mOldPointerCount = 1;
+    private int mOldKeyIndex;
 
-    // Accessibility
-    private boolean mIsAccessibilityEnabled;
-
-    protected KeyDetector mKeyDetector = new ProximityKeyDetector();
+    protected KeyDetector mKeyDetector = new KeyDetector();
 
     // Swipe gesture detector
-    private GestureDetector mGestureDetector;
+    protected GestureDetector mGestureDetector;
     private final SwipeTracker mSwipeTracker = new SwipeTracker();
     private final int mSwipeThreshold;
     private final boolean mDisambiguateSwipe;
@@ -172,25 +180,24 @@
     private Bitmap mBuffer;
     /** The canvas for the above mutable keyboard bitmap */
     private Canvas mCanvas;
-    private final Paint mPaint;
-    private final Rect mPadding;
+    private final Paint mPaint = new Paint();
+    private final Rect mPadding = new Rect();
+    private final Rect mTextBounds = new Rect();
     // This map caches key label text height in pixel as value and key label text size as map key.
     private final HashMap<Integer, Integer> mTextHeightCache = new HashMap<Integer, Integer>();
+    // This map caches key label text width in pixel as value and key label text size as map key.
+    private final HashMap<Integer, Integer> mTextWidthCache = new HashMap<Integer, Integer>();
     // Distance from horizontal center of the key, proportional to key label text height and width.
-    private final float KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER = 0.45f;
-    private final float KEY_LABEL_VERTICAL_PADDING_FACTOR = 1.60f;
-    private final String KEY_LABEL_REFERENCE_CHAR = "H";
-    private final int KEY_LABEL_OPTION_ALIGN_LEFT = 1;
-    private final int KEY_LABEL_OPTION_ALIGN_RIGHT = 2;
-    private final int KEY_LABEL_OPTION_ALIGN_BOTTOM = 8;
-    private final int KEY_LABEL_OPTION_FONT_NORMAL = 16;
+    private static final float KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER = 0.45f;
+    private static final float KEY_LABEL_VERTICAL_PADDING_FACTOR = 1.60f;
+    private static final String KEY_LABEL_REFERENCE_CHAR = "M";
     private final int mKeyLabelHorizontalPadding;
 
     private final UIHandler mHandler = new UIHandler();
 
     class UIHandler extends Handler {
-        private static final int MSG_POPUP_PREVIEW = 1;
-        private static final int MSG_DISMISS_PREVIEW = 2;
+        private static final int MSG_SHOW_KEY_PREVIEW = 1;
+        private static final int MSG_DISMISS_KEY_PREVIEW = 2;
         private static final int MSG_REPEAT_KEY = 3;
         private static final int MSG_LONGPRESS_KEY = 4;
         private static final int MSG_LONGPRESS_SHIFT_KEY = 5;
@@ -200,55 +207,56 @@
 
         @Override
         public void handleMessage(Message msg) {
+            final PointerTracker tracker = (PointerTracker) msg.obj;
             switch (msg.what) {
-                case MSG_POPUP_PREVIEW:
-                    showKey(msg.arg1, (PointerTracker)msg.obj);
-                    break;
-                case MSG_DISMISS_PREVIEW:
-                    mPreviewPopup.dismiss();
-                    break;
-                case MSG_REPEAT_KEY: {
-                    final PointerTracker tracker = (PointerTracker)msg.obj;
-                    tracker.repeatKey(msg.arg1);
-                    startKeyRepeatTimer(mKeyRepeatInterval, msg.arg1, tracker);
-                    break;
-                }
-                case MSG_LONGPRESS_KEY: {
-                    final PointerTracker tracker = (PointerTracker)msg.obj;
-                    openPopupIfRequired(msg.arg1, tracker);
-                    break;
-                }
-                case MSG_LONGPRESS_SHIFT_KEY: {
-                    final PointerTracker tracker = (PointerTracker)msg.obj;
-                    onLongPressShiftKey(tracker);
-                    break;
-                }
+            case MSG_SHOW_KEY_PREVIEW:
+                showKey(msg.arg1, tracker);
+                break;
+            case MSG_DISMISS_KEY_PREVIEW:
+                mPreviewText.setVisibility(View.INVISIBLE);
+                break;
+            case MSG_REPEAT_KEY:
+                tracker.onRepeatKey(msg.arg1);
+                startKeyRepeatTimer(mKeyRepeatInterval, msg.arg1, tracker);
+                break;
+            case MSG_LONGPRESS_KEY:
+                openMiniKeyboardIfRequired(msg.arg1, tracker);
+                break;
+            case MSG_LONGPRESS_SHIFT_KEY:
+                onLongPressShiftKey(tracker);
+                break;
             }
         }
 
-        public void popupPreview(long delay, int keyIndex, PointerTracker tracker) {
-            removeMessages(MSG_POPUP_PREVIEW);
-            if (mPreviewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) {
+        public void showKeyPreview(long delay, int keyIndex, PointerTracker tracker) {
+            removeMessages(MSG_SHOW_KEY_PREVIEW);
+            if (mPreviewText.getVisibility() == VISIBLE || delay == 0) {
                 // Show right away, if it's already visible and finger is moving around
                 showKey(keyIndex, tracker);
             } else {
-                sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0, tracker),
-                        delay);
+                sendMessageDelayed(
+                        obtainMessage(MSG_SHOW_KEY_PREVIEW, keyIndex, 0, tracker), delay);
             }
         }
 
-        public void cancelPopupPreview() {
-            removeMessages(MSG_POPUP_PREVIEW);
+        public void cancelShowKeyPreview(PointerTracker tracker) {
+            removeMessages(MSG_SHOW_KEY_PREVIEW, tracker);
         }
 
-        public void dismissPreview(long delay) {
-            if (mPreviewPopup.isShowing()) {
-                sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay);
-            }
+        public void cancelAllShowKeyPreviews() {
+            removeMessages(MSG_SHOW_KEY_PREVIEW);
         }
 
-        public void cancelDismissPreview() {
-            removeMessages(MSG_DISMISS_PREVIEW);
+        public void dismissKeyPreview(long delay, PointerTracker tracker) {
+            sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, tracker), delay);
+        }
+
+        public void cancelDismissKeyPreview(PointerTracker tracker) {
+            removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker);
+        }
+
+        public void cancelAllDismissKeyPreviews() {
+            removeMessages(MSG_DISMISS_KEY_PREVIEW);
         }
 
         public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {
@@ -300,8 +308,8 @@
 
         public void cancelAllMessages() {
             cancelKeyTimers();
-            cancelPopupPreview();
-            cancelDismissPreview();
+            cancelAllShowKeyPreviews();
+            cancelAllDismissKeyPreviews();
         }
     }
 
@@ -314,107 +322,60 @@
 
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
-        int previewLayout = 0;
-        int keyTextSize = 0;
 
-        int n = a.getIndexCount();
-
-        for (int i = 0; i < n; i++) {
-            int attr = a.getIndex(i);
-
-            switch (attr) {
-            case R.styleable.KeyboardView_keyBackground:
-                mKeyBackground = a.getDrawable(attr);
-                break;
-            case R.styleable.KeyboardView_keyHysteresisDistance:
-                mKeyHysteresisDistance = a.getDimensionPixelOffset(attr, 0);
-                break;
-            case R.styleable.KeyboardView_verticalCorrection:
-                mVerticalCorrection = a.getDimensionPixelOffset(attr, 0);
-                break;
-            case R.styleable.KeyboardView_keyPreviewLayout:
-                previewLayout = a.getResourceId(attr, 0);
-                break;
-            case R.styleable.KeyboardView_keyPreviewOffset:
-                mPreviewOffset = a.getDimensionPixelOffset(attr, 0);
-                break;
-            case R.styleable.KeyboardView_keyPreviewHeight:
-                mPreviewHeight = a.getDimensionPixelSize(attr, 80);
-                break;
-            case R.styleable.KeyboardView_keyLetterSize:
-                mKeyLetterSize = a.getDimensionPixelSize(attr, 18);
-                break;
-            case R.styleable.KeyboardView_keyTextColor:
-                mKeyTextColor = a.getColor(attr, 0xFF000000);
-                break;
-            case R.styleable.KeyboardView_keyTextColorDisabled:
-                mKeyTextColorDisabled = a.getColor(attr, 0xFF000000);
-                break;
-            case R.styleable.KeyboardView_labelTextSize:
-                mLabelTextSize = a.getDimensionPixelSize(attr, 14);
-                break;
-            case R.styleable.KeyboardView_popupLayout:
-                mPopupLayout = a.getResourceId(attr, 0);
-                break;
-            case R.styleable.KeyboardView_shadowColor:
-                mShadowColor = a.getColor(attr, 0);
-                break;
-            case R.styleable.KeyboardView_shadowRadius:
-                mShadowRadius = a.getFloat(attr, 0f);
-                break;
-            // TODO: Use Theme (android.R.styleable.Theme_backgroundDimAmount)
-            case R.styleable.KeyboardView_backgroundDimAmount:
-                mBackgroundDimAmount = a.getFloat(attr, 0.5f);
-                break;
-            case R.styleable.KeyboardView_keyLetterStyle:
-                mKeyLetterStyle = Typeface.defaultFromStyle(a.getInt(attr, Typeface.NORMAL));
-                break;
-            case R.styleable.KeyboardView_colorScheme:
-                mColorScheme = a.getInt(attr, COLOR_SCHEME_WHITE);
-                break;
-            }
-        }
+        mKeyBackground = a.getDrawable(R.styleable.KeyboardView_keyBackground);
+        mKeyHysteresisDistance = a.getDimensionPixelOffset(
+                R.styleable.KeyboardView_keyHysteresisDistance, 0);
+        mVerticalCorrection = a.getDimensionPixelOffset(
+                R.styleable.KeyboardView_verticalCorrection, 0);
+        final int previewLayout = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0);
+        mPreviewOffset = a.getDimensionPixelOffset(R.styleable.KeyboardView_keyPreviewOffset, 0);
+        mPreviewHeight = a.getDimensionPixelSize(R.styleable.KeyboardView_keyPreviewHeight, 80);
+        mKeyLetterRatio = getRatio(a, R.styleable.KeyboardView_keyLetterRatio);
+        mKeyLabelRatio = getRatio(a, R.styleable.KeyboardView_keyLabelRatio);
+        mKeyHintLetterRatio = getRatio(a, R.styleable.KeyboardView_keyHintLetterRatio);
+        mKeyUppercaseLetterRatio = getRatio(a,
+                R.styleable.KeyboardView_keyUppercaseLetterRatio);
+        mKeyTextColor = a.getColor(R.styleable.KeyboardView_keyTextColor, 0xFF000000);
+        mKeyTextInactivatedColor = a.getColor(
+                R.styleable.KeyboardView_keyTextInactivatedColor, 0xFF000000);
+        mKeyPopupHintIcon = a.getDrawable(R.styleable.KeyboardView_keyPopupHintIcon);
+        mKeyHintLetterColor = a.getColor(R.styleable.KeyboardView_keyHintLetterColor, 0);
+        mKeyUppercaseLetterInactivatedColor = a.getColor(
+                R.styleable.KeyboardView_keyUppercaseLetterInactivatedColor, 0);
+        mKeyUppercaseLetterActivatedColor = a.getColor(
+                R.styleable.KeyboardView_keyUppercaseLetterActivatedColor, 0);
+        mKeyTextStyle = Typeface.defaultFromStyle(
+                a.getInt(R.styleable.KeyboardView_keyTextStyle, Typeface.NORMAL));
+        mPopupLayout = a.getResourceId(R.styleable.KeyboardView_popupLayout, 0);
+        mShadowColor = a.getColor(R.styleable.KeyboardView_shadowColor, 0);
+        mShadowRadius = a.getFloat(R.styleable.KeyboardView_shadowRadius, 0f);
+        // TODO: Use Theme (android.R.styleable.Theme_backgroundDimAmount)
+        mBackgroundDimAmount = a.getFloat(R.styleable.KeyboardView_backgroundDimAmount, 0.5f);
+        a.recycle();
 
         final Resources res = getResources();
 
-        mPreviewPopup = new PopupWindow(context);
         if (previewLayout != 0) {
             mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null);
-            mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large);
-            mPreviewPopup.setContentView(mPreviewText);
-            mPreviewPopup.setBackgroundDrawable(null);
+            mPreviewTextRatio = getRatio(res, R.fraction.key_preview_text_ratio);
         } else {
-            mShowPreview = false;
+            mShowKeyPreviewPopup = false;
         }
-        mPreviewPopup.setTouchable(false);
-        mPreviewPopup.setAnimationStyle(R.style.KeyPreviewAnimation);
         mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview);
         mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview);
         mKeyLabelHorizontalPadding = (int)res.getDimension(
                 R.dimen.key_label_horizontal_alignment_padding);
 
-        mMiniKeyboardParent = this;
-        mMiniKeyboardPopup = new PopupWindow(context);
-        mMiniKeyboardPopup.setBackgroundDrawable(null);
-        mMiniKeyboardPopup.setAnimationStyle(R.style.MiniKeyboardAnimation);
-        // Allow popup window to be drawn off the screen.
-        mMiniKeyboardPopup.setClippingEnabled(false);
-
-        mPaint = new Paint();
         mPaint.setAntiAlias(true);
-        mPaint.setTextSize(keyTextSize);
         mPaint.setTextAlign(Align.CENTER);
         mPaint.setAlpha(255);
 
-        mPadding = new Rect(0, 0, 0, 0);
         mKeyBackground.getPadding(mPadding);
 
         mSwipeThreshold = (int) (500 * res.getDisplayMetrics().density);
-        // TODO: Refer frameworks/base/core/res/res/values/config.xml
+        // TODO: Refer to frameworks/base/core/res/res/values/config.xml
         mDisambiguateSwipe = res.getBoolean(R.bool.config_swipeDisambiguation);
-        mMiniKeyboardSlideAllowance = res.getDimension(R.dimen.mini_keyboard_slide_allowance);
-        mConfigShowMiniKeyboardAtTouchedPoint = res.getBoolean(
-                R.bool.config_show_mini_keyboard_at_touched_point);
 
         GestureDetector.SimpleOnGestureListener listener =
                 new GestureDetector.SimpleOnGestureListener() {
@@ -489,6 +450,16 @@
         mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
     }
 
+    // Read fraction value in TypedArray as float.
+    private static float getRatio(TypedArray a, int index) {
+        return a.getFraction(index, 1000, 1000, 1) / 1000.0f;
+    }
+
+    // Read fraction value in resource as float.
+    private static float getRatio(Resources res, int id) {
+        return res.getFraction(id, 1000, 1000) / 1000.0f;
+    }
+
     public void startIgnoringDoubleTap() {
         if (ENABLE_CAPSLOCK_BY_DOUBLETAP)
             mHandler.startIgnoringDoubleTap();
@@ -509,6 +480,12 @@
         return mKeyboardActionListener;
     }
 
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        // TODO: Should notify InputMethodService instead?
+        KeyboardSwitcher.getInstance().onSizeChanged();
+    }
+
     /**
      * Attaches a keyboard to this view. The keyboard can be switched at any time and the
      * view will re-layout itself to accommodate the keyboard.
@@ -518,23 +495,30 @@
      */
     public void setKeyboard(Keyboard keyboard) {
         if (mKeyboard != null) {
-            dismissKeyPreview();
+            dismissAllKeyPreviews();
         }
         // Remove any pending messages, except dismissing preview
         mHandler.cancelKeyTimers();
-        mHandler.cancelPopupPreview();
+        mHandler.cancelAllShowKeyPreviews();
         mKeyboard = keyboard;
         LatinImeLogger.onSetKeyboard(keyboard);
-        mKeys = mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
+        mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
                 -getPaddingTop() + mVerticalCorrection);
         for (PointerTracker tracker : mPointerTrackers) {
-            tracker.setKeyboard(keyboard, mKeys, mKeyHysteresisDistance);
+            tracker.setKeyboard(keyboard, mKeyHysteresisDistance);
         }
         requestLayout();
         mKeyboardChanged = true;
         invalidateAllKeys();
-        mKeyDetector.setProximityThreshold(KeyDetector.getMostCommonKeyWidth(keyboard));
-        mMiniKeyboardCache.clear();
+        mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth());
+        mPopupPanelCache.clear();
+        final int keyHeight = keyboard.getRowHeight() - keyboard.getVerticalGap();
+        mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio);
+        mKeyLabelSize = (int)(keyHeight * mKeyLabelRatio);
+        mKeyHintLetterSize = (int)(keyHeight * mKeyHintLetterRatio);
+        mKeyUppercaseLetterSize = (int)(
+                keyHeight * mKeyUppercaseLetterRatio);
+        mPreviewTextSize = (int)(keyHeight * mPreviewTextRatio);
     }
 
     /**
@@ -556,54 +540,24 @@
     }
 
     /**
-     * Enables or disables accessibility.
-     * @param accessibilityEnabled whether or not to enable accessibility
-     */
-    public void setAccessibilityEnabled(boolean accessibilityEnabled) {
-        mIsAccessibilityEnabled = accessibilityEnabled;
-
-        // Propagate this change to all existing pointer trackers.
-        for (PointerTracker tracker : mPointerTrackers) {
-            tracker.setAccessibilityEnabled(accessibilityEnabled);
-        }
-    }
-
-    /**
-     * Returns whether the device has accessibility enabled.
-     * @return true if the device has accessibility enabled.
-     */
-    @Override
-    public boolean isAccessibilityEnabled() {
-        return mIsAccessibilityEnabled;
-    }
-
-    /**
      * Enables or disables the key feedback popup. This is a popup that shows a magnified
      * version of the depressed key. By default the preview is enabled.
-     * @param previewEnabled whether or not to enable the key feedback popup
-     * @see #isPreviewEnabled()
+     * @param previewEnabled whether or not to enable the key feedback preview
+     * @param delay the delay after which the preview is dismissed
+     * @see #isKeyPreviewPopupEnabled()
      */
-    public void setPreviewEnabled(boolean previewEnabled) {
-        mShowPreview = previewEnabled;
+    public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
+        mShowKeyPreviewPopup = previewEnabled;
+        mDelayAfterPreview = delay;
     }
 
     /**
-     * Returns the enabled state of the key feedback popup.
-     * @return whether or not the key feedback popup is enabled
-     * @see #setPreviewEnabled(boolean)
+     * Returns the enabled state of the key feedback preview
+     * @return whether or not the key feedback preview is enabled
+     * @see #setKeyPreviewPopupEnabled(boolean, int)
      */
-    public boolean isPreviewEnabled() {
-        return mShowPreview;
-    }
-
-    public int getColorScheme() {
-        return mColorScheme;
-    }
-
-    public void setPopupOffset(int x, int y) {
-        mPopupPreviewOffsetX = x;
-        mPopupPreviewOffsetY = y;
-        mPreviewPopup.dismiss();
+    public boolean isKeyPreviewPopupEnabled() {
+        return mShowKeyPreviewPopup;
     }
 
     /**
@@ -666,157 +620,43 @@
             mDirtyRect.union(0, 0, width, height);
         }
         if (mBuffer == null || mBuffer.getWidth() != width || mBuffer.getHeight() != height) {
+            if (mBuffer != null)
+                mBuffer.recycle();
             mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-            mCanvas = new Canvas(mBuffer);
+            if (mCanvas != null) {
+                mCanvas.setBitmap(mBuffer);
+            } else {
+                mCanvas = new Canvas(mBuffer);
+            }
         }
         final Canvas canvas = mCanvas;
         canvas.clipRect(mDirtyRect, Op.REPLACE);
+        canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
 
         if (mKeyboard == null) return;
 
-        final Paint paint = mPaint;
-        final Drawable keyBackground = mKeyBackground;
-        final Rect padding = mPadding;
-        final int kbdPaddingLeft = getPaddingLeft();
-        final int kbdPaddingTop = getPaddingTop();
-        final Key[] keys = mKeys;
-        final boolean isManualTemporaryUpperCase = mKeyboard.isManualTemporaryUpperCase();
-        final boolean drawSingleKey = (mInvalidatedKey != null
-                && mInvalidatedKeyRect.contains(mDirtyRect));
-
-        canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
-        final int keyCount = keys.length;
-        for (int i = 0; i < keyCount; i++) {
-            final Key key = keys[i];
-            if (drawSingleKey && key != mInvalidatedKey) {
-                continue;
+        if (mInvalidatedKey != null && mInvalidatedKeyRect.contains(mDirtyRect)) {
+            // Draw a single key.
+            onBufferDrawKey(canvas, mInvalidatedKey);
+        } else {
+            // Draw all keys.
+            for (final Key key : mKeyboard.getKeys()) {
+                onBufferDrawKey(canvas, key);
             }
-            int[] drawableState = key.getCurrentDrawableState();
-            keyBackground.setState(drawableState);
-
-            // Switch the character to uppercase if shift is pressed
-            String label = key.mLabel == null? null : adjustCase(key.mLabel).toString();
-
-            final Rect bounds = keyBackground.getBounds();
-            if (key.mWidth != bounds.right || key.mHeight != bounds.bottom) {
-                keyBackground.setBounds(0, 0, key.mWidth, key.mHeight);
-            }
-            canvas.translate(key.mX + kbdPaddingLeft, key.mY + kbdPaddingTop);
-            keyBackground.draw(canvas);
-
-            final int rowHeight = padding.top + key.mHeight;
-            // Draw key label
-            if (label != null) {
-                // For characters, use large font. For labels like "Done", use small font.
-                final int labelSize = getLabelSizeAndSetPaint(label, key.mLabelOption, paint);
-                final int labelCharHeight = getLabelCharHeight(labelSize, paint);
-
-                // Vertical label text alignment.
-                final float baseline;
-                if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_BOTTOM) != 0) {
-                    baseline = key.mHeight -
-                            + labelCharHeight * KEY_LABEL_VERTICAL_PADDING_FACTOR;
-                    if (DEBUG_SHOW_ALIGN)
-                        drawHorizontalLine(canvas, (int)baseline, key.mWidth, 0xc0008000,
-                                new Paint());
-                } else { // Align center
-                    final float centerY = (key.mHeight + padding.top - padding.bottom) / 2;
-                    baseline = centerY
-                            + labelCharHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER;
-                    if (DEBUG_SHOW_ALIGN)
-                        drawHorizontalLine(canvas, (int)baseline, key.mWidth, 0xc0008000,
-                                new Paint());
-                }
-                // Horizontal label text alignment
-                final int positionX;
-                if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
-                    positionX = mKeyLabelHorizontalPadding + padding.left;
-                    paint.setTextAlign(Align.LEFT);
-                    if (DEBUG_SHOW_ALIGN)
-                        drawVerticalLine(canvas, positionX, rowHeight, 0xc0800080, new Paint());
-                } else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
-                    positionX = key.mWidth - mKeyLabelHorizontalPadding - padding.right;
-                    paint.setTextAlign(Align.RIGHT);
-                    if (DEBUG_SHOW_ALIGN)
-                        drawVerticalLine(canvas, positionX, rowHeight, 0xc0808000, new Paint());
-                } else {
-                    positionX = (key.mWidth + padding.left - padding.right) / 2;
-                    paint.setTextAlign(Align.CENTER);
-                    if (DEBUG_SHOW_ALIGN) {
-                        if (label.length() > 1)
-                            drawVerticalLine(canvas, positionX, rowHeight, 0xc0008080, new Paint());
-                    }
-                }
-                if (key.mManualTemporaryUpperCaseHintIcon != null && isManualTemporaryUpperCase) {
-                    paint.setColor(mKeyTextColorDisabled);
-                } else {
-                    paint.setColor(mKeyTextColor);
-                }
-                if (key.mEnabled) {
-                    // Set a drop shadow for the text
-                    paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor);
-                } else {
-                    // Make label invisible
-                    paint.setColor(Color.TRANSPARENT);
-                }
-                canvas.drawText(label, positionX, baseline, paint);
-                // Turn off drop shadow
-                paint.setShadowLayer(0, 0, 0, 0);
-            }
-            // Draw key icon
-            final Drawable icon = key.getIcon();
-            if (key.mLabel == null && icon != null) {
-                final int drawableWidth = icon.getIntrinsicWidth();
-                final int drawableHeight = icon.getIntrinsicHeight();
-                final int drawableX;
-                final int drawableY = (
-                        key.mHeight + padding.top - padding.bottom - drawableHeight) / 2;
-                if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
-                    drawableX = padding.left + mKeyLabelHorizontalPadding;
-                    if (DEBUG_SHOW_ALIGN)
-                        drawVerticalLine(canvas, drawableX, rowHeight, 0xc0800080, new Paint());
-                } else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
-                    drawableX = key.mWidth - padding.right - mKeyLabelHorizontalPadding
-                            - drawableWidth;
-                    if (DEBUG_SHOW_ALIGN)
-                        drawVerticalLine(canvas, drawableX + drawableWidth, rowHeight,
-                                0xc0808000, new Paint());
-                } else { // Align center
-                    drawableX = (key.mWidth + padding.left - padding.right - drawableWidth) / 2;
-                    if (DEBUG_SHOW_ALIGN)
-                        drawVerticalLine(canvas, drawableX + drawableWidth / 2, rowHeight,
-                                0xc0008080, new Paint());
-                }
-                drawIcon(canvas, icon, drawableX, drawableY, drawableWidth, drawableHeight);
-                if (DEBUG_SHOW_ALIGN)
-                    drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
-                            0x80c00000, new Paint());
-            }
-            if (key.mHintIcon != null) {
-                final int drawableWidth = key.mWidth;
-                final int drawableHeight = key.mHeight;
-                final int drawableX = 0;
-                final int drawableY = HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL;
-                Drawable hintIcon = (isManualTemporaryUpperCase
-                        && key.mManualTemporaryUpperCaseHintIcon != null)
-                        ? key.mManualTemporaryUpperCaseHintIcon : key.mHintIcon;
-                drawIcon(canvas, hintIcon, drawableX, drawableY, drawableWidth, drawableHeight);
-                if (DEBUG_SHOW_ALIGN)
-                    drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
-                            0x80c0c000, new Paint());
-            }
-            canvas.translate(-key.mX - kbdPaddingLeft, -key.mY - kbdPaddingTop);
         }
 
-        // TODO: Move this function to ProximityInfo for getting rid of public declarations for
+        // TODO: Move this function to ProximityInfo for getting rid of
+        // public declarations for
         // GRID_WIDTH and GRID_HEIGHT
         if (DEBUG_KEYBOARD_GRID) {
             Paint p = new Paint();
             p.setStyle(Paint.Style.STROKE);
             p.setStrokeWidth(1.0f);
             p.setColor(0x800000c0);
-            int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1) / mKeyboard.GRID_WIDTH;
-            int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1) / mKeyboard.GRID_HEIGHT;
+            int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1)
+                    / mKeyboard.GRID_WIDTH;
+            int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1)
+                    / mKeyboard.GRID_HEIGHT;
             for (int i = 0; i <= mKeyboard.GRID_WIDTH; i++)
                 canvas.drawLine(i * cw, 0, i * cw, ch * mKeyboard.GRID_HEIGHT, p);
             for (int i = 0; i <= mKeyboard.GRID_HEIGHT; i++)
@@ -824,9 +664,9 @@
         }
 
         // Overlay a dark rectangle to dim the keyboard
-        if (mMiniKeyboardView != null) {
-            paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
-            canvas.drawRect(0, 0, width, height, paint);
+        if (mPopupMiniKeyboardPanel != null) {
+            mPaint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
+            canvas.drawRect(0, 0, width, height, mPaint);
         }
 
         mInvalidatedKey = null;
@@ -834,20 +674,170 @@
         mDirtyRect.setEmpty();
     }
 
+    private void onBufferDrawKey(final Canvas canvas, final Key key) {
+        final Paint paint = mPaint;
+        final Drawable keyBackground = mKeyBackground;
+        final Rect padding = mPadding;
+        final int kbdPaddingLeft = getPaddingLeft();
+        final int kbdPaddingTop = getPaddingTop();
+        final int keyDrawX = key.mX + key.mVisualInsetsLeft;
+        final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
+        final int rowHeight = padding.top + key.mHeight;
+        final boolean isManualTemporaryUpperCase = mKeyboard.isManualTemporaryUpperCase();
+
+        canvas.translate(keyDrawX + kbdPaddingLeft, key.mY + kbdPaddingTop);
+
+        // Draw key background.
+        final int[] drawableState = key.getCurrentDrawableState();
+        keyBackground.setState(drawableState);
+        final Rect bounds = keyBackground.getBounds();
+        if (keyDrawWidth != bounds.right || key.mHeight != bounds.bottom) {
+            keyBackground.setBounds(0, 0, keyDrawWidth, key.mHeight);
+        }
+        keyBackground.draw(canvas);
+
+        // Draw key label.
+        if (key.mLabel != null) {
+            // Switch the character to uppercase if shift is pressed
+            final String label = key.mLabel == null ? null : adjustCase(key.mLabel).toString();
+            // For characters, use large font. For labels like "Done", use small font.
+            final int labelSize = getLabelSizeAndSetPaint(label, key.mLabelOption, paint);
+            final int labelCharHeight = getLabelCharHeight(labelSize, paint);
+
+            // Vertical label text alignment.
+            final float baseline;
+            if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_BOTTOM) != 0) {
+                baseline = key.mHeight - labelCharHeight * KEY_LABEL_VERTICAL_PADDING_FACTOR;
+                if (DEBUG_SHOW_ALIGN)
+                    drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
+                            new Paint());
+            } else { // Align center
+                final float centerY = (key.mHeight + padding.top - padding.bottom) / 2;
+                baseline = centerY + labelCharHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER;
+                if (DEBUG_SHOW_ALIGN)
+                    drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
+                            new Paint());
+            }
+            // Horizontal label text alignment
+            final int positionX;
+            if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT) != 0) {
+                positionX = mKeyLabelHorizontalPadding + padding.left;
+                paint.setTextAlign(Align.LEFT);
+                if (DEBUG_SHOW_ALIGN)
+                    drawVerticalLine(canvas, positionX, rowHeight, 0xc0800080, new Paint());
+            } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_RIGHT) != 0) {
+                positionX = keyDrawWidth - mKeyLabelHorizontalPadding - padding.right;
+                paint.setTextAlign(Align.RIGHT);
+                if (DEBUG_SHOW_ALIGN)
+                    drawVerticalLine(canvas, positionX, rowHeight, 0xc0808000, new Paint());
+            } else {
+                positionX = (keyDrawWidth + padding.left - padding.right) / 2;
+                paint.setTextAlign(Align.CENTER);
+                if (DEBUG_SHOW_ALIGN) {
+                    if (label.length() > 1)
+                        drawVerticalLine(canvas, positionX, rowHeight, 0xc0008080, new Paint());
+                }
+            }
+            if (key.hasUppercaseLetter() && isManualTemporaryUpperCase) {
+                paint.setColor(mKeyTextInactivatedColor);
+            } else {
+                paint.setColor(mKeyTextColor);
+            }
+            if (key.isEnabled()) {
+                // Set a drop shadow for the text
+                paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor);
+            } else {
+                // Make label invisible
+                paint.setColor(Color.TRANSPARENT);
+            }
+            canvas.drawText(label, positionX, baseline, paint);
+            // Turn off drop shadow
+            paint.setShadowLayer(0, 0, 0, 0);
+        }
+
+        // Draw hint letter.
+        if (key.mHintLetter != null) {
+            final String label = key.mHintLetter.toString();
+            final int textColor;
+            final int textSize;
+            if (key.hasUppercaseLetter()) {
+                textColor = isManualTemporaryUpperCase ? mKeyUppercaseLetterActivatedColor
+                        : mKeyUppercaseLetterInactivatedColor;
+                textSize = mKeyUppercaseLetterSize;
+            } else {
+                textColor = mKeyHintLetterColor;
+                textSize = mKeyHintLetterSize;
+            }
+            paint.setColor(textColor);
+            paint.setTextSize(textSize);
+            // Note: padding.right for drawX?
+            final float drawX = keyDrawWidth - getLabelCharWidth(textSize, paint);
+            final float drawY = -paint.ascent() + padding.top;
+            canvas.drawText(label, drawX, drawY, paint);
+        }
+
+        // Draw key icon.
+        final Drawable icon = key.getIcon();
+        if (key.mLabel == null && icon != null) {
+            final int drawableWidth = icon.getIntrinsicWidth();
+            final int drawableHeight = icon.getIntrinsicHeight();
+            final int drawableX;
+            final int drawableY = (key.mHeight + padding.top - padding.bottom - drawableHeight) / 2;
+            if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT) != 0) {
+                drawableX = padding.left + mKeyLabelHorizontalPadding;
+                if (DEBUG_SHOW_ALIGN)
+                    drawVerticalLine(canvas, drawableX, rowHeight, 0xc0800080, new Paint());
+            } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_RIGHT) != 0) {
+                drawableX = keyDrawWidth - padding.right - mKeyLabelHorizontalPadding
+                        - drawableWidth;
+                if (DEBUG_SHOW_ALIGN)
+                    drawVerticalLine(canvas, drawableX + drawableWidth, rowHeight,
+                            0xc0808000, new Paint());
+            } else { // Align center
+                drawableX = (keyDrawWidth + padding.left - padding.right - drawableWidth) / 2;
+                if (DEBUG_SHOW_ALIGN)
+                    drawVerticalLine(canvas, drawableX + drawableWidth / 2, rowHeight,
+                            0xc0008080, new Paint());
+            }
+            drawIcon(canvas, icon, drawableX, drawableY, drawableWidth, drawableHeight);
+            if (DEBUG_SHOW_ALIGN)
+                drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
+                        0x80c00000, new Paint());
+        }
+
+        // Draw popup hint icon "...".
+        // TODO: Draw "..." by text.
+        if (key.hasPopupHint()) {
+            final int drawableWidth = keyDrawWidth;
+            final int drawableHeight = key.mHeight;
+            final int drawableX = 0;
+            final int drawableY = HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL;
+            final Drawable hintIcon = mKeyPopupHintIcon;
+            drawIcon(canvas, hintIcon, drawableX, drawableY, drawableWidth, drawableHeight);
+            if (DEBUG_SHOW_ALIGN)
+                drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
+                        0x80c0c000, new Paint());
+        }
+
+        canvas.translate(-keyDrawX - kbdPaddingLeft, -key.mY - kbdPaddingTop);
+    }
+
     public int getLabelSizeAndSetPaint(CharSequence label, int keyLabelOption, Paint paint) {
         // For characters, use large font. For labels like "Done", use small font.
         final int labelSize;
         final Typeface labelStyle;
+        if ((keyLabelOption & Key.LABEL_OPTION_FONT_NORMAL) != 0) {
+            labelStyle = Typeface.DEFAULT;
+        } else if ((keyLabelOption & Key.LABEL_OPTION_FONT_FIXED_WIDTH) != 0) {
+            labelStyle = Typeface.MONOSPACE;
+        } else {
+            labelStyle = mKeyTextStyle;
+        }
         if (label.length() > 1) {
-            labelSize = mLabelTextSize;
-            if ((keyLabelOption & KEY_LABEL_OPTION_FONT_NORMAL) != 0) {
-                labelStyle = Typeface.DEFAULT;
-            } else {
-                labelStyle = Typeface.DEFAULT_BOLD;
-            }
+            labelSize = (keyLabelOption & Key.LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO) != 0
+                    ? mKeyLetterSize : mKeyLabelSize;
         } else {
             labelSize = mKeyLetterSize;
-            labelStyle = mKeyLetterStyle;
         }
         paint.setTextSize(labelSize);
         paint.setTypeface(labelStyle);
@@ -860,14 +850,26 @@
         if (labelHeightValue != null) {
             labelCharHeight = labelHeightValue;
         } else {
-            Rect textBounds = new Rect();
-            paint.getTextBounds(KEY_LABEL_REFERENCE_CHAR, 0, 1, textBounds);
-            labelCharHeight = textBounds.height();
+            paint.getTextBounds(KEY_LABEL_REFERENCE_CHAR, 0, 1, mTextBounds);
+            labelCharHeight = mTextBounds.height();
             mTextHeightCache.put(labelSize, labelCharHeight);
         }
         return labelCharHeight;
     }
 
+    private int getLabelCharWidth(int labelSize, Paint paint) {
+        Integer labelWidthValue = mTextWidthCache.get(labelSize);
+        final int labelCharWidth;
+        if (labelWidthValue != null) {
+            labelCharWidth = labelWidthValue;
+        } else {
+            paint.getTextBounds(KEY_LABEL_REFERENCE_CHAR, 0, 1, mTextBounds);
+            labelCharWidth = mTextBounds.width();
+            mTextWidthCache.put(labelSize, labelCharWidth);
+        }
+        return labelCharWidth;
+    }
+
     private static void drawIcon(Canvas canvas, Drawable icon, int x, int y, int width,
             int height) {
         canvas.translate(x, y);
@@ -905,118 +907,102 @@
     }
 
     // TODO: clean up this method.
-    private void dismissKeyPreview() {
-        for (PointerTracker tracker : mPointerTrackers)
-            tracker.releaseKey();
-        showPreview(KeyDetector.NOT_A_KEY, null);
+    private void dismissAllKeyPreviews() {
+        for (PointerTracker tracker : mPointerTrackers) {
+            tracker.setReleasedKeyGraphics();
+            dismissKeyPreview(tracker);
+        }
     }
 
     @Override
-    public void showPreview(int keyIndex, PointerTracker tracker) {
-        int oldKeyIndex = mOldPreviewKeyIndex;
-        mOldPreviewKeyIndex = keyIndex;
-        // We should re-draw popup preview when 1) we need to hide the preview, 2) we will show
-        // the space key preview and 3) pointer moves off the space key to other letter key, we
-        // should hide the preview of the previous key.
-        final boolean hidePreviewOrShowSpaceKeyPreview = (tracker == null)
-                || (SubtypeSwitcher.getInstance().useSpacebarLanguageSwitcher()
-                        && SubtypeSwitcher.getInstance().needsToDisplayLanguage()
-                        && (tracker.isSpaceKey(keyIndex) || tracker.isSpaceKey(oldKeyIndex)));
-        // If key changed and preview is on or the key is space (language switch is enabled)
-        if (oldKeyIndex != keyIndex && (mShowPreview || (hidePreviewOrShowSpaceKeyPreview))) {
-            if (keyIndex == KeyDetector.NOT_A_KEY) {
-                mHandler.cancelPopupPreview();
-                mHandler.dismissPreview(mDelayAfterPreview);
-            } else if (tracker != null) {
-                mHandler.popupPreview(mDelayBeforePreview, keyIndex, tracker);
-            }
+    public void showKeyPreview(int keyIndex, PointerTracker tracker) {
+        if (mShowKeyPreviewPopup) {
+            mHandler.showKeyPreview(mDelayBeforePreview, keyIndex, tracker);
+        } else if (mKeyboard.needSpacebarPreview(keyIndex)) {
+            // Show key preview (in this case, slide language switcher) without any delay.
+            showKey(keyIndex, tracker);
         }
     }
 
-    // TODO Must fix popup preview on xlarge layout
+    @Override
+    public void dismissKeyPreview(PointerTracker tracker) {
+        if (mShowKeyPreviewPopup) {
+            mHandler.cancelShowKeyPreview(tracker);
+            mHandler.dismissKeyPreview(mDelayAfterPreview, tracker);
+        } else if (mKeyboard.needSpacebarPreview(KeyDetector.NOT_A_KEY)) {
+            // Dismiss key preview (in this case, slide language switcher) without any delay.
+            mPreviewText.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    private void addKeyPreview(TextView keyPreview) {
+        if (mPreviewPlacer == null) {
+            mPreviewPlacer = FrameLayoutCompatUtils.getPlacer(
+                    (ViewGroup)getRootView().findViewById(android.R.id.content));
+        }
+        final ViewGroup placer = mPreviewPlacer;
+        placer.addView(keyPreview, FrameLayoutCompatUtils.newLayoutParam(placer, 0, 0));
+    }
+
+    // TODO: Introduce minimum duration for displaying key previews
+    // TODO: Display up to two key previews when the user presses two keys at the same time
     private void showKey(final int keyIndex, PointerTracker tracker) {
-        Key key = tracker.getKey(keyIndex);
+        final TextView previewText = mPreviewText;
+        // If the key preview has no parent view yet, add it to the ViewGroup which can place
+        // key preview absolutely in SoftInputWindow.
+        if (previewText.getParent() == null) {
+            addKeyPreview(previewText);
+        }
+
+        final Key key = tracker.getKey(keyIndex);
         // If keyIndex is invalid or IME is already closed, we must not show key preview.
-        // Trying to show preview PopupWindow while root window is closed causes
+        // Trying to show key preview while root window is closed causes
         // WindowManager.BadTokenException.
         if (key == null || !mInForeground)
             return;
+
+        mHandler.cancelAllDismissKeyPreviews();
+
+        final int keyDrawX = key.mX + key.mVisualInsetsLeft;
+        final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
         // What we show as preview should match what we show on key top in onBufferDraw(). 
         if (key.mLabel != null) {
             // TODO Should take care of temporaryShiftLabel here.
-            mPreviewText.setCompoundDrawables(null, null, null, null);
-            mPreviewText.setText(adjustCase(tracker.getPreviewText(key)));
+            previewText.setCompoundDrawables(null, null, null, null);
             if (key.mLabel.length() > 1) {
-                mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyLetterSize);
-                mPreviewText.setTypeface(Typeface.DEFAULT_BOLD);
+                previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyLetterSize);
+                previewText.setTypeface(Typeface.DEFAULT_BOLD);
             } else {
-                mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge);
-                mPreviewText.setTypeface(mKeyLetterStyle);
+                previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSize);
+                previewText.setTypeface(mKeyTextStyle);
             }
+            previewText.setText(adjustCase(tracker.getPreviewText(key)));
         } else {
             final Drawable previewIcon = key.getPreviewIcon();
-            mPreviewText.setCompoundDrawables(null, null, null,
+            previewText.setCompoundDrawables(null, null, null,
                    previewIcon != null ? previewIcon : key.getIcon());
-            mPreviewText.setText(null);
-        }
-        mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
-                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-        int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.mWidth
-                + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
-        final int popupHeight = mPreviewHeight;
-        LayoutParams lp = mPreviewText.getLayoutParams();
-        if (lp != null) {
-            lp.width = popupWidth;
-            lp.height = popupHeight;
-        }
-
-        int popupPreviewX = key.mX - (popupWidth - key.mWidth) / 2;
-        int popupPreviewY = key.mY - popupHeight + mPreviewOffset;
-
-        mHandler.cancelDismissPreview();
-        if (mOffsetInWindow == null) {
-            mOffsetInWindow = new int[2];
-            getLocationInWindow(mOffsetInWindow);
-            mOffsetInWindow[0] += mPopupPreviewOffsetX; // Offset may be zero
-            mOffsetInWindow[1] += mPopupPreviewOffsetY; // Offset may be zero
-            int[] windowLocation = new int[2];
-            getLocationOnScreen(windowLocation);
-            mWindowY = windowLocation[1];
+            previewText.setText(null);
         }
         // Set the preview background state
-        mPreviewText.getBackground().setState(
+        previewText.getBackground().setState(
                 key.mPopupCharacters != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
-        popupPreviewX += mOffsetInWindow[0];
-        popupPreviewY += mOffsetInWindow[1];
 
-        // If the popup cannot be shown above the key, put it on the side
-        if (popupPreviewY + mWindowY < 0) {
-            // If the key you're pressing is on the left side of the keyboard, show the popup on
-            // the right, offset by enough to see at least one key to the left/right.
-            if (key.mX + key.mWidth <= getWidth() / 2) {
-                popupPreviewX += (int) (key.mWidth * 2.5);
-            } else {
-                popupPreviewX -= (int) (key.mWidth * 2.5);
-            }
-            popupPreviewY += popupHeight;
-        }
+        previewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+        final int previewWidth = Math.max(previewText.getMeasuredWidth(), keyDrawWidth
+                + previewText.getPaddingLeft() + previewText.getPaddingRight());
+        final int previewHeight = mPreviewHeight;
+        getLocationInWindow(mCoordinates);
+        final int previewX = keyDrawX - (previewWidth - keyDrawWidth) / 2 + mCoordinates[0];
+        final int previewY = key.mY - previewHeight + mCoordinates[1] + mPreviewOffset;
 
-        try {
-            if (mPreviewPopup.isShowing()) {
-                mPreviewPopup.update(popupPreviewX, popupPreviewY, popupWidth, popupHeight);
-            } else {
-                mPreviewPopup.setWidth(popupWidth);
-                mPreviewPopup.setHeight(popupHeight);
-                mPreviewPopup.showAtLocation(mMiniKeyboardParent, Gravity.NO_GRAVITY,
-                        popupPreviewX, popupPreviewY);
-            }
-        } catch (WindowManager.BadTokenException e) {
-            // Swallow the exception which will be happened when IME is already closed.
-            Log.w(TAG, "LatinIME is already closed when tried showing key preview.");
-        }
-        // Record popup preview position to display mini-keyboard later at the same positon
-        mPopupPreviewDisplayedY = popupPreviewY;
-        mPreviewText.setVisibility(VISIBLE);
+        // Place the key preview.
+        // TODO: Adjust position of key previews which touch screen edges
+        final MarginLayoutParams lp = (MarginLayoutParams)previewText.getLayoutParams();
+        lp.width = previewWidth;
+        lp.height = previewHeight;
+        lp.setMargins(previewX, previewY, 0, 0);
+        previewText.setVisibility(VISIBLE);
     }
 
     /**
@@ -1043,70 +1029,72 @@
         if (key == null)
             return;
         mInvalidatedKey = key;
-        mInvalidatedKeyRect.set(0, 0, key.mWidth, key.mHeight);
-        mInvalidatedKeyRect.offset(key.mX + getPaddingLeft(), key.mY + getPaddingTop());
+        final int x = key.mX + getPaddingLeft();
+        final int y = key.mY + getPaddingTop();
+        mInvalidatedKeyRect.set(x, y, x + key.mWidth, y + key.mHeight);
         mDirtyRect.union(mInvalidatedKeyRect);
         onBufferDraw();
         invalidate(mInvalidatedKeyRect);
     }
 
-    private boolean openPopupIfRequired(int keyIndex, PointerTracker tracker) {
+    private boolean openMiniKeyboardIfRequired(int keyIndex, PointerTracker tracker) {
         // Check if we have a popup layout specified first.
         if (mPopupLayout == 0) {
             return false;
         }
 
-        Key popupKey = tracker.getKey(keyIndex);
-        if (popupKey == null)
+        final Key parentKey = tracker.getKey(keyIndex);
+        if (parentKey == null)
             return false;
-        boolean result = onLongPress(popupKey, tracker);
+        boolean result = onLongPress(parentKey, tracker);
         if (result) {
-            dismissKeyPreview();
-            mMiniKeyboardTrackerId = tracker.mPointerId;
-            // Mark this tracker "already processed" and remove it from the pointer queue
-            tracker.setAlreadyProcessed();
-            mPointerQueue.remove(tracker);
+            dismissAllKeyPreviews();
+            tracker.onLongPressed(mPointerQueue);
         }
         return result;
     }
 
     private void onLongPressShiftKey(PointerTracker tracker) {
-        tracker.setAlreadyProcessed();
-        mPointerQueue.remove(tracker);
+        tracker.onLongPressed(mPointerQueue);
         mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0);
     }
 
-    private void onDoubleTapShiftKey(PointerTracker tracker) {
+    private void onDoubleTapShiftKey(@SuppressWarnings("unused") PointerTracker tracker) {
         // When shift key is double tapped, the first tap is correctly processed as usual tap. And
         // the second tap is treated as this double tap event, so that we need not mark tracker
         // calling setAlreadyProcessed() nor remove the tracker from mPointerQueueueue.
         mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0);
     }
 
-    private View inflateMiniKeyboardContainer(Key popupKey) {
+    // This default implementation returns a popup mini keyboard panel.
+    // A derived class may return a language switcher popup panel, for instance.
+    protected PopupPanel onCreatePopupPanel(Key parentKey) {
+        if (parentKey.mPopupCharacters == null)
+            return null;
+
         final View container = LayoutInflater.from(getContext()).inflate(mPopupLayout, null);
         if (container == null)
             throw new NullPointerException();
 
-        final KeyboardView miniKeyboardView =
-                (KeyboardView)container.findViewById(R.id.KeyboardView);
+        final PopupMiniKeyboardView miniKeyboardView =
+                (PopupMiniKeyboardView)container.findViewById(R.id.mini_keyboard_view);
         miniKeyboardView.setOnKeyboardActionListener(new KeyboardActionListener() {
             @Override
             public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
                 mKeyboardActionListener.onCodeInput(primaryCode, keyCodes, x, y);
-                dismissPopupKeyboard();
+                dismissMiniKeyboard();
             }
 
             @Override
             public void onTextInput(CharSequence text) {
                 mKeyboardActionListener.onTextInput(text);
-                dismissPopupKeyboard();
+                dismissMiniKeyboard();
             }
 
             @Override
             public void onCancelInput() {
                 mKeyboardActionListener.onCancelInput();
-                dismissPopupKeyboard();
+                dismissMiniKeyboard();
             }
 
             @Override
@@ -1122,99 +1110,57 @@
                 mKeyboardActionListener.onRelease(primaryCode, withSliding);
             }
         });
-        // Override default ProximityKeyDetector.
-        miniKeyboardView.mKeyDetector = new MiniKeyboardKeyDetector(mMiniKeyboardSlideAllowance);
-        // Remove gesture detector on mini-keyboard
-        miniKeyboardView.mGestureDetector = null;
 
         final Keyboard keyboard = new MiniKeyboardBuilder(this, mKeyboard.getPopupKeyboardResId(),
-                popupKey, mKeyboard).build();
+                parentKey, mKeyboard).build();
         miniKeyboardView.setKeyboard(keyboard);
-        miniKeyboardView.mMiniKeyboardParent = this;
 
         container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
-                MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
+                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
 
-        return container;
+        return miniKeyboardView;
     }
 
     /**
-     * Called when a key is long pressed. By default this will open any popup keyboard associated
-     * with this key through the attributes popupLayout and popupCharacters.
-     * @param popupKey the key that was long pressed
+     * Called when a key is long pressed. By default this will open mini keyboard associated
+     * with this key.
+     * @param parentKey the key that was long pressed
+     * @param tracker the pointer tracker which pressed the parent key
      * @return true if the long press is handled, false otherwise. Subclasses should call the
      * method on the base class if the subclass doesn't wish to handle the call.
      */
-    protected boolean onLongPress(Key popupKey, PointerTracker tracker) {
-        if (popupKey.mPopupCharacters == null)
-            return false;
-
-        View container = mMiniKeyboardCache.get(popupKey);
-        if (container == null) {
-            container = inflateMiniKeyboardContainer(popupKey);
-            mMiniKeyboardCache.put(popupKey, container);
+    protected boolean onLongPress(Key parentKey, PointerTracker tracker) {
+        PopupPanel popupPanel = mPopupPanelCache.get(parentKey);
+        if (popupPanel == null) {
+            popupPanel = onCreatePopupPanel(parentKey);
+            if (popupPanel == null)
+                return false;
+            mPopupPanelCache.put(parentKey, popupPanel);
         }
-        mMiniKeyboardView = (KeyboardView)container.findViewById(R.id.KeyboardView);
-        final MiniKeyboard miniKeyboard = (MiniKeyboard)mMiniKeyboardView.getKeyboard();
-
-        if (mWindowOffset == null) {
-            mWindowOffset = new int[2];
-            getLocationInWindow(mWindowOffset);
+        if (mPopupWindow == null) {
+            mPopupWindow = new PopupWindow(getContext());
+            mPopupWindow.setBackgroundDrawable(null);
+            mPopupWindow.setAnimationStyle(R.style.PopupMiniKeyboardAnimation);
+            // Allow popup window to be drawn off the screen.
+            mPopupWindow.setClippingEnabled(false);
         }
-        final int pointX = (mConfigShowMiniKeyboardAtTouchedPoint) ? tracker.getLastX()
-                : popupKey.mX + popupKey.mWidth / 2;
-        final int keyboardLeft = pointX - miniKeyboard.getDefaultCoordX() + getPaddingLeft();
-        final int popupX = Math.max(0, Math.min(keyboardLeft,
-                mMiniKeyboardParent.getWidth() - miniKeyboard.getMinWidth()))
-                - container.getPaddingLeft() + mWindowOffset[0];
-        final int popupY = popupKey.mY - mKeyboard.getVerticalGap() + getPaddingTop()
-                - (container.getMeasuredHeight() - container.getPaddingBottom()) + mWindowOffset[1];
-        final int x = popupX;
-        final int y = mShowPreview && miniKeyboard.isOneRowKeys()
-                ? mPopupPreviewDisplayedY : popupY;
-
-        mMiniKeyboardOriginX = x + container.getPaddingLeft() - mWindowOffset[0];
-        mMiniKeyboardOriginY = y + container.getPaddingTop() - mWindowOffset[1];
-        mMiniKeyboardView.setPopupOffset(x, y);
-        if (miniKeyboard.setShifted(
-                mKeyboard == null ? false : mKeyboard.isShiftedOrShiftLocked())) {
-            mMiniKeyboardView.invalidateAllKeys();
-        }
-        // Mini keyboard needs no pop-up key preview displayed.
-        mMiniKeyboardView.setPreviewEnabled(false);
-        mMiniKeyboardPopup.setContentView(container);
-        mMiniKeyboardPopup.setWidth(container.getMeasuredWidth());
-        mMiniKeyboardPopup.setHeight(container.getMeasuredHeight());
-        mMiniKeyboardPopup.showAtLocation(this, Gravity.NO_GRAVITY, x, y);
-
-        // Inject down event on the key to mini keyboard.
-        final long eventTime = SystemClock.uptimeMillis();
-        mMiniKeyboardPopupTime = eventTime;
-        final MotionEvent downEvent = generateMiniKeyboardMotionEvent(MotionEvent.ACTION_DOWN,
-                pointX, popupKey.mY + popupKey.mHeight / 2, eventTime);
-        mMiniKeyboardView.onTouchEvent(downEvent);
-        downEvent.recycle();
+        mPopupMiniKeyboardPanel = popupPanel;
+        popupPanel.showPanel(this, parentKey, tracker, mPopupWindow);
 
         invalidateAllKeys();
         return true;
     }
 
-    private MotionEvent generateMiniKeyboardMotionEvent(int action, int x, int y, long eventTime) {
-        return MotionEvent.obtain(mMiniKeyboardPopupTime, eventTime, action,
-                    x - mMiniKeyboardOriginX, y - mMiniKeyboardOriginY, 0);
-    }
-
     private PointerTracker getPointerTracker(final int id) {
         final ArrayList<PointerTracker> pointers = mPointerTrackers;
-        final Key[] keys = mKeys;
         final KeyboardActionListener listener = mKeyboardActionListener;
 
         // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
         for (int i = pointers.size(); i <= id; i++) {
             final PointerTracker tracker =
-                new PointerTracker(i, mHandler, mKeyDetector, this, getResources());
-            if (keys != null)
-                tracker.setKeyboard(mKeyboard, keys, mKeyHysteresisDistance);
+                new PointerTracker(i, this, mHandler, mKeyDetector, this);
+            if (mKeyboard != null)
+                tracker.setKeyboard(mKeyboard, mKeyHysteresisDistance);
             if (listener != null)
                 tracker.setOnKeyboardActionListener(listener);
             pointers.add(tracker);
@@ -1224,8 +1170,8 @@
     }
 
     public boolean isInSlidingKeyInput() {
-        if (mMiniKeyboardView != null) {
-            return mMiniKeyboardView.isInSlidingKeyInput();
+        if (mPopupMiniKeyboardPanel != null) {
+            return mPopupMiniKeyboardPanel.isInSlidingKeyInput();
         } else {
             return mPointerQueue.isInSlidingKeyInput();
         }
@@ -1245,20 +1191,17 @@
         // TODO: cleanup this code into a multi-touch to single-touch event converter class?
         // If the device does not have distinct multi-touch support panel, ignore all multi-touch
         // events except a transition from/to single-touch.
-        if ((!mHasDistinctMultitouch || mIsAccessibilityEnabled)
-                && pointerCount > 1 && oldPointerCount > 1) {
+        if (!mHasDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
             return true;
         }
 
         // Track the last few movements to look for spurious swipes.
         mSwipeTracker.addMovement(me);
 
-        // Gesture detector must be enabled only when mini-keyboard is not on the screen and
-        // accessibility is not enabled.
-        // TODO: Reconcile gesture detection and accessibility features.
-        if (mMiniKeyboardView == null && !mIsAccessibilityEnabled
-                && mGestureDetector != null && mGestureDetector.onTouchEvent(me)) {
-            dismissKeyPreview();
+        // Gesture detector must be enabled only when mini-keyboard is not on the screen.
+        if (mPopupMiniKeyboardPanel == null && mGestureDetector != null
+                && mGestureDetector.onTouchEvent(me)) {
+            dismissAllKeyPreviews();
             mHandler.cancelKeyTimers();
             return true;
         }
@@ -1269,26 +1212,13 @@
         final int x = (int)me.getX(index);
         final int y = (int)me.getY(index);
 
-        // Needs to be called after the gesture detector gets a turn, as it may have
-        // displayed the mini keyboard
-        if (mMiniKeyboardView != null) {
-            final int miniKeyboardPointerIndex = me.findPointerIndex(mMiniKeyboardTrackerId);
-            if (miniKeyboardPointerIndex >= 0 && miniKeyboardPointerIndex < pointerCount) {
-                final int miniKeyboardX = (int)me.getX(miniKeyboardPointerIndex);
-                final int miniKeyboardY = (int)me.getY(miniKeyboardPointerIndex);
-                MotionEvent translated = generateMiniKeyboardMotionEvent(action,
-                        miniKeyboardX, miniKeyboardY, eventTime);
-                mMiniKeyboardView.onTouchEvent(translated);
-                translated.recycle();
-            }
-            return true;
+        // Needs to be called after the gesture detector gets a turn, as it may have displayed the
+        // mini keyboard
+        if (mPopupMiniKeyboardPanel != null) {
+            return mPopupMiniKeyboardPanel.onTouchEvent(me);
         }
 
         if (mHandler.isInKeyRepeat()) {
-            // It will keep being in the key repeating mode while the key is being pressed.
-            if (action == MotionEvent.ACTION_MOVE) {
-                return true;
-            }
             final PointerTracker tracker = getPointerTracker(id);
             // Key repeating timer will be canceled if 2 or more keys are in action, and current
             // event (UP or DOWN) is non-modifier key.
@@ -1301,17 +1231,26 @@
         // TODO: cleanup this code into a multi-touch to single-touch event converter class?
         // Translate mutli-touch event to single-touch events on the device that has no distinct
         // multi-touch panel.
-        if (!mHasDistinctMultitouch || mIsAccessibilityEnabled) {
+        if (!mHasDistinctMultitouch) {
             // Use only main (id=0) pointer tracker.
             PointerTracker tracker = getPointerTracker(0);
             if (pointerCount == 1 && oldPointerCount == 2) {
                 // Multi-touch to single touch transition.
-                // Send a down event for the latest pointer.
-                tracker.onDownEvent(x, y, eventTime, null);
+                // Send a down event for the latest pointer if the key is different from the
+                // previous key.
+                final int newKeyIndex = tracker.getKeyIndexOn(x, y);
+                if (mOldKeyIndex != newKeyIndex) {
+                    tracker.onDownEvent(x, y, eventTime, null);
+                    if (action == MotionEvent.ACTION_UP)
+                        tracker.onUpEvent(x, y, eventTime, null);
+                }
             } else if (pointerCount == 2 && oldPointerCount == 1) {
                 // Single-touch to multi-touch transition.
                 // Send an up event for the last pointer.
-                tracker.onUpEvent(tracker.getLastX(), tracker.getLastY(), eventTime, null);
+                final int lastX = tracker.getLastX();
+                final int lastY = tracker.getLastY();
+                mOldKeyIndex = tracker.getKeyIndexOn(lastX, lastY);
+                tracker.onUpEvent(lastX, lastY, eventTime, null);
             } else if (pointerCount == 1 && oldPointerCount == 1) {
                 tracker.onTouchEvent(action, x, y, eventTime, null);
             } else {
@@ -1352,12 +1291,12 @@
     }
 
     public void closing() {
-        mPreviewPopup.dismiss();
+        mPreviewText.setVisibility(View.GONE);
         mHandler.cancelAllMessages();
 
-        dismissPopupKeyboard();
+        dismissMiniKeyboard();
         mDirtyRect.union(0, 0, getWidth(), getHeight());
-        mMiniKeyboardCache.clear();
+        mPopupPanelCache.clear();
         requestLayout();
     }
 
@@ -1372,21 +1311,50 @@
         closing();
     }
 
-    private void dismissPopupKeyboard() {
-        if (mMiniKeyboardPopup.isShowing()) {
-            mMiniKeyboardPopup.dismiss();
-            mMiniKeyboardView = null;
-            mMiniKeyboardOriginX = 0;
-            mMiniKeyboardOriginY = 0;
+    private boolean dismissMiniKeyboard() {
+        if (mPopupWindow != null && mPopupWindow.isShowing()) {
+            mPopupWindow.dismiss();
+            mPopupMiniKeyboardPanel = null;
             invalidateAllKeys();
+            return true;
         }
+        return false;
     }
 
     public boolean handleBack() {
-        if (mMiniKeyboardPopup.isShowing()) {
-            dismissPopupKeyboard();
-            return true;
+        return dismissMiniKeyboard();
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+            return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event)
+                    || super.dispatchTouchEvent(event);
         }
+
+        return super.dispatchTouchEvent(event);
+    }
+
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+            final PointerTracker tracker = getPointerTracker(0);
+            return AccessibleKeyboardViewProxy.getInstance().dispatchPopulateAccessibilityEvent(
+                    event, tracker) || super.dispatchPopulateAccessibilityEvent(event);
+        }
+
+        return super.dispatchPopulateAccessibilityEvent(event);
+    }
+
+    public boolean onHoverEvent(MotionEvent event) {
+        // Since reflection doesn't support calling superclass methods, this
+        // method checks for the existence of onHoverEvent() in the View class
+        // before returning a value.
+        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+            final PointerTracker tracker = getPointerTracker(0);
+            return AccessibleKeyboardViewProxy.getInstance().onHoverEvent(event, tracker);
+        }
+
         return false;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index 5820049..76eac1a 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -16,9 +16,6 @@
 
 package com.android.inputmethod.keyboard;
 
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SubtypeSwitcher;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
@@ -26,6 +23,9 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.graphics.Paint.Align;
 import android.graphics.PorterDuff;
@@ -33,41 +33,55 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 
+import com.android.inputmethod.keyboard.internal.SlidingLocaleDrawable;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+
+import java.lang.ref.SoftReference;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 
 // TODO: We should remove this class
 public class LatinKeyboard extends Keyboard {
-    public static final int OPACITY_FULLY_OPAQUE = 255;
     private static final int SPACE_LED_LENGTH_PERCENT = 80;
 
-    private final Context mContext;
+    public static final int CODE_NEXT_LANGUAGE = -100;
+    public static final int CODE_PREV_LANGUAGE = -101;
+
+    private final Resources mRes;
+    private final Theme mTheme;
+    private final SubtypeSwitcher mSubtypeSwitcher = SubtypeSwitcher.getInstance();
 
     /* Space key and its icons, drawables and colors. */
     private final Key mSpaceKey;
     private final Drawable mSpaceIcon;
     private final Drawable mSpacePreviewIcon;
-    private final int[] mSpaceKeyIndexArray;
-    private final Drawable mSpaceAutoCorrectionIndicator;
-    private final Drawable mButtonArrowLeftIcon;
-    private final Drawable mButtonArrowRightIcon;
+    private final int mSpaceKeyIndex;
+    private final boolean mAutoCorrectionSpacebarLedEnabled;
+    private final Drawable mAutoCorrectionSpacebarLedIcon;
+    private final Drawable mSpacebarArrowLeftIcon;
+    private final Drawable mSpacebarArrowRightIcon;
     private final int mSpacebarTextColor;
     private final int mSpacebarTextShadowColor;
-    private final int mSpacebarVerticalCorrection;
     private float mSpacebarTextFadeFactor = 0.0f;
-    private int mSpaceDragStartX;
-    private int mSpaceDragLastDiff;
-    private boolean mCurrentlyInSpace;
-    private SlidingLocaleDrawable mSlidingLocaleIcon;
+    private final int mSpacebarLanguageSwitchThreshold;
+    private int mSpacebarSlidingLanguageSwitchDiff;
+    private final SlidingLocaleDrawable mSlidingLocaleIcon;
+    private final HashMap<Integer, SoftReference<BitmapDrawable>> mSpaceDrawableCache =
+            new HashMap<Integer, SoftReference<BitmapDrawable>>();
 
     /* Shortcut key and its icons if available */
     private final Key mShortcutKey;
     private final Drawable mEnabledShortcutIcon;
     private final Drawable mDisabledShortcutIcon;
 
-    private static final float SPACEBAR_DRAG_THRESHOLD = 0.8f;
-    // Minimum width of space key preview (proportional to keyboard width)
-    private static final float SPACEBAR_POPUP_MIN_RATIO = 0.4f;
+    // Minimum width of spacebar dragging to trigger the language switch (represented by the number
+    // of the most common key width of this keyboard).
+    private static final int SPACEBAR_DRAG_WIDTH = 3;
+    // Minimum width of space key preview (proportional to keyboard width).
+    private static final float SPACEBAR_POPUP_MIN_RATIO = 0.5f;
     // Height in space key the language name will be drawn. (proportional to space key height)
     public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f;
     // If the full language name needs to be smaller than this value to be drawn on space key,
@@ -77,10 +91,10 @@
     private static final String SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "small";
     private static final String MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "medium";
 
-    public LatinKeyboard(Context context, KeyboardId id) {
-        super(context, id.getXmlId(), id);
-        final Resources res = context.getResources();
-        mContext = context;
+    public LatinKeyboard(Context context, KeyboardId id, int width) {
+        super(context, id.getXmlId(), id, width);
+        mRes = context.getResources();
+        mTheme = context.getTheme();
 
         final List<Key> keys = getKeys();
         int spaceKeyIndex = -1;
@@ -92,7 +106,7 @@
             case CODE_SPACE:
                 spaceKeyIndex = index;
                 break;
-            case CODE_VOICE:
+            case CODE_SHORTCUT:
                 shortcutKeyIndex = index;
                 break;
             }
@@ -102,26 +116,40 @@
         mSpaceKey = (spaceKeyIndex >= 0) ? keys.get(spaceKeyIndex) : null;
         mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon() : null;
         mSpacePreviewIcon = (mSpaceKey != null) ? mSpaceKey.getPreviewIcon() : null;
-        mSpaceKeyIndexArray = new int[] { spaceKeyIndex };
+        mSpaceKeyIndex = spaceKeyIndex;
 
         mShortcutKey = (shortcutKeyIndex >= 0) ? keys.get(shortcutKeyIndex) : null;
         mEnabledShortcutIcon = (mShortcutKey != null) ? mShortcutKey.getIcon() : null;
 
-        mSpacebarTextColor = res.getColor(R.color.latinkeyboard_bar_language_text);
-        if (id.mColorScheme == KeyboardView.COLOR_SCHEME_BLACK) {
-            mSpacebarTextShadowColor = res.getColor(
-                    R.color.latinkeyboard_bar_language_shadow_black);
-            mDisabledShortcutIcon = res.getDrawable(R.drawable.sym_bkeyboard_voice_off);
-        } else { // default color scheme is KeyboardView.COLOR_SCHEME_WHITE
-            mSpacebarTextShadowColor = res.getColor(
-                    R.color.latinkeyboard_bar_language_shadow_white);
-            mDisabledShortcutIcon = res.getDrawable(R.drawable.sym_keyboard_voice_off_holo);
+        final TypedArray a = context.obtainStyledAttributes(
+                null, R.styleable.LatinKeyboard, R.attr.latinKeyboardStyle, R.style.LatinKeyboard);
+        mAutoCorrectionSpacebarLedEnabled = a.getBoolean(
+                R.styleable.LatinKeyboard_autoCorrectionSpacebarLedEnabled, false);
+        mAutoCorrectionSpacebarLedIcon = a.getDrawable(
+                R.styleable.LatinKeyboard_autoCorrectionSpacebarLedIcon);
+        mDisabledShortcutIcon = a.getDrawable(R.styleable.LatinKeyboard_disabledShortcutIcon);
+        mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboard_spacebarTextColor, 0);
+        mSpacebarTextShadowColor = a.getColor(
+                R.styleable.LatinKeyboard_spacebarTextShadowColor, 0);
+        mSpacebarArrowLeftIcon = a.getDrawable(
+                R.styleable.LatinKeyboard_spacebarArrowLeftIcon);
+        mSpacebarArrowRightIcon = a.getDrawable(
+                R.styleable.LatinKeyboard_spacebarArrowRightIcon);
+        a.recycle();
+
+        // The threshold is "key width" x 1.25
+        mSpacebarLanguageSwitchThreshold = (getMostCommonKeyWidth() * 5) / 4;
+
+        if (mSpaceKey != null) {
+            final int slidingIconWidth = Math.max(mSpaceKey.mWidth,
+                    (int)(getMinWidth() * SPACEBAR_POPUP_MIN_RATIO));
+            final int spaceKeyheight = mSpacePreviewIcon.getIntrinsicHeight();
+            mSlidingLocaleIcon = new SlidingLocaleDrawable(
+                    context, mSpacePreviewIcon, slidingIconWidth, spaceKeyheight);
+            mSlidingLocaleIcon.setBounds(0, 0, slidingIconWidth, spaceKeyheight);
+        } else {
+            mSlidingLocaleIcon = null;
         }
-        mSpaceAutoCorrectionIndicator = res.getDrawable(R.drawable.sym_keyboard_space_led);
-        mButtonArrowLeftIcon = res.getDrawable(R.drawable.sym_keyboard_language_arrows_left);
-        mButtonArrowRightIcon = res.getDrawable(R.drawable.sym_keyboard_language_arrows_right);
-        mSpacebarVerticalCorrection = res.getDimensionPixelOffset(
-                R.dimen.spacebar_vertical_correction);
     }
 
     public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboardView view) {
@@ -137,15 +165,25 @@
         return newColor;
     }
 
+    private static ColorFilter getSpacebarDrawableFilter(float fadeFactor) {
+        final ColorMatrix colorMatrix = new ColorMatrix();
+        colorMatrix.setScale(1, 1, 1, fadeFactor);
+        return new ColorMatrixColorFilter(colorMatrix);
+    }
+
     public void updateShortcutKey(boolean available, LatinKeyboardView view) {
         if (mShortcutKey == null)
             return;
-        mShortcutKey.mEnabled = available;
+        mShortcutKey.setEnabled(available);
         mShortcutKey.setIcon(available ? mEnabledShortcutIcon : mDisabledShortcutIcon);
         if (view != null)
             view.invalidateKey(mShortcutKey);
     }
 
+    public boolean needsAutoCorrectionSpacebarLed() {
+        return mAutoCorrectionSpacebarLedEnabled;
+    }
+
     /**
      * @return a key which should be invalidated.
      */
@@ -157,19 +195,14 @@
     private void updateSpacebarForLocale(boolean isAutoCorrection) {
         if (mSpaceKey == null)
             return;
-        final Resources res = mContext.getResources();
         // If application locales are explicitly selected.
-        if (SubtypeSwitcher.getInstance().needsToDisplayLanguage()) {
-            mSpaceKey.setIcon(new BitmapDrawable(res,
-                    drawSpacebar(OPACITY_FULLY_OPAQUE, isAutoCorrection)));
+        if (mSubtypeSwitcher.needsToDisplayLanguage()) {
+            mSpaceKey.setIcon(getSpaceDrawable(
+                    mSubtypeSwitcher.getInputLocale(), isAutoCorrection));
+        } else if (isAutoCorrection) {
+            mSpaceKey.setIcon(getSpaceDrawable(null, true));
         } else {
-            // sym_keyboard_space_led can be shared with Black and White symbol themes.
-            if (isAutoCorrection) {
-                mSpaceKey.setIcon(new BitmapDrawable(res,
-                        drawSpacebar(OPACITY_FULLY_OPAQUE, isAutoCorrection)));
-            } else {
-                mSpaceKey.setIcon(mSpaceIcon);
-            }
+            mSpaceKey.setIcon(mSpaceIcon);
         }
     }
 
@@ -181,9 +214,8 @@
     }
 
     // Layout local language name and left and right arrow on spacebar.
-    private static String layoutSpacebar(Paint paint, Locale locale, Drawable lArrow,
-            Drawable rArrow, int width, int height, float origTextSize,
-            boolean allowVariableTextSize) {
+    private static String layoutSpacebar(Paint paint, Locale locale, Drawable icon, Drawable lArrow,
+            Drawable rArrow, int width, int height, float origTextSize) {
         final float arrowWidth = lArrow.getIntrinsicWidth();
         final float arrowHeight = lArrow.getIntrinsicHeight();
         final float maxTextWidth = width - (arrowWidth + arrowWidth);
@@ -194,17 +226,23 @@
         int textWidth = getTextWidth(paint, language, origTextSize, bounds);
         // Assuming text width and text size are proportional to each other.
         float textSize = origTextSize * Math.min(maxTextWidth / textWidth, 1.0f);
+        // allow variable text size
+        textWidth = getTextWidth(paint, language, textSize, bounds);
+        // If text size goes too small or text does not fit, use middle or short name
+        final boolean useMiddleName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME)
+                || (textWidth > maxTextWidth);
 
         final boolean useShortName;
-        if (allowVariableTextSize) {
-            textWidth = getTextWidth(paint, language, textSize, bounds);
-            // If text size goes too small or text does not fit, use short name
-            useShortName = textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME
-                    || textWidth > maxTextWidth;
+        if (useMiddleName) {
+            language = SubtypeSwitcher.getMiddleDisplayLanguage(locale);
+            textWidth = getTextWidth(paint, language, origTextSize, bounds);
+            textSize = origTextSize * Math.min(maxTextWidth / textWidth, 1.0f);
+            useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME)
+                    || (textWidth > maxTextWidth);
         } else {
-            useShortName = textWidth > maxTextWidth;
-            textSize = origTextSize;
+            useShortName = false;
         }
+
         if (useShortName) {
             language = SubtypeSwitcher.getShortDisplayLanguage(locale);
             textWidth = getTextWidth(paint, language, origTextSize, bounds);
@@ -213,7 +251,9 @@
         paint.setTextSize(textSize);
 
         // Place left and right arrow just before and after language text.
-        final float baseline = height * SPACEBAR_LANGUAGE_BASELINE;
+        final float textHeight = -paint.ascent() + paint.descent();
+        final float baseline = (icon != null) ? height * SPACEBAR_LANGUAGE_BASELINE
+                : height / 2 + textHeight / 2;
         final int top = (int)(baseline - arrowHeight);
         final float remains = (width - textWidth) / 2;
         lArrow.setBounds((int)(remains - arrowWidth), top, (int)remains, (int)baseline);
@@ -223,19 +263,31 @@
         return language;
     }
 
-    private Bitmap drawSpacebar(int opacity, boolean isAutoCorrection) {
+    private BitmapDrawable getSpaceDrawable(Locale locale, boolean isAutoCorrection) {
+        final Integer hashCode = Arrays.hashCode(
+                new Object[] { locale, isAutoCorrection, mSpacebarTextFadeFactor });
+        final SoftReference<BitmapDrawable> ref = mSpaceDrawableCache.get(hashCode);
+        BitmapDrawable drawable = (ref == null) ? null : ref.get();
+        if (drawable == null) {
+            drawable = new BitmapDrawable(mRes, drawSpacebar(
+                    locale, isAutoCorrection, mSpacebarTextFadeFactor));
+            mSpaceDrawableCache.put(hashCode, new SoftReference<BitmapDrawable>(drawable));
+        }
+        return drawable;
+    }
+
+    private Bitmap drawSpacebar(Locale inputLocale, boolean isAutoCorrection,
+            float textFadeFactor) {
         final int width = mSpaceKey.mWidth;
         final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : mSpaceKey.mHeight;
         final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         final Canvas canvas = new Canvas(buffer);
-        final Resources res = mContext.getResources();
-        canvas.drawColor(res.getColor(R.color.latinkeyboard_transparent), PorterDuff.Mode.CLEAR);
+        final Resources res = mRes;
+        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
 
-        SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
         // If application locales are explicitly selected.
-        if (subtypeSwitcher.needsToDisplayLanguage()) {
+        if (inputLocale != null) {
             final Paint paint = new Paint();
-            paint.setAlpha(opacity);
             paint.setAntiAlias(true);
             paint.setTextAlign(Align.CENTER);
 
@@ -252,11 +304,9 @@
                 defaultTextSize = 14;
             }
 
-            final boolean allowVariableTextSize = true;
-            final String language = layoutSpacebar(paint, subtypeSwitcher.getInputLocale(),
-                    mButtonArrowLeftIcon, mButtonArrowRightIcon, width, height,
-                    getTextSizeFromTheme(mContext.getTheme(), textStyle, defaultTextSize),
-                    allowVariableTextSize);
+            final String language = layoutSpacebar(paint, inputLocale, mSpaceIcon,
+                    mSpacebarArrowLeftIcon, mSpacebarArrowRightIcon, width, height,
+                    getTextSizeFromTheme(mTheme, textStyle, defaultTextSize));
 
             // Draw language text with shadow
             // In case there is no space icon, we will place the language text at the center of
@@ -265,27 +315,32 @@
             final float textHeight = -paint.ascent() + descent;
             final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE
                     : height / 2 + textHeight / 2;
-            paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, mSpacebarTextFadeFactor));
+            paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, textFadeFactor));
             canvas.drawText(language, width / 2, baseline - descent - 1, paint);
-            paint.setColor(getSpacebarTextColor(mSpacebarTextColor, mSpacebarTextFadeFactor));
+            paint.setColor(getSpacebarTextColor(mSpacebarTextColor, textFadeFactor));
             canvas.drawText(language, width / 2, baseline - descent, paint);
 
-            // Put arrows that are already layed out on either side of the text
-            if (SubtypeSwitcher.getInstance().useSpacebarLanguageSwitcher()
-                    && subtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
-                mButtonArrowLeftIcon.draw(canvas);
-                mButtonArrowRightIcon.draw(canvas);
+            // Put arrows that are already laid out on either side of the text
+            // Because language switch is disabled on phone and number layouts, hide arrows.
+            // TODO: Sort out how to enable language switch on these layouts.
+            if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()
+                    && mSubtypeSwitcher.getEnabledKeyboardLocaleCount() > 1
+                    && !(isPhoneKeyboard() || isNumberKeyboard())) {
+                mSpacebarArrowLeftIcon.setColorFilter(getSpacebarDrawableFilter(textFadeFactor));
+                mSpacebarArrowRightIcon.setColorFilter(getSpacebarDrawableFilter(textFadeFactor));
+                mSpacebarArrowLeftIcon.draw(canvas);
+                mSpacebarArrowRightIcon.draw(canvas);
             }
         }
 
         // Draw the spacebar icon at the bottom
         if (isAutoCorrection) {
             final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100;
-            final int iconHeight = mSpaceAutoCorrectionIndicator.getIntrinsicHeight();
+            final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight();
             int x = (width - iconWidth) / 2;
             int y = height - iconHeight;
-            mSpaceAutoCorrectionIndicator.setBounds(x, y, x + iconWidth, y + iconHeight);
-            mSpaceAutoCorrectionIndicator.draw(canvas);
+            mAutoCorrectionSpacebarLedIcon.setBounds(x, y, x + iconWidth, y + iconHeight);
+            mAutoCorrectionSpacebarLedIcon.draw(canvas);
         } else if (mSpaceIcon != null) {
             final int iconWidth = mSpaceIcon.getIntrinsicWidth();
             final int iconHeight = mSpaceIcon.getIntrinsicHeight();
@@ -297,16 +352,16 @@
         return buffer;
     }
 
-    private void updateLocaleDrag(int diff) {
-        if (mSlidingLocaleIcon == null) {
-            final int width = Math.max(mSpaceKey.mWidth,
-                    (int)(getMinWidth() * SPACEBAR_POPUP_MIN_RATIO));
-            final int height = mSpacePreviewIcon.getIntrinsicHeight();
-            mSlidingLocaleIcon =
-                    new SlidingLocaleDrawable(mContext, mSpacePreviewIcon, width, height);
-            mSlidingLocaleIcon.setBounds(0, 0, width, height);
-            mSpaceKey.setPreviewIcon(mSlidingLocaleIcon);
-        }
+    public void setSpacebarSlidingLanguageSwitchDiff(int diff) {
+        mSpacebarSlidingLanguageSwitchDiff = diff;
+    }
+
+    public void updateSpacebarPreviewIcon(int diff) {
+        if (mSpacebarSlidingLanguageSwitchDiff == diff)
+            return;
+        mSpacebarSlidingLanguageSwitchDiff = diff;
+        if (mSlidingLocaleIcon == null)
+            return;
         mSlidingLocaleIcon.setDiff(diff);
         if (Math.abs(diff) == Integer.MAX_VALUE) {
             mSpaceKey.setPreviewIcon(mSpacePreviewIcon);
@@ -316,72 +371,52 @@
         mSpaceKey.getPreviewIcon().invalidateSelf();
     }
 
-    public int getLanguageChangeDirection() {
-        if (mSpaceKey == null || SubtypeSwitcher.getInstance().getEnabledKeyboardLocaleCount() <= 1
-                || Math.abs(mSpaceDragLastDiff) < mSpaceKey.mWidth * SPACEBAR_DRAG_THRESHOLD) {
-            return 0; // No change
-        }
-        return mSpaceDragLastDiff > 0 ? 1 : -1;
-    }
-
-    public void keyReleased() {
-        mCurrentlyInSpace = false;
-        mSpaceDragLastDiff = 0;
-        if (mSpaceKey != null) {
-            updateLocaleDrag(Integer.MAX_VALUE);
-        }
+    public boolean shouldTriggerSpacebarSlidingLanguageSwitch(int diff) {
+        // On phone and number layouts, sliding language switch is disabled.
+        // TODO: Sort out how to enable language switch on these layouts.
+        if (isPhoneKeyboard() || isNumberKeyboard())
+            return false;
+        return Math.abs(diff) > mSpacebarLanguageSwitchThreshold;
     }
 
     /**
-     * Does the magic of locking the touch gesture into the spacebar when
-     * switching input languages.
+     * Return true if spacebar needs showing preview even when "popup on keypress" is off.
+     * @param keyIndex index of the pressing key
+     * @return true if spacebar needs showing preview
      */
     @Override
-    public boolean isInside(Key key, int pointX, int pointY) {
-        int x = pointX;
-        int y = pointY;
-        final int code = key.mCode;
-        if (code == CODE_SPACE) {
-            y += mSpacebarVerticalCorrection;
-            if (SubtypeSwitcher.getInstance().useSpacebarLanguageSwitcher()
-                    && SubtypeSwitcher.getInstance().getEnabledKeyboardLocaleCount() > 1) {
-                if (mCurrentlyInSpace) {
-                    int diff = x - mSpaceDragStartX;
-                    if (Math.abs(diff - mSpaceDragLastDiff) > 0) {
-                        updateLocaleDrag(diff);
-                    }
-                    mSpaceDragLastDiff = diff;
-                    return true;
-                } else {
-                    boolean isOnSpace = key.isOnKey(x, y);
-                    if (isOnSpace) {
-                        mCurrentlyInSpace = true;
-                        mSpaceDragStartX = x;
-                        updateLocaleDrag(0);
-                    }
-                    return isOnSpace;
-                }
-            }
+    public boolean needSpacebarPreview(int keyIndex) {
+        // This method is called when "popup on keypress" is off.
+        if (!mSubtypeSwitcher.useSpacebarLanguageSwitcher())
+            return false;
+        // Dismiss key preview.
+        if (keyIndex == KeyDetector.NOT_A_KEY)
+            return true;
+        // Key is not a spacebar.
+        if (keyIndex != mSpaceKeyIndex)
+            return false;
+        // The language switcher will be displayed only when the dragging distance is greater
+        // than the threshold.
+        return shouldTriggerSpacebarSlidingLanguageSwitch(mSpacebarSlidingLanguageSwitchDiff);
+    }
+
+    public int getLanguageChangeDirection() {
+        if (mSpaceKey == null || mSubtypeSwitcher.getEnabledKeyboardLocaleCount() <= 1
+                || Math.abs(mSpacebarSlidingLanguageSwitchDiff)
+                    < getMostCommonKeyWidth() * SPACEBAR_DRAG_WIDTH) {
+            return 0; // No change
         }
-
-        // Lock into the spacebar
-        if (mCurrentlyInSpace) return false;
-
-        return key.isOnKey(x, y);
+        return mSpacebarSlidingLanguageSwitchDiff > 0 ? 1 : -1;
     }
 
     @Override
     public int[] getNearestKeys(int x, int y) {
-        if (mCurrentlyInSpace) {
-            return mSpaceKeyIndexArray;
-        } else {
-            // Avoid dead pixels at edges of the keyboard
-            return super.getNearestKeys(Math.max(0, Math.min(x, getMinWidth() - 1)),
-                    Math.max(0, Math.min(y, getHeight() - 1)));
-        }
+        // Avoid dead pixels at edges of the keyboard
+        return super.getNearestKeys(Math.max(0, Math.min(x, getMinWidth() - 1)),
+                Math.max(0, Math.min(y, getHeight() - 1)));
     }
 
-    private static int getTextSizeFromTheme(Theme theme, int style, int defValue) {
+    public static int getTextSizeFromTheme(Theme theme, int style, int defValue) {
         TypedArray array = theme.obtainStyledAttributes(
                 style, new int[] { android.R.attr.textSize });
         int textSize = array.getDimensionPixelSize(array.getResourceId(0, 0), defValue);
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index 77e9cae..901df6a 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -16,10 +16,6 @@
 
 package com.android.inputmethod.keyboard;
 
-import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.Utils;
-import com.android.inputmethod.voice.VoiceIMEConnector;
-
 import android.content.Context;
 import android.graphics.Canvas;
 import android.text.TextUtils;
@@ -27,6 +23,10 @@
 import android.util.Log;
 import android.view.MotionEvent;
 
+import com.android.inputmethod.deprecated.VoiceProxy;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.Utils;
+
 // TODO: We should remove this class
 public class LatinKeyboardView extends KeyboardView {
     private static final String TAG = LatinKeyboardView.class.getSimpleName();
@@ -47,7 +47,7 @@
     private int mLastY;
 
     public LatinKeyboardView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
+        super(context, attrs);
     }
 
     public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
@@ -55,24 +55,19 @@
     }
 
     @Override
-    public void setPreviewEnabled(boolean previewEnabled) {
+    public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
         LatinKeyboard latinKeyboard = getLatinKeyboard();
         if (latinKeyboard != null
                 && (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard())) {
             // Phone and number keyboard never shows popup preview (except language switch).
-            super.setPreviewEnabled(false);
+            super.setKeyPreviewPopupEnabled(false, delay);
         } else {
-            super.setPreviewEnabled(previewEnabled);
+            super.setKeyPreviewPopupEnabled(previewEnabled, delay);
         }
     }
 
     @Override
     public void setKeyboard(Keyboard newKeyboard) {
-        final LatinKeyboard oldKeyboard = getLatinKeyboard();
-        if (oldKeyboard != null) {
-            // Reset old keyboard state before switching to new keyboard.
-            oldKeyboard.keyReleased();
-        }
         super.setKeyboard(newKeyboard);
         // One-seventh of the keyboard width seems like a reasonable threshold
         mJumpThresholdSquare = newKeyboard.getMinWidth() / 7;
@@ -125,7 +120,7 @@
                 && keyboard.isShiftedOrShiftLocked()
                 && !TextUtils.isEmpty(label) && label.length() < 3
                 && Character.isLowerCase(label.charAt(0))) {
-            return label.toString().toUpperCase();
+            return label.toString().toUpperCase(keyboard.mId.mLocale);
         }
         return label;
     }
@@ -145,10 +140,6 @@
         // If device has distinct multi touch panel, there is no need to check sudden jump.
         if (hasDistinctMultitouch())
             return false;
-        // If accessibiliy is enabled, stop looking for sudden jumps because it interferes
-        // with touch exploration of the keyboard.
-        if (isAccessibilityEnabled())
-            return false;
         final int action = me.getAction();
         final int x = (int) me.getX();
         final int y = (int) me.getY();
@@ -182,7 +173,8 @@
                 if (!mDroppingEvents) {
                     mDroppingEvents = true;
                     // Send an up event
-                    MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
+                    MotionEvent translated = MotionEvent.obtain(
+                            me.getEventTime(), me.getEventTime(),
                             MotionEvent.ACTION_UP,
                             mLastX, mLastY, me.getMetaState());
                     super.onTouchEvent(translated);
@@ -216,8 +208,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent me) {
-        LatinKeyboard keyboard = getLatinKeyboard();
-        if (keyboard == null) return true;
+        if (getLatinKeyboard() == null) return true;
 
         // If there was a sudden jump, return without processing the actual motion event.
         if (handleSuddenJump(me)) {
@@ -226,24 +217,6 @@
             return true;
         }
 
-        // Reset any bounding box controls in the keyboard
-        if (me.getAction() == MotionEvent.ACTION_DOWN) {
-            keyboard.keyReleased();
-        }
-
-        if (me.getAction() == MotionEvent.ACTION_UP) {
-            int languageDirection = keyboard.getLanguageChangeDirection();
-            if (languageDirection != 0) {
-                getOnKeyboardActionListener().onCodeInput(
-                        languageDirection == 1
-                        ? Keyboard.CODE_NEXT_LANGUAGE : Keyboard.CODE_PREV_LANGUAGE,
-                        null, mLastX, mLastY);
-                me.setAction(MotionEvent.ACTION_CANCEL);
-                keyboard.keyReleased();
-                return super.onTouchEvent(me);
-            }
-        }
-
         return super.onTouchEvent(me);
     }
 
@@ -264,6 +237,6 @@
     @Override
     protected void onAttachedToWindow() {
         // Token is available from here.
-        VoiceIMEConnector.getInstance().onAttachedToWindow();
+        VoiceProxy.getInstance().onAttachedToWindow();
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
index 8d243e0..2d6766f 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
@@ -23,8 +23,8 @@
 public class MiniKeyboard extends Keyboard {
     private int mDefaultKeyCoordX;
 
-    public MiniKeyboard(Context context, int xmlLayoutResId, KeyboardId id) {
-        super(context, xmlLayoutResId, id);
+    public MiniKeyboard(Context context, int xmlLayoutResId, Keyboard parentKeyboard) {
+        super(context, xmlLayoutResId, null, parentKeyboard.getMinWidth());
     }
 
     public void setDefaultCoordX(int pos) {
@@ -35,7 +35,7 @@
         return mDefaultKeyCoordX;
     }
 
-    public boolean isOneRowKeys() {
+    public boolean isOneRowKeyboard() {
         final List<Key> keys = getKeys();
         if (keys.size() == 0) return false;
         final int edgeFlags = keys.get(0).mEdgeFlags;
@@ -47,5 +47,6 @@
         // both top and bottom edge flags set.
         return (edgeFlags & Keyboard.EDGE_TOP) != 0
                 && (edgeFlags & Keyboard.EDGE_BOTTOM) != 0;
+
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
index 1243f6f..cc5c3bb 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
@@ -16,9 +16,9 @@
 
 package com.android.inputmethod.keyboard;
 
-public class MiniKeyboardKeyDetector extends KeyDetector {
-    private static final int MAX_NEARBY_KEYS = 1;
+import java.util.List;
 
+public class MiniKeyboardKeyDetector extends KeyDetector {
     private final int mSlideAllowanceSquare;
     private final int mSlideAllowanceSquareTop;
 
@@ -31,20 +31,21 @@
 
     @Override
     protected int getMaxNearbyKeys() {
-        return MAX_NEARBY_KEYS;
+        // No nearby key will be returned.
+        return 1;
     }
 
     @Override
     public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
-        final Key[] keys = getKeys();
+        final List<Key> keys = getKeys();
         final int touchX = getTouchX(x);
         final int touchY = getTouchY(y);
 
         int nearestIndex = NOT_A_KEY;
         int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
-        final int keyCount = keys.length;
+        final int keyCount = keys.size();
         for (int index = 0; index < keyCount; index++) {
-            final int dist = keys[index].squaredDistanceToEdge(touchX, touchY);
+            final int dist = keys.get(index).squaredDistanceToEdge(touchX, touchY);
             if (dist < nearestDist) {
                 nearestIndex = index;
                 nearestDist = dist;
@@ -52,7 +53,7 @@
         }
 
         if (allCodes != null && nearestIndex != NOT_A_KEY)
-            allCodes[0] = keys[nearestIndex].mCode;
+            allCodes[0] = keys.get(nearestIndex).mCode;
         return nearestIndex;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 4b3fe8b..c7620f9 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -16,15 +16,20 @@
 
 package com.android.inputmethod.keyboard;
 
-import com.android.inputmethod.keyboard.KeyboardView.UIHandler;
-import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.R;
-
 import android.content.res.Resources;
+import android.os.SystemClock;
 import android.util.Log;
 import android.view.MotionEvent;
 
+import com.android.inputmethod.keyboard.KeyboardView.UIHandler;
+import com.android.inputmethod.keyboard.internal.PointerTrackerKeyState;
+import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+
 import java.util.Arrays;
+import java.util.List;
 
 public class PointerTracker {
     private static final String TAG = PointerTracker.class.getSimpleName();
@@ -36,9 +41,9 @@
 
     public interface UIProxy {
         public void invalidateKey(Key key);
-        public void showPreview(int keyIndex, PointerTracker tracker);
+        public void showKeyPreview(int keyIndex, PointerTracker tracker);
+        public void dismissKeyPreview(PointerTracker tracker);
         public boolean hasDistinctMultitouch();
-        public boolean isAccessibilityEnabled();
     }
 
     public final int mPointerId;
@@ -48,9 +53,7 @@
     private final int mLongPressKeyTimeout;
     private final int mLongPressShiftKeyTimeout;
 
-    // Miscellaneous constants
-    private static final int NOT_A_KEY = KeyDetector.NOT_A_KEY;
-
+    private final KeyboardView mKeyboardView;
     private final UIProxy mProxy;
     private final UIHandler mHandler;
     private final KeyDetector mKeyDetector;
@@ -63,15 +66,12 @@
     private final int mTouchNoiseThresholdDistanceSquared;
 
     private Keyboard mKeyboard;
-    private Key[] mKeys;
+    private List<Key> mKeys;
     private int mKeyHysteresisDistanceSquared = -1;
     private int mKeyQuarterWidthSquared;
 
     private final PointerTrackerKeyState mKeyState;
 
-    // true if accessibility is enabled in the parent keyboard
-    private boolean mIsAccessibilityEnabled;
-
     // true if keyboard layout has been changed.
     private boolean mKeyboardLayoutHasBeenChanged;
 
@@ -87,8 +87,14 @@
     // true if sliding key is allowed.
     private boolean mIsAllowedSlidingKeyInput;
 
-    // pressed key
-    private int mPreviousKey = NOT_A_KEY;
+    // ignore modifier key if true
+    private boolean mIgnoreModifierKey;
+
+    // TODO: Remove these hacking variables
+    // true if this pointer is in sliding language switch
+    private boolean mIsInSlidingLanguageSwitch;
+    private int mSpaceKeyIndex;
+    private final SubtypeSwitcher mSubtypeSwitcher;
 
     // Empty {@link KeyboardActionListener}
     private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener() {
@@ -106,18 +112,19 @@
         public void onSwipeDown() {}
     };
 
-    public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy,
-            Resources res) {
+    public PointerTracker(int id, KeyboardView keyboardView, UIHandler handler,
+            KeyDetector keyDetector, UIProxy proxy) {
         if (proxy == null || handler == null || keyDetector == null)
             throw new NullPointerException();
         mPointerId = id;
+        mKeyboardView = keyboardView;
         mProxy = proxy;
         mHandler = handler;
         mKeyDetector = keyDetector;
         mKeyboardSwitcher = KeyboardSwitcher.getInstance();
         mKeyState = new PointerTrackerKeyState(keyDetector);
-        mIsAccessibilityEnabled = proxy.isAccessibilityEnabled();
         mHasDistinctMultitouch = proxy.hasDistinctMultitouch();
+        final Resources res = mKeyboardView.getResources();
         mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled);
         mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start);
         mLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout);
@@ -127,21 +134,22 @@
                 R.dimen.config_touch_noise_threshold_distance);
         mTouchNoiseThresholdDistanceSquared = (int)(
                 touchNoiseThresholdDistance * touchNoiseThresholdDistance);
+        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
     }
 
     public void setOnKeyboardActionListener(KeyboardActionListener listener) {
         mListener = listener;
     }
 
-    public void setAccessibilityEnabled(boolean accessibilityEnabled) {
-        mIsAccessibilityEnabled = accessibilityEnabled;
-    }
-
     // Returns true if keyboard has been changed by this callback.
     private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key, boolean withSliding) {
+        final boolean ignoreModifierKey = mIgnoreModifierKey && isModifierCode(key.mCode);
         if (DEBUG_LISTENER)
-            Log.d(TAG, "onPress    : " + keyCodePrintable(key.mCode) + " sliding=" + withSliding);
-        if (key.mEnabled) {
+            Log.d(TAG, "onPress    : " + keyCodePrintable(key.mCode) + " sliding=" + withSliding
+                    + " ignoreModifier=" + ignoreModifierKey);
+        if (ignoreModifierKey)
+            return false;
+        if (key.isEnabled()) {
             mListener.onPress(key.mCode, withSliding);
             final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
             mKeyboardLayoutHasBeenChanged = false;
@@ -153,26 +161,34 @@
     // Note that we need primaryCode argument because the keyboard may in shifted state and the
     // primaryCode is different from {@link Key#mCode}.
     private void callListenerOnCodeInput(Key key, int primaryCode, int[] keyCodes, int x, int y) {
+        final boolean ignoreModifierKey = mIgnoreModifierKey && isModifierCode(key.mCode);
         if (DEBUG_LISTENER)
             Log.d(TAG, "onCodeInput: " + keyCodePrintable(primaryCode)
-                    + " codes="+ Arrays.toString(keyCodes) + " x=" + x + " y=" + y);
-        if (key.mEnabled)
+                    + " codes="+ Arrays.toString(keyCodes) + " x=" + x + " y=" + y
+                    + " ignoreModifier=" + ignoreModifierKey);
+        if (ignoreModifierKey)
+            return;
+        if (key.isEnabled())
             mListener.onCodeInput(primaryCode, keyCodes, x, y);
     }
 
     private void callListenerOnTextInput(Key key) {
         if (DEBUG_LISTENER)
             Log.d(TAG, "onTextInput: text=" + key.mOutputText);
-        if (key.mEnabled)
+        if (key.isEnabled())
             mListener.onTextInput(key.mOutputText);
     }
 
     // Note that we need primaryCode argument because the keyboard may in shifted state and the
     // primaryCode is different from {@link Key#mCode}.
     private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) {
+        final boolean ignoreModifierKey = mIgnoreModifierKey && isModifierCode(key.mCode);
         if (DEBUG_LISTENER)
-            Log.d(TAG, "onRelease  : " + keyCodePrintable(primaryCode) + " sliding=" + withSliding);
-        if (key.mEnabled)
+            Log.d(TAG, "onRelease  : " + keyCodePrintable(primaryCode) + " sliding="
+                    + withSliding + " ignoreModifier=" + ignoreModifierKey);
+        if (ignoreModifierKey)
+            return;
+        if (key.isEnabled())
             mListener.onRelease(primaryCode, withSliding);
     }
 
@@ -182,11 +198,11 @@
         mListener.onCancelInput();
     }
 
-    public void setKeyboard(Keyboard keyboard, Key[] keys, float keyHysteresisDistance) {
-        if (keyboard == null || keys == null || keyHysteresisDistance < 0)
+    public void setKeyboard(Keyboard keyboard, float keyHysteresisDistance) {
+        if (keyboard == null || keyHysteresisDistance < 0)
             throw new IllegalArgumentException();
         mKeyboard = keyboard;
-        mKeys = keys;
+        mKeys = keyboard.getKeys();
         mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance);
         final int keyQuarterWidth = keyboard.getKeyWidth() / 4;
         mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
@@ -199,11 +215,11 @@
     }
 
     private boolean isValidKeyIndex(int keyIndex) {
-        return keyIndex >= 0 && keyIndex < mKeys.length;
+        return keyIndex >= 0 && keyIndex < mKeys.size();
     }
 
     public Key getKey(int keyIndex) {
-        return isValidKeyIndex(keyIndex) ? mKeys[keyIndex] : null;
+        return isValidKeyIndex(keyIndex) ? mKeys.get(keyIndex) : null;
     }
 
     private static boolean isModifierCode(int primaryCode) {
@@ -229,34 +245,33 @@
         return key != null && key.mCode == Keyboard.CODE_SHIFT;
     }
 
+    public int getKeyIndexOn(int x, int y) {
+        return mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
+    }
+
     public boolean isSpaceKey(int keyIndex) {
         Key key = getKey(keyIndex);
         return key != null && key.mCode == Keyboard.CODE_SPACE;
     }
 
-    public void releaseKey() {
-        updateKeyGraphics(NOT_A_KEY);
+    public void setReleasedKeyGraphics() {
+        setReleasedKeyGraphics(mKeyState.getKeyIndex());
     }
 
-    private void updateKeyGraphics(int keyIndex) {
-        int oldKeyIndex = mPreviousKey;
-        mPreviousKey = keyIndex;
-        if (keyIndex != oldKeyIndex) {
-            if (isValidKeyIndex(oldKeyIndex)) {
-                // if new key index is not a key, old key was just released inside of the key.
-                final boolean inside = (keyIndex == NOT_A_KEY);
-                mKeys[oldKeyIndex].onReleased(inside);
-                mProxy.invalidateKey(mKeys[oldKeyIndex]);
-            }
-            if (isValidKeyIndex(keyIndex)) {
-                mKeys[keyIndex].onPressed();
-                mProxy.invalidateKey(mKeys[keyIndex]);
-            }
+    private void setReleasedKeyGraphics(int keyIndex) {
+        final Key key = getKey(keyIndex);
+        if (key != null) {
+            key.onReleased();
+            mProxy.invalidateKey(key);
         }
     }
 
-    public void setAlreadyProcessed() {
-        mKeyAlreadyProcessed = true;
+    private void setPressedKeyGraphics(int keyIndex) {
+        final Key key = getKey(keyIndex);
+        if (key != null && key.isEnabled()) {
+            key.onPressed();
+            mProxy.invalidateKey(key);
+        }
     }
 
     private void checkAssertion(PointerTrackerQueue queue) {
@@ -302,7 +317,7 @@
                 if (DEBUG_MODE)
                     Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT
                             + " distance=" + distanceSquared);
-                setAlreadyProcessed();
+                mKeyAlreadyProcessed = true;
                 return;
             }
         }
@@ -321,32 +336,33 @@
     private void onDownEventInternal(int x, int y, long eventTime) {
         int keyIndex = mKeyState.onDownKey(x, y, eventTime);
         // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
-        // from modifier key, 3) this pointer is on mini-keyboard, or 4) accessibility is enabled.
+        // from modifier key, or 3) this pointer is on mini-keyboard.
         mIsAllowedSlidingKeyInput = mConfigSlidingKeyInputEnabled || isModifierInternal(keyIndex)
-                || mKeyDetector instanceof MiniKeyboardKeyDetector
-                || mIsAccessibilityEnabled;
+                || mKeyDetector instanceof MiniKeyboardKeyDetector;
         mKeyboardLayoutHasBeenChanged = false;
         mKeyAlreadyProcessed = false;
         mIsRepeatableKey = false;
         mIsInSlidingKeyInput = false;
+        mIsInSlidingLanguageSwitch = false;
+        mIgnoreModifierKey = false;
         if (isValidKeyIndex(keyIndex)) {
             // This onPress call may have changed keyboard layout. Those cases are detected at
             // {@link #setKeyboard}. In those cases, we should update keyIndex according to the new
             // keyboard layout.
-            if (callListenerOnPressAndCheckKeyboardLayoutChange(mKeys[keyIndex], false))
+            if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), false))
                 keyIndex = mKeyState.onDownKey(x, y, eventTime);
-        }
-        if (isValidKeyIndex(keyIndex)) {
-            // Accessibility disables key repeat because users may need to pause on a key to hear
-            // its spoken description.
-            if (mKeys[keyIndex].mRepeatable && !mIsAccessibilityEnabled) {
-                repeatKey(keyIndex);
-                mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this);
-                mIsRepeatableKey = true;
-            }
+
+            startRepeatKey(keyIndex);
             startLongPressTimer(keyIndex);
+            showKeyPreview(keyIndex);
+            setPressedKeyGraphics(keyIndex);
         }
-        showKeyPreviewAndUpdateKeyGraphics(keyIndex);
+    }
+
+    private void startSlidingKeyInput(Key key) {
+        if (!mIsInSlidingKeyInput)
+            mIgnoreModifierKey = isModifierCode(key.mCode);
+        mIsInSlidingKeyInput = true;
     }
 
     public void onMoveEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
@@ -357,10 +373,17 @@
             return;
         final PointerTrackerKeyState keyState = mKeyState;
 
+        // TODO: Remove this hacking code
+        if (mIsInSlidingLanguageSwitch) {
+            ((LatinKeyboard)mKeyboard).updateSpacebarPreviewIcon(x - keyState.getKeyX());
+            showKeyPreview(mSpaceKeyIndex);
+            return;
+        }
         final int lastX = keyState.getLastX();
         final int lastY = keyState.getLastY();
+        final int oldKeyIndex = keyState.getKeyIndex();
+        final Key oldKey = getKey(oldKeyIndex);
         int keyIndex = keyState.onMoveKey(x, y);
-        final Key oldKey = getKey(keyState.getKeyIndex());
         if (isValidKeyIndex(keyIndex)) {
             if (oldKey == null) {
                 // The pointer has been slid in to the new key, but the finger was not on any keys.
@@ -372,13 +395,17 @@
                     keyIndex = keyState.onMoveKey(x, y);
                 keyState.onMoveToNewKey(keyIndex, x, y);
                 startLongPressTimer(keyIndex);
-            } else if (!isMinorMoveBounce(x, y, keyIndex)) {
+                showKeyPreview(keyIndex);
+                setPressedKeyGraphics(keyIndex);
+            } else if (isMajorEnoughMoveToBeOnNewKey(x, y, keyIndex)) {
                 // The pointer has been slid in to the new key from the previous key, we must call
                 // onRelease() first to notify that the previous key has been released, then call
                 // onPress() to notify that the new key is being pressed.
-                mIsInSlidingKeyInput = true;
+                setReleasedKeyGraphics(oldKeyIndex);
                 callListenerOnRelease(oldKey, oldKey.mCode, true);
-                mHandler.cancelLongPressTimers();
+                startSlidingKeyInput(oldKey);
+                mHandler.cancelKeyTimers();
+                startRepeatKey(keyIndex);
                 if (mIsAllowedSlidingKeyInput) {
                     // This onPress call may have changed keyboard layout. Those cases are detected
                     // at {@link #setKeyboard}. In those cases, we should update keyIndex according
@@ -387,6 +414,8 @@
                         keyIndex = keyState.onMoveKey(x, y);
                     keyState.onMoveToNewKey(keyIndex, x, y);
                     startLongPressTimer(keyIndex);
+                    setPressedKeyGraphics(keyIndex);
+                    showKeyPreview(keyIndex);
                 } else {
                     // HACK: On some devices, quick successive touches may be translated to sudden
                     // move by touch panel firmware. This hack detects the case and translates the
@@ -398,32 +427,50 @@
                         if (DEBUG_MODE)
                             Log.w(TAG, String.format("onMoveEvent: sudden move is translated to "
                                     + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y));
-                        onUpEventInternal(lastX, lastY, eventTime);
+                        onUpEventInternal(lastX, lastY, eventTime, true);
                         onDownEventInternal(x, y, eventTime);
                     } else {
-                        setAlreadyProcessed();
-                        showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY);
+                        mKeyAlreadyProcessed = true;
+                        dismissKeyPreview();
+                        setReleasedKeyGraphics(oldKeyIndex);
                     }
-                    return;
+                }
+            }
+            // TODO: Remove this hack code
+            else if (isSpaceKey(keyIndex) && !mIsInSlidingLanguageSwitch
+                    && mKeyboard instanceof LatinKeyboard) {
+                final LatinKeyboard keyboard = ((LatinKeyboard)mKeyboard);
+                if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()
+                        && mSubtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
+                    final int diff = x - keyState.getKeyX();
+                    if (keyboard.shouldTriggerSpacebarSlidingLanguageSwitch(diff)) {
+                        // Detect start sliding language switch.
+                        mIsInSlidingLanguageSwitch = true;
+                        mSpaceKeyIndex = keyIndex;
+                        keyboard.updateSpacebarPreviewIcon(diff);
+                        // Display spacebar slide language switcher.
+                        showKeyPreview(keyIndex);
+                        if (queue != null)
+                            queue.releaseAllPointersExcept(this, eventTime, true);
+                    }
                 }
             }
         } else {
-            if (oldKey != null && !isMinorMoveBounce(x, y, keyIndex)) {
+            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, keyIndex)) {
                 // The pointer has been slid out from the previous key, we must call onRelease() to
                 // notify that the previous key has been released.
-                mIsInSlidingKeyInput = true;
+                setReleasedKeyGraphics(oldKeyIndex);
                 callListenerOnRelease(oldKey, oldKey.mCode, true);
+                startSlidingKeyInput(oldKey);
                 mHandler.cancelLongPressTimers();
                 if (mIsAllowedSlidingKeyInput) {
-                    keyState.onMoveToNewKey(keyIndex, x ,y);
+                    keyState.onMoveToNewKey(keyIndex, x, y);
                 } else {
-                    setAlreadyProcessed();
-                    showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY);
-                    return;
+                    mKeyAlreadyProcessed = true;
+                    dismissKeyPreview();
                 }
             }
         }
-        showKeyPreviewAndUpdateKeyGraphics(mKeyState.getKeyIndex());
     }
 
     public void onUpEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
@@ -435,42 +482,72 @@
             if (isModifier()) {
                 // Before processing an up event of modifier key, all pointers already being
                 // tracked should be released.
-                queue.releaseAllPointersExcept(this, eventTime);
+                queue.releaseAllPointersExcept(this, eventTime, true);
             } else {
                 queue.releaseAllPointersOlderThan(this, eventTime);
             }
             queue.remove(this);
         }
-        onUpEventInternal(x, y, eventTime);
+        onUpEventInternal(x, y, eventTime, true);
     }
 
-    public void onUpEventForRelease(int x, int y, long eventTime) {
-        onUpEventInternal(x, y, eventTime);
+    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
+    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
+    // "virtual" up event.
+    public void onPhantomUpEvent(int x, int y, long eventTime, boolean updateReleasedKeyGraphics) {
+        if (DEBUG_EVENT)
+            printTouchEvent("onPhntEvent:", x, y, eventTime);
+        onUpEventInternal(x, y, eventTime, updateReleasedKeyGraphics);
+        mKeyAlreadyProcessed = true;
     }
 
-    private void onUpEventInternal(int pointX, int pointY, long eventTime) {
-        int x = pointX;
-        int y = pointY;
+    private void onUpEventInternal(int x, int y, long eventTime,
+            boolean updateReleasedKeyGraphics) {
         mHandler.cancelKeyTimers();
-        mHandler.cancelPopupPreview();
-        showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY);
+        mHandler.cancelShowKeyPreview(this);
         mIsInSlidingKeyInput = false;
+        final PointerTrackerKeyState keyState = mKeyState;
+        final int keyX, keyY;
+        if (isMajorEnoughMoveToBeOnNewKey(x, y, keyState.onMoveKey(x, y))) {
+            keyX = x;
+            keyY = y;
+        } else {
+            // Use previous fixed key coordinates.
+            keyX = keyState.getKeyX();
+            keyY = keyState.getKeyY();
+        }
+        final int keyIndex = keyState.onUpKey(keyX, keyY, eventTime);
+        dismissKeyPreview();
+        if (updateReleasedKeyGraphics)
+            setReleasedKeyGraphics(keyIndex);
         if (mKeyAlreadyProcessed)
             return;
-        final PointerTrackerKeyState keyState = mKeyState;
-        int keyIndex = keyState.onUpKey(x, y, eventTime);
-        if (isMinorMoveBounce(x, y, keyIndex)) {
-            // Use previous fixed key index and coordinates.
-            keyIndex = keyState.getKeyIndex();
-            x = keyState.getKeyX();
-            y = keyState.getKeyY();
+        // TODO: Remove this hacking code
+        if (mIsInSlidingLanguageSwitch) {
+            setReleasedKeyGraphics(mSpaceKeyIndex);
+            final int languageDir = ((LatinKeyboard)mKeyboard).getLanguageChangeDirection();
+            if (languageDir != 0) {
+                final int code = (languageDir == 1)
+                        ? LatinKeyboard.CODE_NEXT_LANGUAGE : LatinKeyboard.CODE_PREV_LANGUAGE;
+                // This will change keyboard layout.
+                mListener.onCodeInput(code, new int[] {code}, keyX, keyY);
+            }
+            mIsInSlidingLanguageSwitch = false;
+            ((LatinKeyboard)mKeyboard).setSpacebarSlidingLanguageSwitchDiff(0);
+            return;
         }
         if (!mIsRepeatableKey) {
-            detectAndSendKey(keyIndex, x, y);
+            detectAndSendKey(keyIndex, keyX, keyY);
         }
+    }
 
-        if (isValidKeyIndex(keyIndex))
-            mProxy.invalidateKey(mKeys[keyIndex]);
+    public void onLongPressed(PointerTrackerQueue queue) {
+        mKeyAlreadyProcessed = true;
+        if (queue != null) {
+            // TODO: Support chording + long-press input.
+            queue.releaseAllPointersExcept(this, SystemClock.uptimeMillis(), true);
+            queue.remove(this);
+        }
     }
 
     public void onCancelEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
@@ -478,22 +555,34 @@
         if (DEBUG_EVENT)
             printTouchEvent("onCancelEvt:", x, y, eventTime);
 
-        if (queue != null)
+        if (queue != null) {
+            queue.releaseAllPointersExcept(this, eventTime, true);
             queue.remove(this);
+        }
         onCancelEventInternal();
     }
 
     private void onCancelEventInternal() {
         mHandler.cancelKeyTimers();
-        mHandler.cancelPopupPreview();
-        showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY);
+        mHandler.cancelShowKeyPreview(this);
+        dismissKeyPreview();
+        setReleasedKeyGraphics(mKeyState.getKeyIndex());
         mIsInSlidingKeyInput = false;
-        int keyIndex = mKeyState.getKeyIndex();
-        if (isValidKeyIndex(keyIndex))
-           mProxy.invalidateKey(mKeys[keyIndex]);
     }
 
-    public void repeatKey(int keyIndex) {
+    private void startRepeatKey(int keyIndex) {
+        final Key key = getKey(keyIndex);
+        if (key != null && key.mRepeatable) {
+            dismissKeyPreview();
+            onRepeatKey(keyIndex);
+            mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this);
+            mIsRepeatableKey = true;
+        } else {
+            mIsRepeatableKey = false;
+        }
+    }
+
+    public void onRepeatKey(int keyIndex) {
         Key key = getKey(keyIndex);
         if (key != null) {
             detectAndSendKey(keyIndex, key.mX, key.mY);
@@ -512,47 +601,51 @@
         return mKeyState.getDownTime();
     }
 
-    private boolean isMinorMoveBounce(int x, int y, int newKey) {
+    private boolean isMajorEnoughMoveToBeOnNewKey(int x, int y, int newKey) {
         if (mKeys == null || mKeyHysteresisDistanceSquared < 0)
             throw new IllegalStateException("keyboard and/or hysteresis not set");
         int curKey = mKeyState.getKeyIndex();
         if (newKey == curKey) {
-            return true;
-        } else if (isValidKeyIndex(curKey)) {
-            return mKeys[curKey].squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared;
-        } else {
             return false;
+        } else if (isValidKeyIndex(curKey)) {
+            return mKeys.get(curKey).squaredDistanceToEdge(x, y) >= mKeyHysteresisDistanceSquared;
+        } else {
+            return true;
         }
     }
 
-    private void showKeyPreviewAndUpdateKeyGraphics(int keyIndex) {
-        updateKeyGraphics(keyIndex);
-        // The modifier key, such as shift key, should not be shown as preview when multi-touch is
-        // supported. On the other hand, if multi-touch is not supported, the modifier key should
-        // be shown as preview. If accessibility is turned on, the modifier key should be shown as
-        // preview.
-        if (mHasDistinctMultitouch && isModifier() && !mIsAccessibilityEnabled) {
-            mProxy.showPreview(NOT_A_KEY, this);
-        } else {
-            mProxy.showPreview(keyIndex, this);
-        }
+    // The modifier key, such as shift key, should not show its key preview.
+    private boolean isKeyPreviewNotRequired(int keyIndex) {
+        final Key key = getKey(keyIndex);
+        if (key == null || !key.isEnabled())
+            return true;
+        // Such as spacebar sliding language switch.
+        if (mKeyboard.needSpacebarPreview(keyIndex))
+            return false;
+        final int code = key.mCode;
+        return isModifierCode(code) || code == Keyboard.CODE_DELETE
+                || code == Keyboard.CODE_ENTER || code == Keyboard.CODE_SPACE;
+    }
+
+    private void showKeyPreview(int keyIndex) {
+        if (isKeyPreviewNotRequired(keyIndex))
+            return;
+        mProxy.showKeyPreview(keyIndex, this);
+    }
+
+    private void dismissKeyPreview() {
+        mProxy.dismissKeyPreview(this);
     }
 
     private void startLongPressTimer(int keyIndex) {
-        // Accessibility disables long press because users are likely to need to pause on a key
-        // for an unspecified duration in order to hear the key's spoken description.
-        if (mIsAccessibilityEnabled) {
-            return;
-        }
         Key key = getKey(keyIndex);
         if (key.mCode == Keyboard.CODE_SHIFT) {
             mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this);
-        } else if (key.mManualTemporaryUpperCaseCode != Keyboard.CODE_DUMMY
-                && mKeyboard.isManualTemporaryUpperCase()) {
+        } else if (key.hasUppercaseLetter() && mKeyboard.isManualTemporaryUpperCase()) {
             // We need not start long press timer on the key which has manual temporary upper case
             // code defined and the keyboard is in manual temporary upper case mode.
             return;
-        } else if (mKeyboardSwitcher.isInMomentaryAutoModeSwitchState()) {
+        } else if (mKeyboardSwitcher.isInMomentarySwitchState()) {
             // We use longer timeout for sliding finger input started from the symbols mode key.
             mHandler.startLongPressTimer(mLongPressKeyTimeout * 3, keyIndex, this);
         } else {
@@ -575,10 +668,9 @@
             mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes);
 
             // If keyboard is in manual temporary upper case state and key has manual temporary
-            // shift code, alternate character code should be sent.
-            if (mKeyboard.isManualTemporaryUpperCase()
-                    && key.mManualTemporaryUpperCaseCode != Keyboard.CODE_DUMMY) {
-                code = key.mManualTemporaryUpperCaseCode;
+            // uppercase letter as key hint letter, alternate character code should be sent.
+            if (mKeyboard.isManualTemporaryUpperCase() && key.hasUppercaseLetter()) {
+                code = key.mHintLetter.charAt(0);
                 codes[0] = code;
             }
 
diff --git a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
new file mode 100644
index 0000000..3b8c364
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.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.keyboard;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.PopupWindow;
+
+import com.android.inputmethod.latin.R;
+
+/**
+ * A view that renders a virtual {@link MiniKeyboard}. It handles rendering of keys and detecting
+ * key presses and touch movements.
+ */
+public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
+    private final int[] mCoordinates = new int[2];
+    private final boolean mConfigShowMiniKeyboardAtTouchedPoint;
+
+    private int mOriginX;
+    private int mOriginY;
+    private long mDownTime;
+
+    public PopupMiniKeyboardView(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.popupMiniKeyboardViewStyle);
+    }
+
+    public PopupMiniKeyboardView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        final Resources res = context.getResources();
+        mConfigShowMiniKeyboardAtTouchedPoint = res.getBoolean(
+                R.bool.config_show_mini_keyboard_at_touched_point);
+        // Override default ProximityKeyDetector.
+        mKeyDetector = new MiniKeyboardKeyDetector(res.getDimension(
+                R.dimen.mini_keyboard_slide_allowance));
+        // Remove gesture detector on mini-keyboard
+        mGestureDetector = null;
+        setKeyPreviewPopupEnabled(false, 0);
+    }
+
+    @Override
+    public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
+        // Mini keyboard needs no pop-up key preview displayed, so we pass always false with a
+        // delay of 0. The delay does not matter actually since the popup is not shown anyway.
+        super.setKeyPreviewPopupEnabled(false, 0);
+    }
+
+    @Override
+    public void showPanel(KeyboardView parentKeyboardView, Key parentKey,
+            PointerTracker tracker, PopupWindow window) {
+        final View container = (View)getParent();
+        final MiniKeyboard miniKeyboard = (MiniKeyboard)getKeyboard();
+        final Keyboard parentKeyboard = parentKeyboardView.getKeyboard();
+
+        parentKeyboardView.getLocationInWindow(mCoordinates);
+        final int pointX = (mConfigShowMiniKeyboardAtTouchedPoint) ? tracker.getLastX()
+                : parentKey.mX + parentKey.mWidth / 2;
+        final int pointY = parentKey.mY;
+        final int miniKeyboardLeft = pointX - miniKeyboard.getDefaultCoordX()
+                + parentKeyboardView.getPaddingLeft();
+        final int x = Math.max(0, Math.min(miniKeyboardLeft,
+                parentKeyboardView.getWidth() - miniKeyboard.getMinWidth()))
+                - container.getPaddingLeft() + mCoordinates[0];
+        final int y = pointY - parentKeyboard.getVerticalGap()
+                - (container.getMeasuredHeight() - container.getPaddingBottom())
+                + parentKeyboardView.getPaddingTop() + mCoordinates[1];
+
+        if (miniKeyboard.setShifted(parentKeyboard.isShiftedOrShiftLocked())) {
+            invalidateAllKeys();
+        }
+        window.setContentView(container);
+        window.setWidth(container.getMeasuredWidth());
+        window.setHeight(container.getMeasuredHeight());
+        window.showAtLocation(parentKeyboardView, Gravity.NO_GRAVITY, x, y);
+
+        mOriginX = x + container.getPaddingLeft() - mCoordinates[0];
+        mOriginY = y + container.getPaddingTop() - mCoordinates[1];
+        mDownTime = SystemClock.uptimeMillis();
+
+        // Inject down event on the key to mini keyboard.
+        final MotionEvent downEvent = MotionEvent.obtain(mDownTime, mDownTime,
+                MotionEvent.ACTION_DOWN, pointX - mOriginX,
+                pointY + parentKey.mHeight / 2 - mOriginY, 0);
+        onTouchEvent(downEvent);
+        downEvent.recycle();
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent me) {
+        me.offsetLocation(-mOriginX, -mOriginY);
+        return super.onTouchEvent(me);
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/PopupPanel.java b/java/src/com/android/inputmethod/keyboard/PopupPanel.java
new file mode 100644
index 0000000..386e11f
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/PopupPanel.java
@@ -0,0 +1,45 @@
+/*
+ * 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.keyboard;
+
+import android.view.MotionEvent;
+import android.widget.PopupWindow;
+
+public interface PopupPanel {
+    /**
+     * Show popup panel.
+     * @param parentKeyboardView the parent KeyboardView that has the parent key.
+     * @param parentKey the parent key that is the source of this popup panel
+     * @param tracker the pointer tracker that pressesd the parent key
+     * @param window PopupWindow to be used to show this popup panel
+     */
+    public void showPanel(KeyboardView parentKeyboardView, Key parentKey,
+            PointerTracker tracker, PopupWindow window);
+
+    /**
+     * Check if the pointer is in siding key input mode.
+     * @return true if the pointer is sliding key input mode.
+     */
+    public boolean isInSlidingKeyInput();
+
+    /**
+     * The motion event handler.
+     * @param me the MotionEvent to be processed.
+     * @return true if the motion event is processed and should be consumed.
+     */
+    public boolean onTouchEvent(MotionEvent me);
+}
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java b/java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java
deleted file mode 100644
index e2ff8c4..0000000
--- a/java/src/com/android/inputmethod/keyboard/ProximityKeyDetector.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.util.Log;
-
-import java.util.Arrays;
-
-public class ProximityKeyDetector extends KeyDetector {
-    private static final String TAG = ProximityKeyDetector.class.getSimpleName();
-    private static final boolean DEBUG = false;
-
-    private static final int MAX_NEARBY_KEYS = 12;
-
-    // working area
-    private final int[] mDistances = new int[MAX_NEARBY_KEYS];
-    private final int[] mIndices = new int[MAX_NEARBY_KEYS];
-
-    @Override
-    protected int getMaxNearbyKeys() {
-        return MAX_NEARBY_KEYS;
-    }
-
-    private void initializeNearbyKeys() {
-        Arrays.fill(mDistances, Integer.MAX_VALUE);
-        Arrays.fill(mIndices, NOT_A_KEY);
-    }
-
-    /**
-     * Insert the key into nearby keys buffer and sort nearby keys by ascending order of distance.
-     *
-     * @param keyIndex index of the key.
-     * @param distance distance between the key's edge and user touched point.
-     * @param isOnKey true if the point is on the key.
-     * @return order of the key in the nearby buffer, 0 if it is the nearest key.
-     */
-    private int sortNearbyKeys(int keyIndex, int distance, boolean isOnKey) {
-        final int[] distances = mDistances;
-        final int[] indices = mIndices;
-        for (int insertPos = 0; insertPos < distances.length; insertPos++) {
-            final int comparingDistance = distances[insertPos];
-            if (distance < comparingDistance || (distance == comparingDistance && isOnKey)) {
-                final int nextPos = insertPos + 1;
-                if (nextPos < distances.length) {
-                    System.arraycopy(distances, insertPos, distances, nextPos,
-                            distances.length - nextPos);
-                    System.arraycopy(indices, insertPos, indices, nextPos,
-                            indices.length - nextPos);
-                }
-                distances[insertPos] = distance;
-                indices[insertPos] = keyIndex;
-                return insertPos;
-            }
-        }
-        return distances.length;
-    }
-
-    private void getNearbyKeyCodes(final int[] allCodes) {
-        final Key[] keys = getKeys();
-        final int[] indices = mIndices;
-
-        // allCodes[0] should always have the key code even if it is a non-letter key.
-        if (indices[0] == NOT_A_KEY) {
-            allCodes[0] = NOT_A_CODE;
-            return;
-        }
-
-        int numCodes = 0;
-        for (int j = 0; j < indices.length && numCodes < allCodes.length; j++) {
-            final int index = indices[j];
-            if (index == NOT_A_KEY)
-                break;
-            final int code = keys[index].mCode;
-            // filter out a non-letter key from nearby keys
-            if (code < Keyboard.CODE_SPACE)
-                continue;
-            allCodes[numCodes++] = code;
-        }
-    }
-
-    @Override
-    public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
-        final Key[] keys = getKeys();
-        final int touchX = getTouchX(x);
-        final int touchY = getTouchY(y);
-
-        initializeNearbyKeys();
-        int primaryIndex = NOT_A_KEY;
-        for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) {
-            final Key key = keys[index];
-            final boolean isInside = key.isInside(touchX, touchY);
-            final int distance = key.squaredDistanceToEdge(touchX, touchY);
-            if (isInside || (mProximityCorrectOn && distance < mProximityThresholdSquare)) {
-                final int insertedPosition = sortNearbyKeys(index, distance, isInside);
-                if (insertedPosition == 0 && isInside)
-                    primaryIndex = index;
-            }
-        }
-
-        if (allCodes != null && allCodes.length > 0) {
-            getNearbyKeyCodes(allCodes);
-            if (DEBUG) {
-                Log.d(TAG, "x=" + x + " y=" + y
-                        + " primary="
-                        + (primaryIndex == NOT_A_KEY ? "none" : keys[primaryIndex].mCode)
-                        + " codes=" + Arrays.toString(allCodes));
-            }
-        }
-
-        return primaryIndex;
-    }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
similarity index 89%
rename from java/src/com/android/inputmethod/keyboard/KeyStyles.java
rename to java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
index 8d9b1b4..983f064 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
@@ -14,16 +14,15 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
-
-import com.android.inputmethod.keyboard.KeyboardParser.ParseException;
-import com.android.inputmethod.latin.R;
+package com.android.inputmethod.keyboard.internal;
 
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.graphics.drawable.Drawable;
 import android.util.Log;
 
+import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException;
+import com.android.inputmethod.latin.R;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -37,7 +36,6 @@
 
     public interface KeyStyle {
         public CharSequence[] getTextArray(TypedArray a, int index);
-        public Drawable getDrawable(TypedArray a, int index);
         public CharSequence getText(TypedArray a, int index);
         public int getInt(TypedArray a, int index, int defaultValue);
         public int getFlag(TypedArray a, int index, int defaultValue);
@@ -55,11 +53,6 @@
         }
 
         @Override
-        public Drawable getDrawable(TypedArray a, int index) {
-            return a.getDrawable(index);
-        }
-
-        @Override
         public CharSequence getText(TypedArray a, int index) {
             return a.getText(index);
         }
@@ -140,12 +133,6 @@
         }
 
         @Override
-        public Drawable getDrawable(TypedArray a, int index) {
-            return a.hasValue(index)
-                    ? super.getDrawable(a, index) : (Drawable)mAttributes.get(index);
-        }
-
-        @Override
         public CharSequence getText(TypedArray a, int index) {
             return a.hasValue(index)
                     ? super.getText(a, index) : (CharSequence)mAttributes.get(index);
@@ -177,25 +164,20 @@
             // TODO: Currently not all Key attributes can be declared as style.
             readInt(keyAttr, R.styleable.Keyboard_Key_code);
             readText(keyAttr, R.styleable.Keyboard_Key_keyLabel);
-            readFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption);
-            readTextArray(keyAttr, R.styleable.Keyboard_Key_popupCharacters);
-            readInt(keyAttr, R.styleable.Keyboard_Key_maxPopupKeyboardColumn);
             readText(keyAttr, R.styleable.Keyboard_Key_keyOutputText);
-            readDrawable(keyAttr, R.styleable.Keyboard_Key_keyIcon);
-            readDrawable(keyAttr, R.styleable.Keyboard_Key_iconPreview);
-            readDrawable(keyAttr, R.styleable.Keyboard_Key_keyHintIcon);
-            readDrawable(keyAttr, R.styleable.Keyboard_Key_shiftedIcon);
-            readBoolean(keyAttr, R.styleable.Keyboard_Key_isModifier);
+            readText(keyAttr, R.styleable.Keyboard_Key_keyHintLetter);
+            readTextArray(keyAttr, R.styleable.Keyboard_Key_popupCharacters);
+            readFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption);
+            readInt(keyAttr, R.styleable.Keyboard_Key_keyIcon);
+            readInt(keyAttr, R.styleable.Keyboard_Key_keyIconPreview);
+            readInt(keyAttr, R.styleable.Keyboard_Key_keyIconShifted);
+            readInt(keyAttr, R.styleable.Keyboard_Key_maxPopupKeyboardColumn);
+            readBoolean(keyAttr, R.styleable.Keyboard_Key_isFunctional);
             readBoolean(keyAttr, R.styleable.Keyboard_Key_isSticky);
             readBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable);
             readBoolean(keyAttr, R.styleable.Keyboard_Key_enabled);
         }
 
-        private void readDrawable(TypedArray a, int index) {
-            if (a.hasValue(index))
-                mAttributes.put(index, a.getDrawable(index));
-        }
-
         private void readText(TypedArray a, int index) {
             if (a.hasValue(index))
                 mAttributes.put(index, a.getText(index));
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
new file mode 100644
index 0000000..37b3682
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
@@ -0,0 +1,146 @@
+/*
+ * 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.keyboard.internal;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.R;
+
+public class KeyboardIconsSet {
+    private static final String TAG = KeyboardIconsSet.class.getSimpleName();
+
+    public static final int ICON_UNDEFINED = 0;
+
+    // This should be aligned with Keyboard.keyIcon enum.
+    private static final int ICON_SHIFT_KEY = 1;
+    private static final int ICON_TO_SYMBOL_KEY = 2;
+    private static final int ICON_TO_SYMBOL_KEY_WITH_SHORTCUT = 3;
+    private static final int ICON_DELETE_KEY = 4;
+    private static final int ICON_SETTINGS_KEY = 5;
+    private static final int ICON_SHORTCUT_KEY = 6;
+    private static final int ICON_SPACE_KEY = 7;
+    private static final int ICON_RETURN_KEY = 8;
+    private static final int ICON_SEARCH_KEY = 9;
+    private static final int ICON_TAB_KEY = 10;
+    private static final int ICON_NUM1_KEY = 11;
+    private static final int ICON_NUM2_KEY = 12;
+    private static final int ICON_NUM3_KEY = 13;
+    private static final int ICON_NUM4_KEY = 14;
+    private static final int ICON_NUM5_KEY = 15;
+    private static final int ICON_NUM6_KEY = 16;
+    private static final int ICON_NUM7_KEY = 17;
+    private static final int ICON_NUM8_KEY = 18;
+    private static final int ICON_NUM9_KEY = 19;
+    private static final int ICON_NUM0_KEY = 20;
+    // This should be aligned with Keyboard.keyIconShifted enum.
+    private static final int ICON_SHIFTED_SHIFT_KEY = 21;
+    // This should be aligned with Keyboard.keyIconPreview enum.
+    private static final int ICON_PREVIEW_SPACE_KEY = 22;
+    private static final int ICON_PREVIEW_TAB_KEY = 23;
+    private static final int ICON_PREVIEW_SETTINGS_KEY = 24;
+    private static final int ICON_PREVIEW_SHORTCUT_KEY = 25;
+
+    private static final int ICON_LAST = 25;
+
+    private final Drawable mIcons[] = new Drawable[ICON_LAST + 1];
+
+    private static final int getIconId(int attrIndex) {
+        switch (attrIndex) {
+        case R.styleable.Keyboard_iconShiftKey:
+            return ICON_SHIFT_KEY;
+        case R.styleable.Keyboard_iconToSymbolKey:
+            return ICON_TO_SYMBOL_KEY;
+        case R.styleable.Keyboard_iconToSymbolKeyWithShortcut:
+            return ICON_TO_SYMBOL_KEY_WITH_SHORTCUT;
+        case R.styleable.Keyboard_iconDeleteKey:
+            return ICON_DELETE_KEY;
+        case R.styleable.Keyboard_iconSettingsKey:
+            return ICON_SETTINGS_KEY;
+        case R.styleable.Keyboard_iconShortcutKey:
+            return ICON_SHORTCUT_KEY;
+        case R.styleable.Keyboard_iconSpaceKey:
+            return ICON_SPACE_KEY;
+        case R.styleable.Keyboard_iconReturnKey:
+            return ICON_RETURN_KEY;
+        case R.styleable.Keyboard_iconSearchKey:
+            return ICON_SEARCH_KEY;
+        case R.styleable.Keyboard_iconTabKey:
+            return ICON_TAB_KEY;
+        case R.styleable.Keyboard_iconNum1Key:
+            return ICON_NUM1_KEY;
+        case R.styleable.Keyboard_iconNum2Key:
+            return ICON_NUM2_KEY;
+        case R.styleable.Keyboard_iconNum3Key:
+            return ICON_NUM3_KEY;
+        case R.styleable.Keyboard_iconNum4Key:
+            return ICON_NUM4_KEY;
+        case R.styleable.Keyboard_iconNum5Key:
+            return ICON_NUM5_KEY;
+        case R.styleable.Keyboard_iconNum6Key:
+            return ICON_NUM6_KEY;
+        case R.styleable.Keyboard_iconNum7Key:
+            return ICON_NUM7_KEY;
+        case R.styleable.Keyboard_iconNum8Key:
+            return ICON_NUM8_KEY;
+        case R.styleable.Keyboard_iconNum9Key:
+            return ICON_NUM9_KEY;
+        case R.styleable.Keyboard_iconNum0Key:
+            return ICON_NUM0_KEY;
+        case R.styleable.Keyboard_iconShiftedShiftKey:
+            return ICON_SHIFTED_SHIFT_KEY;
+        case R.styleable.Keyboard_iconPreviewSpaceKey:
+            return ICON_PREVIEW_SPACE_KEY;
+        case R.styleable.Keyboard_iconPreviewTabKey:
+            return ICON_PREVIEW_TAB_KEY;
+        case R.styleable.Keyboard_iconPreviewSettingsKey:
+            return ICON_PREVIEW_SETTINGS_KEY;
+        case R.styleable.Keyboard_iconPreviewShortcutKey:
+            return ICON_PREVIEW_SHORTCUT_KEY;
+        default:
+            return ICON_UNDEFINED;
+        }
+    }
+
+    public void loadIcons(TypedArray keyboardAttrs) {
+        final int count = keyboardAttrs.getIndexCount();
+        for (int i = 0; i < count; i++) {
+            final int attrIndex = keyboardAttrs.getIndex(i);
+            final int iconId = getIconId(attrIndex);
+            if (iconId != ICON_UNDEFINED) {
+                try {
+                    final Drawable icon = keyboardAttrs.getDrawable(attrIndex);
+                    Keyboard.setDefaultBounds(icon);
+                    mIcons[iconId] = icon;
+                } catch (Resources.NotFoundException e) {
+                    Log.w(TAG, "Drawable resource for icon #" + iconId + " not found");
+                }
+            }
+        }
+    }
+
+    public Drawable getIcon(int iconId) {
+        if (iconId == ICON_UNDEFINED)
+            return null;
+        if (iconId < 0 || iconId >= mIcons.length)
+            throw new IllegalArgumentException("icon id is out of range: " + iconId);
+        return mIcons[iconId];
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
similarity index 79%
rename from java/src/com/android/inputmethod/keyboard/KeyboardParser.java
rename to java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
index b240f6d..8954eec 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
@@ -14,13 +14,9 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
+package com.android.inputmethod.keyboard.internal;
 
-import com.android.inputmethod.latin.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
+import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -29,6 +25,15 @@
 import android.util.Xml;
 import android.view.InflateException;
 
+import com.android.inputmethod.compat.EditorInfoCompatUtils;
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.latin.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
@@ -86,14 +91,14 @@
  * You can declare Key style and specify styles within Key tags.
  * <pre>
  *     &gt;switch&lt;
- *       &gt;case colorScheme="white"&lt;
- *         &gt;key-style styleName="shift-key" parentStyle="modifier-key"
- *           keyIcon="@drawable/sym_keyboard_shift"
+ *       &gt;case mode="email"&lt;
+ *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
+ *           keyLabel=".com"
  *         /&lt;
  *       &gt;/case&lt;
- *       &gt;case colorScheme="black"&lt;
- *         &gt;key-style styleName="shift-key" parentStyle="modifier-key"
- *           keyIcon="@drawable/sym_bkeyboard_shift"
+ *       &gt;case mode="url"&lt;
+ *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
+ *           keyLabel="http://"
  *         /&lt;
  *       &gt;/case&lt;
  *     &gt;/switch&lt;
@@ -119,8 +124,12 @@
     public static final String TAG_KEY_STYLE = "key-style";
 
     private final Keyboard mKeyboard;
+    private final Context mContext;
     private final Resources mResources;
 
+    private int mKeyboardTopPadding;
+    private int mKeyboardBottomPadding;
+    private int mHorizontalEdgesPadding;
     private int mCurrentX = 0;
     private int mCurrentY = 0;
     private int mMaxRowWidth = 0;
@@ -128,9 +137,12 @@
     private Row mCurrentRow = null;
     private final KeyStyles mKeyStyles = new KeyStyles();
 
-    public KeyboardParser(Keyboard keyboard, Resources res) {
+    public KeyboardParser(Keyboard keyboard, Context context) {
         mKeyboard = keyboard;
+        mContext = context;
+        final Resources res = context.getResources();
         mResources = res;
+        mHorizontalEdgesPadding = (int)res.getDimension(R.dimen.keyboard_horizontal_edges_padding);
     }
 
     public int getMaxRowWidth() {
@@ -150,6 +162,7 @@
                 final String tag = parser.getName();
                 if (TAG_KEYBOARD.equals(tag)) {
                     parseKeyboardAttributes(parser);
+                    startKeyboard();
                     parseKeyboardContent(parser, mKeyboard.getKeys());
                     break;
                 } else {
@@ -159,10 +172,32 @@
         }
     }
 
+    public static String parseKeyboardLocale(
+            Context context, int resId) throws XmlPullParserException, IOException {
+        final Resources res = context.getResources();
+        final XmlResourceParser parser = res.getXml(resId);
+        if (parser == null) return "";
+        int event;
+        while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            if (event == XmlPullParser.START_TAG) {
+                final String tag = parser.getName();
+                if (TAG_KEYBOARD.equals(tag)) {
+                    final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
+                            R.styleable.Keyboard);
+                    return keyboardAttr.getString(R.styleable.Keyboard_keyboardLocale);
+                } else {
+                    throw new IllegalStartTag(parser, TAG_KEYBOARD);
+                }
+            }
+        }
+        return "";
+    }
+
     private void parseKeyboardAttributes(XmlResourceParser parser) {
         final Keyboard keyboard = mKeyboard;
-        final TypedArray keyboardAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.Keyboard);
+        final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
+                Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
+                R.style.Keyboard);
         final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard_Key);
         try {
@@ -170,9 +205,20 @@
             final int keyboardHeight = (int)keyboardAttr.getDimension(
                     R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
             final int maxKeyboardHeight = getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_maxKeyboardHeight, displayHeight,  displayHeight / 2);
-            // Keyboard height will not exceed maxKeyboardHeight.
-            final int height = Math.min(keyboardHeight, maxKeyboardHeight);
+                    R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2);
+            int minKeyboardHeight = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2);
+            if (minKeyboardHeight < 0) {
+                // Specified fraction was negative, so it should be calculated against display
+                // width.
+                final int displayWidth = keyboard.getDisplayWidth();
+                minKeyboardHeight = -getDimensionOrFraction(keyboardAttr,
+                        R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2);
+            }
+            // Keyboard height will not exceed maxKeyboardHeight and will not be less than
+            // minKeyboardHeight.
+            final int height = Math.max(
+                    Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
             final int width = keyboard.getDisplayWidth();
 
             keyboard.setKeyboardHeight(height);
@@ -189,6 +235,12 @@
 
             keyboard.setMaxPopupKeyboardColumn(keyAttr.getInt(
                     R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5));
+
+            mKeyboard.mIconsSet.loadIcons(keyboardAttr);
+            mKeyboardTopPadding = keyboardAttr.getDimensionPixelSize(
+                    R.styleable.Keyboard_keyboardTopPadding, 0);
+            mKeyboardBottomPadding = keyboardAttr.getDimensionPixelSize(
+                    R.styleable.Keyboard_keyboardBottomPadding, 0);
         } finally {
             keyAttr.recycle();
             keyboardAttr.recycle();
@@ -280,7 +332,7 @@
         } else {
             Key key = new Key(mResources, row, mCurrentX, mCurrentY, parser, mKeyStyles);
             if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d popupCharacters=%s />",
-                    TAG_KEY, (key.mEnabled ? "" : " disabled"), key.mLabel, key.mCode,
+                    TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode,
                     Arrays.toString(key.mPopupCharacters)));
             checkEndTag(TAG_KEY, parser);
             keys.add(key);
@@ -430,56 +482,45 @@
         final TypedArray viewAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.KeyboardView);
         try {
-            final boolean modeMatched = matchInteger(a,
-                    R.styleable.Keyboard_Case_mode, id.mMode);
-            final boolean webInputMatched = matchBoolean(a,
-                    R.styleable.Keyboard_Case_webInput, id.mWebInput);
+            final boolean modeMatched = matchTypedValue(a,
+                    R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
+            final boolean navigateActionMatched = matchBoolean(a,
+                    R.styleable.Keyboard_Case_navigateAction, id.mNavigateAction);
             final boolean passwordInputMatched = matchBoolean(a,
                     R.styleable.Keyboard_Case_passwordInput, id.mPasswordInput);
             final boolean settingsKeyMatched = matchBoolean(a,
                     R.styleable.Keyboard_Case_hasSettingsKey, id.mHasSettingsKey);
-            final boolean f2KeyModeMatched = matchInteger(a,
-                    R.styleable.Keyboard_Case_f2KeyMode, id.mF2KeyMode);
-            final boolean clobberSettingsKeyMatched = matchBoolean(a,
-                    R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
             final boolean voiceEnabledMatched = matchBoolean(a,
                     R.styleable.Keyboard_Case_voiceKeyEnabled, id.mVoiceKeyEnabled);
             final boolean voiceKeyMatched = matchBoolean(a,
                     R.styleable.Keyboard_Case_hasVoiceKey, id.mHasVoiceKey);
-            final boolean colorSchemeMatched = matchInteger(viewAttr,
-                    R.styleable.KeyboardView_colorScheme, id.mColorScheme);
             // As noted at {@link KeyboardId} class, we are interested only in enum value masked by
             // {@link android.view.inputmethod.EditorInfo#IME_MASK_ACTION} and
             // {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. So matching
             // this attribute with id.mImeOptions as integer value is enough for our purpose.
             final boolean imeActionMatched = matchInteger(a,
                     R.styleable.Keyboard_Case_imeAction, id.mImeAction);
+            final boolean localeCodeMatched = matchString(a,
+                    R.styleable.Keyboard_Case_localeCode, id.mLocale.toString());
             final boolean languageCodeMatched = matchString(a,
                     R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
             final boolean countryCodeMatched = matchString(a,
                     R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
-            final boolean selected = modeMatched && webInputMatched && passwordInputMatched
-                    && settingsKeyMatched && f2KeyModeMatched && clobberSettingsKeyMatched
-                    && voiceEnabledMatched && voiceKeyMatched && colorSchemeMatched
-                    && imeActionMatched && languageCodeMatched && countryCodeMatched;
+            final boolean selected = modeMatched && navigateActionMatched && passwordInputMatched
+                    && settingsKeyMatched && voiceEnabledMatched && voiceKeyMatched
+                    && imeActionMatched && localeCodeMatched && languageCodeMatched
+                    && countryCodeMatched;
 
-            if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE,
-                    textAttr(KeyboardId.modeName(
-                            a.getInt(R.styleable.Keyboard_Case_mode, -1)), "mode"),
-                    textAttr(KeyboardId.colorSchemeName(
-                            viewAttr.getInt(
-                                    R.styleable.KeyboardView_colorScheme, -1)), "colorSchemeName"),
-                    booleanAttr(a, R.styleable.Keyboard_Case_webInput, "webInput"),
+            if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE,
+                    textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
+                    booleanAttr(a, R.styleable.Keyboard_Case_navigateAction, "navigateAction"),
                     booleanAttr(a, R.styleable.Keyboard_Case_passwordInput, "passwordInput"),
                     booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, "hasSettingsKey"),
-                    textAttr(KeyboardId.f2KeyModeName(
-                            a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)), "f2KeyMode"),
-                    booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
-                            "clobberSettingsKey"),
                     booleanAttr(a, R.styleable.Keyboard_Case_voiceKeyEnabled, "voiceKeyEnabled"),
                     booleanAttr(a, R.styleable.Keyboard_Case_hasVoiceKey, "hasVoiceKey"),
-                    textAttr(KeyboardId.imeOptionsName(
+                    textAttr(EditorInfoCompatUtils.imeOptionsName(
                             a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"),
+                    textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"),
                     textAttr(a.getString(R.styleable.Keyboard_Case_languageCode), "languageCode"),
                     textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), "countryCode"),
                     Boolean.toString(selected)));
@@ -506,7 +547,30 @@
     private static boolean matchString(TypedArray a, int index, String value) {
         // If <case> does not have "index" attribute, that means this <case> is wild-card for the
         // attribute.
-        return !a.hasValue(index) || a.getString(index).equals(value);
+        return !a.hasValue(index) || stringArrayContains(a.getString(index).split("\\|"), value);
+    }
+
+    private static boolean matchTypedValue(TypedArray a, int index, int intValue, String strValue) {
+        // If <case> does not have "index" attribute, that means this <case> is wild-card for the
+        // attribute.
+        final TypedValue v = a.peekValue(index);
+        if (v == null)
+            return true;
+
+        if (isIntegerValue(v)) {
+            return intValue == a.getInt(index, 0);
+        } else if (isStringValue(v)) {
+            return stringArrayContains(a.getString(index).split("\\|"), strValue);
+        }
+        return false;
+    }
+
+    private static boolean stringArrayContains(String[] array, String value) {
+        for (final String elem : array) {
+            if (elem.equals(value))
+                return true;
+        }
+        return false;
     }
 
     private boolean parseDefault(XmlResourceParser parser, Row row, List<Key> keys)
@@ -544,25 +608,32 @@
         throw new NonEmptyTag(tag, parser);
     }
 
+    private void startKeyboard() {
+        mCurrentY += mKeyboardTopPadding;
+    }
+
     private void startRow(Row row) {
         mCurrentX = 0;
+        setSpacer(mCurrentX, mHorizontalEdgesPadding);
         mCurrentRow = row;
     }
 
     private void endRow() {
         if (mCurrentRow == null)
             throw new InflateException("orphant end row tag");
+        setSpacer(mCurrentX, mHorizontalEdgesPadding);
+        if (mCurrentX > mMaxRowWidth)
+            mMaxRowWidth = mCurrentX;
         mCurrentY += mCurrentRow.mDefaultHeight;
         mCurrentRow = null;
     }
 
     private void endKey(Key key) {
         mCurrentX = key.mX - key.mGap / 2 + key.mWidth + key.mGap;
-        if (mCurrentX > mMaxRowWidth)
-            mMaxRowWidth = mCurrentX;
     }
 
     private void endKeyboard(int defaultVerticalGap) {
+        mCurrentY += mKeyboardBottomPadding;
         mTotalHeight = mCurrentY - defaultVerticalGap;
     }
 
@@ -574,15 +645,34 @@
         final TypedValue value = a.peekValue(index);
         if (value == null)
             return defValue;
-        if (value.type == TypedValue.TYPE_DIMENSION) {
-            return a.getDimensionPixelOffset(index, defValue);
-        } else if (value.type == TypedValue.TYPE_FRACTION) {
+        if (isFractionValue(value)) {
             // Round it to avoid values like 47.9999 from getting truncated
             return Math.round(a.getFraction(index, base, base, defValue));
+        } else if (isDimensionValue(value)) {
+            return a.getDimensionPixelOffset(index, defValue);
+        } else if (isIntegerValue(value)) {
+            // For enum value.
+            return a.getInt(index, defValue);
         }
         return defValue;
     }
 
+    private static boolean isFractionValue(TypedValue v) {
+        return v.type == TypedValue.TYPE_FRACTION;
+    }
+
+    private static boolean isDimensionValue(TypedValue v) {
+        return v.type == TypedValue.TYPE_DIMENSION;
+    }
+
+    private static boolean isIntegerValue(TypedValue v) {
+        return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
+    }
+
+    private static boolean isStringValue(TypedValue v) {
+        return v.type == TypedValue.TYPE_STRING;
+    }
+
     @SuppressWarnings("serial")
     public static class ParseException extends InflateException {
         public ParseException(String msg, XmlResourceParser parser) {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardShiftState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardShiftState.java
similarity index 97%
rename from java/src/com/android/inputmethod/keyboard/KeyboardShiftState.java
rename to java/src/com/android/inputmethod/keyboard/internal/KeyboardShiftState.java
index e015b51..0cde4e5 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardShiftState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardShiftState.java
@@ -14,10 +14,12 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
+package com.android.inputmethod.keyboard.internal;
 
 import android.util.Log;
 
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+
 public class KeyboardShiftState {
     private static final String TAG = "KeyboardShiftState";
     private static final boolean DEBUG = KeyboardSwitcher.DEBUG_STATE;
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
similarity index 96%
rename from java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java
rename to java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
index 765750f..040c16d 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
@@ -14,15 +14,19 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
-
-import com.android.inputmethod.latin.R;
+package com.android.inputmethod.keyboard.internal;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Paint;
 import android.graphics.Rect;
 
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.MiniKeyboard;
+import com.android.inputmethod.latin.R;
+
 import java.util.List;
 
 public class MiniKeyboardBuilder {
@@ -185,7 +189,8 @@
             Keyboard parentKeyboard) {
         final Context context = view.getContext();
         mRes = context.getResources();
-        final MiniKeyboard keyboard = new MiniKeyboard(context, layoutTemplateResId, null);
+        final MiniKeyboard keyboard = new MiniKeyboard(
+                context, layoutTemplateResId, parentKeyboard);
         mKeyboard = keyboard;
         mPopupCharacters = parentKey.mPopupCharacters;
 
diff --git a/java/src/com/android/inputmethod/keyboard/ModifierKeyState.java b/java/src/com/android/inputmethod/keyboard/internal/ModifierKeyState.java
similarity index 95%
rename from java/src/com/android/inputmethod/keyboard/ModifierKeyState.java
rename to java/src/com/android/inputmethod/keyboard/internal/ModifierKeyState.java
index ebbc79a..dae73c4 100644
--- a/java/src/com/android/inputmethod/keyboard/ModifierKeyState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/ModifierKeyState.java
@@ -14,10 +14,12 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
+package com.android.inputmethod.keyboard.internal;
 
 import android.util.Log;
 
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+
 public class ModifierKeyState {
     protected static final String TAG = "ModifierKeyState";
     protected static final boolean DEBUG = KeyboardSwitcher.DEBUG_STATE;
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerKeyState.java
similarity index 90%
rename from java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java
rename to java/src/com/android/inputmethod/keyboard/internal/PointerTrackerKeyState.java
index 64ba80a..ddadb13 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerKeyState.java
@@ -14,12 +14,15 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
+package com.android.inputmethod.keyboard.internal;
+
+import com.android.inputmethod.keyboard.KeyDetector;
+import com.android.inputmethod.keyboard.PointerTracker;
 
 /**
  * This class keeps track of a key index and a position where {@link PointerTracker} is.
  */
-/* package */ class PointerTrackerKeyState {
+public class PointerTrackerKeyState {
     private final KeyDetector mKeyDetector;
 
     // The position and time at which first down event occurred.
@@ -92,6 +95,7 @@
 
     public int onUpKey(int x, int y, long eventTime) {
         mUpTime = eventTime;
+        mKeyIndex = KeyDetector.NOT_A_KEY;
         return onMoveKeyInternal(x, y);
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
similarity index 82%
rename from java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java
rename to java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
index 01d9b5d..f87cd86 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -14,7 +14,9 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
+package com.android.inputmethod.keyboard.internal;
+
+import com.android.inputmethod.keyboard.PointerTracker;
 
 import java.util.LinkedList;
 
@@ -29,29 +31,28 @@
         if (mQueue.lastIndexOf(tracker) < 0) {
             return;
         }
-        LinkedList<PointerTracker> queue = mQueue;
+        final LinkedList<PointerTracker> queue = mQueue;
         int oldestPos = 0;
         for (PointerTracker t = queue.get(oldestPos); t != tracker; t = queue.get(oldestPos)) {
             if (t.isModifier()) {
                 oldestPos++;
             } else {
-                t.onUpEventForRelease(t.getLastX(), t.getLastY(), eventTime);
-                t.setAlreadyProcessed();
+                t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime, true);
                 queue.remove(oldestPos);
             }
         }
     }
 
     public void releaseAllPointers(long eventTime) {
-        releaseAllPointersExcept(null, eventTime);
+        releaseAllPointersExcept(null, eventTime, true);
     }
 
-    public void releaseAllPointersExcept(PointerTracker tracker, long eventTime) {
+    public void releaseAllPointersExcept(PointerTracker tracker, long eventTime,
+            boolean updateReleasedKeyGraphics) {
         for (PointerTracker t : mQueue) {
             if (t == tracker)
                 continue;
-            t.onUpEventForRelease(t.getLastX(), t.getLastY(), eventTime);
-            t.setAlreadyProcessed();
+            t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime, updateReleasedKeyGraphics);
         }
         mQueue.clear();
         if (tracker != null)
diff --git a/java/src/com/android/inputmethod/keyboard/PopupCharactersParser.java b/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
similarity index 88%
rename from java/src/com/android/inputmethod/keyboard/PopupCharactersParser.java
rename to java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
index ff78ee5..8276f5d 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupCharactersParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
@@ -14,13 +14,14 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
-
-import com.android.inputmethod.latin.R;
+package com.android.inputmethod.keyboard.internal;
 
 import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.R;
 
 /**
  * String parser of popupCharacters attribute of Key.
@@ -28,16 +29,19 @@
  * Each popup key text is one of the following:
  * - A single letter (Letter)
  * - Label optionally followed by keyOutputText or code (keyLabel|keyOutputText).
- * - Icon followed by keyOutputText or code (@drawable/icon|@integer/key_code)
+ * - Icon followed by keyOutputText or code (@icon/icon_number|@integer/key_code)
  * Special character, comma ',' backslash '\', and bar '|' can be escaped by '\'
  * character.
  * Note that the character '@' and '\' are also parsed by XML parser and CSV parser as well.
+ * See {@link KeyboardIconsSet} about icon_number.
  */
 public class PopupCharactersParser {
+    private static final String TAG = PopupCharactersParser.class.getSimpleName();
+
     private static final char ESCAPE = '\\';
     private static final String LABEL_END = "|";
     private static final String PREFIX_AT = "@";
-    private static final String PREFIX_ICON = PREFIX_AT + "drawable/";
+    private static final String PREFIX_ICON = PREFIX_AT + "icon/";
     private static final String PREFIX_CODE = PREFIX_AT + "integer/";
 
     private PopupCharactersParser() {
@@ -150,13 +154,18 @@
         return Keyboard.CODE_DUMMY;
     }
 
-    public static Drawable getIcon(Resources res, String popupSpec) {
+    public static int getIconId(String popupSpec) {
         if (hasIcon(popupSpec)) {
             int end = popupSpec.indexOf(LABEL_END, PREFIX_ICON.length() + 1);
-            int resId = getResourceId(res, popupSpec.substring(PREFIX_AT.length(), end));
-            return res.getDrawable(resId);
+            final String iconId = popupSpec.substring(PREFIX_ICON.length(), end);
+            try {
+                return Integer.valueOf(iconId);
+            } catch (NumberFormatException e) {
+                Log.w(TAG, "illegal icon id specified: " + iconId);
+                return KeyboardIconsSet.ICON_UNDEFINED;
+            }
         }
-        return null;
+        return KeyboardIconsSet.ICON_UNDEFINED;
     }
 
     private static int getResourceId(Resources res, String name) {
diff --git a/java/src/com/android/inputmethod/keyboard/Row.java b/java/src/com/android/inputmethod/keyboard/internal/Row.java
similarity index 96%
rename from java/src/com/android/inputmethod/keyboard/Row.java
rename to java/src/com/android/inputmethod/keyboard/internal/Row.java
index 40d7e14..06aadcc 100644
--- a/java/src/com/android/inputmethod/keyboard/Row.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/Row.java
@@ -14,15 +14,16 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
-
-import com.android.inputmethod.latin.R;
+package com.android.inputmethod.keyboard.internal;
 
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.util.Xml;
 
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.R;
+
 /**
  * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
  * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
diff --git a/java/src/com/android/inputmethod/keyboard/ShiftKeyState.java b/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java
similarity index 97%
rename from java/src/com/android/inputmethod/keyboard/ShiftKeyState.java
rename to java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java
index ba15624..6617b91 100644
--- a/java/src/com/android/inputmethod/keyboard/ShiftKeyState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
+package com.android.inputmethod.keyboard.internal;
 
 import android.util.Log;
 
diff --git a/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java b/java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java
similarity index 78%
rename from java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java
rename to java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java
index ad8b0d6..df4b575 100644
--- a/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java
@@ -14,40 +14,42 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
-
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SubtypeSwitcher;
+package com.android.inputmethod.keyboard.internal;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
-import android.graphics.PixelFormat;
 import android.graphics.Paint.Align;
+import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.text.TextPaint;
 import android.view.ViewConfiguration;
 
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.LatinKeyboard;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+
 /**
  * Animation to be displayed on the spacebar preview popup when switching languages by swiping the
  * spacebar. It draws the current, previous and next languages and moves them by the delta of touch
  * movement on the spacebar.
  */
 public class SlidingLocaleDrawable extends Drawable {
-
-    private final Context mContext;
-    private final Resources mRes;
+    private static final int SLIDE_SPEED_MULTIPLIER_RATIO = 150;
     private final int mWidth;
     private final int mHeight;
     private final Drawable mBackground;
+    private final int mSpacebarTextColor;
     private final TextPaint mTextPaint;
     private final int mMiddleX;
     private final Drawable mLeftDrawable;
     private final Drawable mRightDrawable;
     private final int mThreshold;
+
     private int mDiff;
     private boolean mHitThreshold;
     private String mCurrentLanguage;
@@ -55,42 +57,36 @@
     private String mPrevLanguage;
 
     public SlidingLocaleDrawable(Context context, Drawable background, int width, int height) {
-        mContext = context;
-        mRes = context.getResources();
         mBackground = background;
         Keyboard.setDefaultBounds(mBackground);
         mWidth = width;
         mHeight = height;
         final TextPaint textPaint = new TextPaint();
-        textPaint.setTextSize(getTextSizeFromTheme(android.R.style.TextAppearance_Medium, 18));
-        textPaint.setColor(R.color.latinkeyboard_transparent);
+        textPaint.setTextSize(LatinKeyboard.getTextSizeFromTheme(
+                context.getTheme(), android.R.style.TextAppearance_Medium, 18));
+        textPaint.setColor(Color.TRANSPARENT);
         textPaint.setTextAlign(Align.CENTER);
-        textPaint.setAlpha(LatinKeyboard.OPACITY_FULLY_OPAQUE);
         textPaint.setAntiAlias(true);
         mTextPaint = textPaint;
         mMiddleX = (mWidth - mBackground.getIntrinsicWidth()) / 2;
-        final Resources res = mRes;
-        mLeftDrawable = res.getDrawable(
-                R.drawable.sym_keyboard_feedback_language_arrows_left);
-        mRightDrawable = res.getDrawable(
-                R.drawable.sym_keyboard_feedback_language_arrows_right);
-        mThreshold = ViewConfiguration.get(mContext).getScaledTouchSlop();
+
+        final TypedArray a = context.obtainStyledAttributes(
+                null, R.styleable.LatinKeyboard, R.attr.latinKeyboardStyle, R.style.LatinKeyboard);
+        mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboard_spacebarTextColor, 0);
+        mLeftDrawable = a.getDrawable(R.styleable.LatinKeyboard_spacebarArrowPreviewLeftIcon);
+        mRightDrawable = a.getDrawable(R.styleable.LatinKeyboard_spacebarArrowPreviewRightIcon);
+        a.recycle();
+
+        mThreshold = ViewConfiguration.get(context).getScaledTouchSlop();
     }
 
-    private int getTextSizeFromTheme(int style, int defValue) {
-        TypedArray array = mContext.getTheme().obtainStyledAttributes(
-                style, new int[] { android.R.attr.textSize });
-        int textSize = array.getDimensionPixelSize(array.getResourceId(0, 0), defValue);
-        return textSize;
-    }
-
-    void setDiff(int diff) {
+    public void setDiff(int diff) {
         if (diff == Integer.MAX_VALUE) {
             mHitThreshold = false;
             mCurrentLanguage = null;
             return;
         }
-        mDiff = diff;
+        mDiff = Math.max(diff, diff * SLIDE_SPEED_MULTIPLIER_RATIO / 100);
         if (mDiff > mWidth) mDiff = mWidth;
         if (mDiff < -mWidth) mDiff = -mWidth;
         if (Math.abs(mDiff) > mThreshold) mHitThreshold = true;
@@ -118,7 +114,7 @@
             // Draw language text with shadow
             final float baseline = mHeight * LatinKeyboard.SPACEBAR_LANGUAGE_BASELINE
                     - paint.descent();
-            paint.setColor(mRes.getColor(R.color.latinkeyboard_feedback_language_text));
+            paint.setColor(mSpacebarTextColor);
             canvas.drawText(mCurrentLanguage, width / 2 + diff, baseline, paint);
             canvas.drawText(mNextLanguage, diff - width / 2, baseline, paint);
             canvas.drawText(mPrevLanguage, diff + width + width / 2, baseline, paint);
diff --git a/java/src/com/android/inputmethod/keyboard/SwipeTracker.java b/java/src/com/android/inputmethod/keyboard/internal/SwipeTracker.java
similarity index 98%
rename from java/src/com/android/inputmethod/keyboard/SwipeTracker.java
rename to java/src/com/android/inputmethod/keyboard/internal/SwipeTracker.java
index 975b13b..8d192c2 100644
--- a/java/src/com/android/inputmethod/keyboard/SwipeTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/SwipeTracker.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
+package com.android.inputmethod.keyboard.internal;
 
 import android.view.MotionEvent;
 
diff --git a/java/src/com/android/inputmethod/latin/AccessibilityUtils.java b/java/src/com/android/inputmethod/latin/AccessibilityUtils.java
deleted file mode 100644
index cd3f9e0..0000000
--- a/java/src/com/android/inputmethod/latin/AccessibilityUtils.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * 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.content.SharedPreferences;
-import android.content.res.TypedArray;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Utility functions for accessibility support.
- */
-public class AccessibilityUtils {
-    /** Shared singleton instance. */
-    private static final AccessibilityUtils sInstance = new AccessibilityUtils();
-    private /* final */ LatinIME mService;
-    private /* final */ AccessibilityManager mAccessibilityManager;
-    private /* final */ Map<Integer, CharSequence> mDescriptions;
-
-    /**
-     * Returns a shared instance of AccessibilityUtils.
-     *
-     * @return A shared instance of AccessibilityUtils.
-     */
-    public static AccessibilityUtils getInstance() {
-        return sInstance;
-    }
-
-    /**
-     * Initializes (or re-initializes) the shared instance of AccessibilityUtils
-     * with the specified parent service and preferences.
-     *
-     * @param service The parent input method service.
-     * @param prefs The parent preferences.
-     */
-    public static void init(LatinIME service, SharedPreferences prefs) {
-        sInstance.initialize(service, prefs);
-    }
-
-    private AccessibilityUtils() {
-        // This class is not publicly instantiable.
-    }
-
-    /**
-     * Initializes (or re-initializes) with the specified parent service and
-     * preferences.
-     *
-     * @param service The parent input method service.
-     * @param prefs The parent preferences.
-     */
-    private void initialize(LatinIME service, SharedPreferences prefs) {
-        mService = service;
-        mAccessibilityManager = (AccessibilityManager) service.getSystemService(
-                Context.ACCESSIBILITY_SERVICE);
-        mDescriptions = null;
-    }
-
-    /**
-     * Returns true if accessibility is enabled.
-     *
-     * @return {@code true} if accessibility is enabled.
-     */
-    public boolean isAccessibilityEnabled() {
-        return mAccessibilityManager.isEnabled();
-    }
-
-    /**
-     * Speaks a key's action after it has been released. Does not speak letter
-     * keys since typed keys are already spoken aloud by TalkBack.
-     * <p>
-     * No-op if accessibility is not enabled.
-     * </p>
-     *
-     * @param primaryCode The primary code of the released key.
-     * @param switcher The input method's {@link KeyboardSwitcher}.
-     */
-    public void onRelease(int primaryCode, KeyboardSwitcher switcher) {
-        if (!isAccessibilityEnabled()) {
-            return;
-        }
-
-        int resId = -1;
-
-        switch (primaryCode) {
-            case Keyboard.CODE_SHIFT: {
-                if (switcher.isShiftedOrShiftLocked()) {
-                    resId = R.string.description_shift_on;
-                } else {
-                    resId = R.string.description_shift_off;
-                }
-                break;
-            }
-
-            case Keyboard.CODE_SWITCH_ALPHA_SYMBOL: {
-                if (switcher.isAlphabetMode()) {
-                    resId = R.string.description_symbols_off;
-                } else {
-                    resId = R.string.description_symbols_on;
-                }
-                break;
-            }
-        }
-
-        if (resId >= 0) {
-            speakDescription(mService.getResources().getText(resId));
-        }
-    }
-
-    /**
-     * Speaks a key's description for accessibility. If a key has an explicit
-     * description defined in keycodes.xml, that will be used. Otherwise, if the
-     * key is a Unicode character, then its character will be used.
-     * <p>
-     * No-op if accessibility is not enabled.
-     * </p>
-     *
-     * @param primaryCode The primary code of the pressed key.
-     * @param switcher The input method's {@link KeyboardSwitcher}.
-     */
-    public void onPress(int primaryCode, KeyboardSwitcher switcher) {
-        if (!isAccessibilityEnabled()) {
-            return;
-        }
-
-        // TODO Use the current keyboard state to read "Switch to symbols"
-        // instead of just "Symbols" (and similar for shift key).
-        CharSequence description = describeKey(primaryCode);
-        if (description == null && Character.isDefined((char) primaryCode)) {
-            description = Character.toString((char) primaryCode);
-        }
-
-        if (description != null) {
-            speakDescription(description);
-        }
-    }
-
-    /**
-     * Returns a text description for a given key code. If the key does not have
-     * an explicit description, returns <code>null</code>.
-     *
-     * @param keyCode An integer key code.
-     * @return A {@link CharSequence} describing the key or <code>null</code> if
-     *         no description is available.
-     */
-    private CharSequence describeKey(int keyCode) {
-        // If not loaded yet, load key descriptions from XML file.
-        if (mDescriptions == null) {
-            mDescriptions = loadDescriptions();
-        }
-
-        return mDescriptions.get(keyCode);
-    }
-
-    /**
-     * Loads key descriptions from resources.
-     */
-    private Map<Integer, CharSequence> loadDescriptions() {
-        final Map<Integer, CharSequence> descriptions = new HashMap<Integer, CharSequence>();
-        final TypedArray array = mService.getResources().obtainTypedArray(R.array.key_descriptions);
-
-        // Key descriptions are stored as a key code followed by a string.
-        for (int i = 0; i < array.length() - 1; i += 2) {
-            int code = array.getInteger(i, 0);
-            CharSequence desc = array.getText(i + 1);
-
-            descriptions.put(code, desc);
-        }
-
-        array.recycle();
-
-        return descriptions;
-    }
-
-    /**
-     * Sends a character sequence to be read aloud.
-     *
-     * @param description The {@link CharSequence} to be read aloud.
-     */
-    private void speakDescription(CharSequence description) {
-        // TODO We need to add an AccessibilityEvent type for IMEs.
-        final AccessibilityEvent event = AccessibilityEvent.obtain(
-                AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
-        event.setPackageName(mService.getPackageName());
-        event.setClassName(getClass().getName());
-        event.setAddedCount(description.length());
-        event.getText().add(description);
-
-        mAccessibilityManager.sendAccessibilityEvent(event);
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/AssetFileAddress.java b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
new file mode 100644
index 0000000..074ecac
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
@@ -0,0 +1,52 @@
+/*
+ * 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 java.io.File;
+
+/**
+ * Immutable class to hold the address of an asset.
+ * As opposed to a normal file, an asset is usually represented as a contiguous byte array in
+ * the package file. Open it correctly thus requires the name of the package it is in, but
+ * also the offset in the file and the length of this data. This class encapsulates these three.
+ */
+class AssetFileAddress {
+    public final String mFilename;
+    public final long mOffset;
+    public final long mLength;
+
+    public AssetFileAddress(final String filename, final long offset, final long length) {
+        mFilename = filename;
+        mOffset = offset;
+        mLength = length;
+    }
+
+    public static AssetFileAddress makeFromFileName(final String filename) {
+        if (null == filename) return null;
+        File f = new File(filename);
+        if (null == f || !f.isFile()) return null;
+        return new AssetFileAddress(filename, 0l, f.length());
+    }
+
+    public static AssetFileAddress makeFromFileNameAndOffset(final String filename,
+            final long offset, final long length) {
+        if (null == filename) return null;
+        File f = new File(filename);
+        if (null == f || !f.isFile()) return null;
+        return new AssetFileAddress(filename, offset, length);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index 092f7ad..d311979 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -48,7 +48,7 @@
     }
 
     public void updateAutoCorrectionStatus(Map<String, Dictionary> dictionaries,
-            WordComposer wordComposer, ArrayList<CharSequence> suggestions, int[] priorities,
+            WordComposer wordComposer, ArrayList<CharSequence> suggestions, int[] sortedScores,
             CharSequence typedWord, double autoCorrectionThreshold, int correctionMode,
             CharSequence quickFixedWord, CharSequence whitelistedWord) {
         if (hasAutoCorrectionForWhitelistedWord(whitelistedWord)) {
@@ -62,7 +62,7 @@
             mHasAutoCorrection = true;
             mAutoCorrectionWord = quickFixedWord;
         } else if (hasAutoCorrectionForBinaryDictionary(wordComposer, suggestions, correctionMode,
-                priorities, typedWord, autoCorrectionThreshold)) {
+                sortedScores, typedWord, autoCorrectionThreshold)) {
             mHasAutoCorrection = true;
             mAutoCorrectionWord = suggestions.get(0);
         }
@@ -114,13 +114,13 @@
     }
 
     private boolean hasAutoCorrectionForBinaryDictionary(WordComposer wordComposer,
-            ArrayList<CharSequence> suggestions, int correctionMode, int[] priorities,
+            ArrayList<CharSequence> suggestions, int correctionMode, int[] sortedScores,
             CharSequence typedWord, double autoCorrectionThreshold) {
         if (wordComposer.size() > 1 && (correctionMode == Suggest.CORRECTION_FULL
                 || correctionMode == Suggest.CORRECTION_FULL_BIGRAM)
-                && typedWord != null && suggestions.size() > 0 && priorities.length > 0) {
+                && typedWord != null && suggestions.size() > 0 && sortedScores.length > 0) {
             final CharSequence autoCorrectionCandidate = suggestions.get(0);
-            final int autoCorrectionCandidateScore = priorities[0];
+            final int autoCorrectionCandidateScore = sortedScores[0];
             // TODO: when the normalized score of the first suggestion is nearly equals to
             //       the normalized score of the second suggestion, behave less aggressive.
             mNormalizedScore = Utils.calcNormalizedScore(
diff --git a/java/src/com/android/inputmethod/latin/AutoDictionary.java b/java/src/com/android/inputmethod/latin/AutoDictionary.java
index 54c6f30..460930f 100644
--- a/java/src/com/android/inputmethod/latin/AutoDictionary.java
+++ b/java/src/com/android/inputmethod/latin/AutoDictionary.java
@@ -27,7 +27,6 @@
 import android.util.Log;
 
 import java.util.HashMap;
-import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 
@@ -42,13 +41,8 @@
     static final int FREQUENCY_FOR_PICKED = 3;
     // Weight added to a user typing a new word that doesn't get corrected (or is reverted)
     static final int FREQUENCY_FOR_TYPED = 1;
-    // A word that is frequently typed and gets promoted to the user dictionary, uses this
-    // frequency.
-    static final int FREQUENCY_FOR_AUTO_ADD = 250;
     // If the user touches a typed word 2 times or more, it will become valid.
     private static final int VALIDITY_THRESHOLD = 2 * FREQUENCY_FOR_PICKED;
-    // If the user touches a typed word 4 times or more, it will be added to the user dict.
-    private static final int PROMOTION_THRESHOLD = 4 * FREQUENCY_FOR_PICKED;
 
     private LatinIME mIme;
     // Locale for which this auto dictionary is storing words
@@ -152,11 +146,6 @@
         freq = freq < 0 ? addFrequency : freq + addFrequency;
         super.addWord(word, freq);
 
-        if (freq >= PROMOTION_THRESHOLD) {
-            mIme.promoteToUserDictionary(word, FREQUENCY_FOR_AUTO_ADD);
-            freq = 0;
-        }
-
         synchronized (mPendingWritesLock) {
             // Write a null frequency if it is to be deleted from the db
             mPendingWrites.put(word, freq == 0 ? null : new Integer(freq));
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 08ddd25..9748d60 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -21,10 +21,7 @@
 import com.android.inputmethod.keyboard.ProximityInfo;
 
 import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.util.Log;
 
-import java.io.File;
 import java.util.Arrays;
 
 /**
@@ -32,8 +29,11 @@
  */
 public class BinaryDictionary extends Dictionary {
 
+    public static final String DICTIONARY_PACK_AUTHORITY =
+            "com.android.inputmethod.latin.dictionarypack";
+
     /**
-     * There is difference between what java and native code can handle.
+     * There is a difference between what java and native code can handle.
      * This value should only be used in BinaryDictionary.java
      * It is necessary to keep it at this value because some languages e.g. German have
      * really long words.
@@ -41,101 +41,59 @@
     public static final int MAX_WORD_LENGTH = 48;
     public static final int MAX_WORDS = 18;
 
+    @SuppressWarnings("unused")
     private static final String TAG = "BinaryDictionary";
     private static final int MAX_PROXIMITY_CHARS_SIZE = ProximityInfo.MAX_PROXIMITY_CHARS_SIZE;
     private static final int MAX_BIGRAMS = 60;
 
     private static final int TYPED_LETTER_MULTIPLIER = 2;
 
-    private static final BinaryDictionary sInstance = new BinaryDictionary();
     private int mDicTypeId;
     private int mNativeDict;
-    private long mDictLength;
     private final int[] mInputCodes = new int[MAX_WORD_LENGTH * MAX_PROXIMITY_CHARS_SIZE];
     private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS];
     private final char[] mOutputChars_bigrams = new char[MAX_WORD_LENGTH * MAX_BIGRAMS];
-    private final int[] mFrequencies = new int[MAX_WORDS];
-    private final int[] mFrequencies_bigrams = new int[MAX_BIGRAMS];
+    private final int[] mScores = new int[MAX_WORDS];
+    private final int[] mBigramScores = new int[MAX_BIGRAMS];
 
     private final KeyboardSwitcher mKeyboardSwitcher = KeyboardSwitcher.getInstance();
-    private final SubtypeSwitcher mSubtypeSwitcher = SubtypeSwitcher.getInstance();
 
-    private static class Flags {
-        private static class FlagEntry {
-            public final String mName;
-            public final int mValue;
-            public FlagEntry(String name, int value) {
-                mName = name;
-                mValue = value;
-            }
-        }
-        public static final FlagEntry[] ALL_FLAGS = {
-            // Here should reside all flags that trigger some special processing
-            // These *must* match the definition in UnigramDictionary enum in
-            // unigram_dictionary.h so please update both at the same time.
-            new FlagEntry("requiresGermanUmlautProcessing", 0x1)
-        };
-    }
+    public static final Flag FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING =
+            new Flag(R.bool.config_require_umlaut_processing, 0x1);
+
+    // Can create a new flag from extravalue :
+    // public static final Flag FLAG_MYFLAG =
+    //         new Flag("my_flag", 0x02);
+
+    private static final Flag[] ALL_FLAGS = {
+        // Here should reside all flags that trigger some special processing
+        // These *must* match the definition in UnigramDictionary enum in
+        // unigram_dictionary.h so please update both at the same time.
+        FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING,
+    };
+
     private int mFlags = 0;
 
-    private BinaryDictionary() {
-    }
-
     /**
-     * Initialize a dictionary from a raw resource file
-     * @param context application context for reading resources
-     * @param resId the resource containing the raw binary dictionary
-     * @return initialized instance of BinaryDictionary
+     * Constructor for the binary dictionary. This is supposed to be called from the
+     * dictionary factory.
+     * All implementations should pass null into flagArray, except for testing purposes.
+     * @param context the context to access the environment from.
+     * @param filename the name of the file to read through native code.
+     * @param offset the offset of the dictionary data within the file.
+     * @param length the length of the binary data.
+     * @param flagArray the flags to limit the dictionary to, or null for default.
      */
-    public static BinaryDictionary initDictionary(Context context, int resId, int dicTypeId) {
-        synchronized (sInstance) {
-            sInstance.closeInternal();
-            try {
-                final AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
-                if (afd == null) {
-                    Log.e(TAG, "Found the resource but it is compressed. resId=" + resId);
-                    return null;
-                }
-                final String sourceDir = context.getApplicationInfo().sourceDir;
-                final File packagePath = new File(sourceDir);
-                // TODO: Come up with a way to handle a directory.
-                if (!packagePath.isFile()) {
-                    Log.e(TAG, "sourceDir is not a file: " + sourceDir);
-                    return null;
-                }
-                sInstance.loadDictionary(sourceDir, afd.getStartOffset(), afd.getLength());
-                sInstance.mDicTypeId = dicTypeId;
-            } catch (android.content.res.Resources.NotFoundException e) {
-                Log.e(TAG, "Could not find the resource. resId=" + resId);
-                return null;
-            }
-        }
-        sInstance.initFlags();
-        return sInstance;
-    }
-
-    /* package for test */ static BinaryDictionary initDictionary(File dictionary, long startOffset,
-            long length, int dicTypeId) {
-        synchronized (sInstance) {
-            sInstance.closeInternal();
-            if (dictionary.isFile()) {
-                sInstance.loadDictionary(dictionary.getAbsolutePath(), startOffset, length);
-                sInstance.mDicTypeId = dicTypeId;
-            } else {
-                Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath());
-                return null;
-            }
-        }
-        return sInstance;
-    }
-
-    private void initFlags() {
-        int flags = 0;
-        for (Flags.FlagEntry entry : Flags.ALL_FLAGS) {
-            if (mSubtypeSwitcher.currentSubtypeContainsExtraValueKey(entry.mName))
-                flags |= entry.mValue;
-        }
-        mFlags = flags;
+    public BinaryDictionary(final Context context,
+            final String filename, final long offset, final long length, Flag[] flagArray) {
+        // Note: at the moment a binary dictionary is always of the "main" type.
+        // Initializing this here will help transitioning out of the scheme where
+        // the Suggest class knows everything about every single dictionary.
+        mDicTypeId = Suggest.DIC_MAIN;
+        // TODO: Stop relying on the state of SubtypeSwitcher, get it as a parameter
+        mFlags = Flag.initFlags(null == flagArray ? ALL_FLAGS : flagArray, context,
+                SubtypeSwitcher.getInstance());
+        loadDictionary(filename, offset, length);
     }
 
     static {
@@ -149,16 +107,15 @@
     private native boolean isValidWordNative(int nativeData, char[] word, int wordLength);
     private native int getSuggestionsNative(int dict, int proximityInfo, int[] xCoordinates,
             int[] yCoordinates, int[] inputCodes, int codesSize, int flags, char[] outputChars,
-            int[] frequencies);
+            int[] scores);
     private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength,
-            int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies,
+            int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores,
             int maxWordLength, int maxBigrams, int maxAlternatives);
 
     private final void loadDictionary(String path, long startOffset, long length) {
         mNativeDict = openNative(path, startOffset, length,
-                    TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER,
+                    TYPED_LETTER_MULTIPLIER, FULL_WORD_SCORE_MULTIPLIER,
                     MAX_WORD_LENGTH, MAX_WORDS, MAX_PROXIMITY_CHARS_SIZE);
-        mDictLength = length;
     }
 
     @Override
@@ -168,27 +125,32 @@
 
         char[] chars = previousWord.toString().toCharArray();
         Arrays.fill(mOutputChars_bigrams, (char) 0);
-        Arrays.fill(mFrequencies_bigrams, 0);
+        Arrays.fill(mBigramScores, 0);
 
         int codesSize = codes.size();
+        if (codesSize <= 0) {
+            // Do not return bigrams from BinaryDictionary when nothing was typed.
+            // Only use user-history bigrams (or whatever other bigram dictionaries decide).
+            return;
+        }
         Arrays.fill(mInputCodes, -1);
         int[] alternatives = codes.getCodesAt(0);
         System.arraycopy(alternatives, 0, mInputCodes, 0,
                 Math.min(alternatives.length, MAX_PROXIMITY_CHARS_SIZE));
 
         int count = getBigramsNative(mNativeDict, chars, chars.length, mInputCodes, codesSize,
-                mOutputChars_bigrams, mFrequencies_bigrams, MAX_WORD_LENGTH, MAX_BIGRAMS,
+                mOutputChars_bigrams, mBigramScores, MAX_WORD_LENGTH, MAX_BIGRAMS,
                 MAX_PROXIMITY_CHARS_SIZE);
 
         for (int j = 0; j < count; ++j) {
-            if (mFrequencies_bigrams[j] < 1) break;
+            if (mBigramScores[j] < 1) break;
             final int start = j * MAX_WORD_LENGTH;
             int len = 0;
             while (len <  MAX_WORD_LENGTH && mOutputChars_bigrams[start + len] != 0) {
                 ++len;
             }
             if (len > 0) {
-                callback.addWord(mOutputChars_bigrams, start, len, mFrequencies_bigrams[j],
+                callback.addWord(mOutputChars_bigrams, start, len, mBigramScores[j],
                         mDicTypeId, DataType.BIGRAM);
             }
         }
@@ -197,17 +159,17 @@
     @Override
     public void getWords(final WordComposer codes, final WordCallback callback) {
         final int count = getSuggestions(codes, mKeyboardSwitcher.getLatinKeyboard(),
-                mOutputChars, mFrequencies);
+                mOutputChars, mScores);
 
         for (int j = 0; j < count; ++j) {
-            if (mFrequencies[j] < 1) break;
+            if (mScores[j] < 1) break;
             final int start = j * MAX_WORD_LENGTH;
             int len = 0;
             while (len < MAX_WORD_LENGTH && mOutputChars[start + len] != 0) {
                 ++len;
             }
             if (len > 0) {
-                callback.addWord(mOutputChars, start, len, mFrequencies[j], mDicTypeId,
+                callback.addWord(mOutputChars, start, len, mScores[j], mDicTypeId,
                         DataType.UNIGRAM);
             }
         }
@@ -218,7 +180,7 @@
     }
 
     /* package for test */ int getSuggestions(final WordComposer codes, final Keyboard keyboard,
-            char[] outputChars, int[] frequencies) {
+            char[] outputChars, int[] scores) {
         if (!isValidDictionary()) return -1;
 
         final int codesSize = codes.size();
@@ -232,12 +194,13 @@
                     Math.min(alternatives.length, MAX_PROXIMITY_CHARS_SIZE));
         }
         Arrays.fill(outputChars, (char) 0);
-        Arrays.fill(frequencies, 0);
+        Arrays.fill(scores, 0);
 
+        final int proximityInfo = keyboard == null ? 0 : keyboard.getProximityInfo();
         return getSuggestionsNative(
-                mNativeDict, keyboard.getProximityInfo(),
+                mNativeDict, proximityInfo,
                 codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize,
-                mFlags, outputChars, frequencies);
+                mFlags, outputChars, scores);
     }
 
     @Override
@@ -247,10 +210,6 @@
         return isValidWordNative(mNativeDict, chars, chars.length);
     }
 
-    public long getSize() {
-        return mDictLength; // This value is initialized in loadDictionary()
-    }
-
     @Override
     public synchronized void close() {
         closeInternal();
@@ -260,7 +219,6 @@
         if (mNativeDict != 0) {
             closeNative(mNativeDict);
             mNativeDict = 0;
-            mDictLength = 0;
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
new file mode 100644
index 0000000..76a230f
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -0,0 +1,151 @@
+/*
+ * 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.ContentResolver;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Group class for static methods to help with creation and getting of the binary dictionary
+ * file from the dictionary provider
+ */
+public class BinaryDictionaryFileDumper {
+    /**
+     * The size of the temporary buffer to copy files.
+     */
+    static final int FILE_READ_BUFFER_SIZE = 1024;
+
+    // Prevents this class to be accidentally instantiated.
+    private BinaryDictionaryFileDumper() {
+    }
+
+    /**
+     * Generates a file name that matches the locale passed as an argument.
+     * The file name is basically the result of the .toString() method, except we replace
+     * any @File.separator with an underscore to avoid generating a file name that may not
+     * be created.
+     * @param locale the locale for which to get the file name
+     * @param context the context to use for getting the directory
+     * @return the name of the file to be created
+     */
+    private static String getCacheFileNameForLocale(Locale locale, Context context) {
+        // The following assumes two things :
+        // 1. That File.separator is not the same character as "_"
+        //    I don't think any android system will ever use "_" as a path separator
+        // 2. That no two locales differ by only a File.separator versus a "_"
+        //    Since "_" can't be part of locale components this should be safe.
+        // Examples:
+        // en -> en
+        // en_US_POSIX -> en_US_POSIX
+        // en__foo/bar -> en__foo_bar
+        final String[] separator = { File.separator };
+        final String[] empty = { "_" };
+        final CharSequence basename = TextUtils.replace(locale.toString(), separator, empty);
+        return context.getFilesDir() + File.separator + basename;
+    }
+
+    /**
+     * Return for a given locale the provider URI to query to get the dictionary.
+     */
+    public static Uri getProviderUri(Locale locale) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(BinaryDictionary.DICTIONARY_PACK_AUTHORITY).appendPath(
+                        locale.toString()).build();
+    }
+
+    /**
+     * Queries a content provider for dictionary data for some locale and returns the file addresses
+     *
+     * This will query a content provider for dictionary data for a given locale, and return
+     * the addresses of a file set the members of which are suitable to be mmap'ed. It will copy
+     * them to local storage if needed.
+     * It should also check the dictionary versions to avoid unnecessary copies but this is
+     * still in TODO state.
+     * This will make the data from the content provider the cached dictionary for this locale,
+     * overwriting any previous cached data.
+     * @returns the addresses of the files, or null if no data could be obtained.
+     * @throw FileNotFoundException if the provider returns non-existent data.
+     * @throw IOException if the provider-returned data could not be read.
+     */
+    public static List<AssetFileAddress> getDictSetFromContentProvider(Locale locale,
+            Context context) throws FileNotFoundException, IOException {
+        // TODO: check whether the dictionary is the same or not and if it is, return the cached
+        // file.
+        // TODO: This should be able to read a number of files from the dictionary pack, copy
+        // them all and return them.
+        final ContentResolver resolver = context.getContentResolver();
+        final Uri dictionaryPackUri = getProviderUri(locale);
+        final AssetFileDescriptor afd = resolver.openAssetFileDescriptor(dictionaryPackUri, "r");
+        if (null == afd) return null;
+        final String fileName =
+                copyFileTo(afd.createInputStream(), getCacheFileNameForLocale(locale, context));
+        return Arrays.asList(AssetFileAddress.makeFromFileName(fileName));
+    }
+
+    /**
+     * Accepts a file as dictionary data for some locale and returns the name of a file.
+     *
+     * This will make the data in the input file the cached dictionary for this locale, overwriting
+     * any previous cached data.
+     */
+    public static String getDictionaryFileFromFile(String fileName, Locale locale,
+            Context context) throws FileNotFoundException, IOException {
+        return copyFileTo(new FileInputStream(fileName), getCacheFileNameForLocale(locale,
+                context));
+    }
+
+    /**
+     * Accepts a resource number as dictionary data for some locale and returns the name of a file.
+     *
+     * This will make the resource the cached dictionary for this locale, overwriting any previous
+     * cached data.
+     */
+    public static String getDictionaryFileFromResource(int resource, Locale locale,
+            Context context) throws FileNotFoundException, IOException {
+        return copyFileTo(context.getResources().openRawResource(resource),
+                getCacheFileNameForLocale(locale, context));
+    }
+
+    /**
+     * Copies the data in an input stream to a target file, creating the file if necessary and
+     * overwriting it if it already exists.
+     * @param input the stream to be copied.
+     * @param outputFileName the name of a file to copy the data to. It is created if necessary.
+     */
+    private static String copyFileTo(final InputStream input, final String outputFileName)
+            throws FileNotFoundException, IOException {
+        final byte[] buffer = new byte[FILE_READ_BUFFER_SIZE];
+        final FileOutputStream output = new FileOutputStream(outputFileName);
+        for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer))
+            output.write(buffer, 0, readBytes);
+        input.close();
+        return outputFileName;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
new file mode 100644
index 0000000..7ce9292
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -0,0 +1,97 @@
+/*
+ * 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.content.res.AssetFileDescriptor;
+import android.util.Log;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Helper class to get the address of a mmap'able dictionary file.
+ */
+class BinaryDictionaryGetter {
+
+    /**
+     * Used for Log actions from this class
+     */
+    private static final String TAG = BinaryDictionaryGetter.class.getSimpleName();
+
+    // Prevents this from being instantiated
+    private BinaryDictionaryGetter() {}
+
+    /**
+     * Returns a file address from a resource, or null if it cannot be opened.
+     */
+    private static AssetFileAddress loadFallbackResource(Context context, int fallbackResId) {
+        final AssetFileDescriptor afd = context.getResources().openRawResourceFd(fallbackResId);
+        if (afd == null) {
+            Log.e(TAG, "Found the resource but cannot read it. Is it compressed? resId="
+                    + fallbackResId);
+            return null;
+        }
+        return AssetFileAddress.makeFromFileNameAndOffset(
+                context.getApplicationInfo().sourceDir, afd.getStartOffset(), afd.getLength());
+    }
+
+    /**
+     * Returns a list of file addresses for a given locale, trying relevant methods in order.
+     *
+     * Tries to get binary dictionaries from various sources, in order:
+     * - Uses a private method of getting a private dictionaries, as implemented by the
+     *   PrivateBinaryDictionaryGetter class.
+     * If that fails:
+     * - Uses a content provider to get a public dictionary set, as per the protocol described
+     *   in BinaryDictionaryFileDumper.
+     * If that fails:
+     * - Gets a file name from the fallback resource passed as an argument.
+     * If that fails:
+     * - Returns null.
+     * @return The address of a valid file, or null.
+     */
+    public static List<AssetFileAddress> getDictionaryFiles(Locale locale, Context context,
+            int fallbackResId) {
+        // Try first to query a private package signed the same way for private files.
+        final List<AssetFileAddress> privateFiles =
+                PrivateBinaryDictionaryGetter.getDictionaryFiles(locale, context);
+        if (null != privateFiles) {
+            return privateFiles;
+        } else {
+            try {
+                // If that was no-go, try to find a publicly exported dictionary.
+                List<AssetFileAddress> listFromContentProvider =
+                        BinaryDictionaryFileDumper.getDictSetFromContentProvider(locale, context);
+                if (null != listFromContentProvider) {
+                    return listFromContentProvider;
+                }
+                // If the list is null, fall through and return the fallback
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "Unable to create dictionary file from provider for locale "
+                        + locale.toString() + ": falling back to internal dictionary");
+            } catch (IOException e) {
+                Log.e(TAG, "Unable to read source data for locale "
+                        + locale.toString() + ": falling back to internal dictionary");
+            }
+            return Arrays.asList(loadFallbackResource(context, fallbackResId));
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index c52f6b2..09b356d 100644
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -16,10 +16,9 @@
 
 package com.android.inputmethod.latin;
 
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-
 import android.content.Context;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.Typeface;
 import android.os.Handler;
@@ -45,28 +44,52 @@
 import android.widget.PopupWindow;
 import android.widget.TextView;
 
+import com.android.inputmethod.compat.FrameLayoutCompatUtils;
+import com.android.inputmethod.compat.LinearLayoutCompatUtils;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
 import java.util.ArrayList;
 import java.util.List;
 
 public class CandidateView extends LinearLayout implements OnClickListener, OnLongClickListener {
 
+    public interface Listener {
+        public boolean addWordToDictionary(String word);
+        public void pickSuggestionManually(int index, CharSequence word);
+    }
+
     private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
     private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
-    private static final int MAX_SUGGESTIONS = 16;
+    // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
+    private static final int MAX_SUGGESTIONS = 18;
+    private static final int UNSPECIFIED_MEASURESPEC = MeasureSpec.makeMeasureSpec(
+            0, MeasureSpec.UNSPECIFIED);
 
     private static final boolean DBG = LatinImeLogger.sDBG;
 
-    private final ArrayList<View> mWords = new ArrayList<View>();
-    private final boolean mConfigCandidateHighlightFontColorEnabled;
+    private static final int NUM_CANDIDATES_IN_STRIP = 3;
+    private final ImageView mExpandCandidatesPane;
+    private final ImageView mCloseCandidatesPane;
+    private ViewGroup mCandidatesPane;
+    private ViewGroup mCandidatesPaneContainer;
+    private View mKeyboardView;
+    private final ArrayList<TextView> mWords = new ArrayList<TextView>();
+    private final ArrayList<View> mDividers = new ArrayList<View>();
+    private final int mCandidatePadding;
+    private final int mCandidateStripHeight;
     private final CharacterStyle mInvertedForegroundColorSpan;
     private final CharacterStyle mInvertedBackgroundColorSpan;
-    private final int mColorNormal;
-    private final int mColorRecommended;
-    private final int mColorOther;
+    private final int mAutoCorrectHighlight;
+    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 final int mColorTypedWord;
+    private final int mColorAutoCorrect;
+    private final int mColorSuggestedCandidate;
     private final PopupWindow mPreviewPopup;
     private final TextView mPreviewText;
 
-    private LatinIME mService;
+    private Listener mListener;
     private SuggestedWords mSuggestions = SuggestedWords.EMPTY;
     private boolean mShowingAutoCorrectionInverted;
     private boolean mShowingAddToDictionary;
@@ -123,47 +146,110 @@
      * @param attrs
      */
     public CandidateView(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.candidateViewStyle);
+    }
+
+    public CandidateView(Context context, AttributeSet attrs, int defStyle) {
+        // Note: Up to version 10 (Gingerbread) of the API, LinearLayout doesn't have 3-argument
+        // constructor.
+        // TODO: Call 3-argument constructor, super(context, attrs, defStyle), when we abandon
+        // backward compatibility with the version 10 or earlier of the API.
         super(context, attrs);
+        if (defStyle != R.attr.candidateViewStyle) {
+            throw new IllegalArgumentException(
+                    "can't accept defStyle other than R.attr.candidayeViewStyle: defStyle="
+                    + defStyle);
+        }
+        setBackgroundDrawable(LinearLayoutCompatUtils.getBackgroundDrawable(
+                context, attrs, defStyle, R.style.CandidateViewStyle));
 
         Resources res = context.getResources();
-        mPreviewPopup = new PopupWindow(context);
         LayoutInflater inflater = LayoutInflater.from(context);
+        inflater.inflate(R.layout.candidates_strip, this);
+
+        mPreviewPopup = new PopupWindow(context);
         mPreviewText = (TextView) inflater.inflate(R.layout.candidate_preview, null);
         mPreviewPopup.setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT,
                 ViewGroup.LayoutParams.WRAP_CONTENT);
         mPreviewPopup.setContentView(mPreviewText);
         mPreviewPopup.setBackgroundDrawable(null);
-        mPreviewPopup.setAnimationStyle(R.style.KeyPreviewAnimation);
-        mConfigCandidateHighlightFontColorEnabled =
-                res.getBoolean(R.bool.config_candidate_highlight_font_color_enabled);
-        mColorNormal = res.getColor(R.color.candidate_normal);
-        mColorRecommended = res.getColor(R.color.candidate_recommended);
-        mColorOther = res.getColor(R.color.candidate_other);
-        mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorNormal ^ 0x00ffffff);
-        mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorNormal);
 
+        mCandidatePadding = res.getDimensionPixelOffset(R.dimen.candidate_padding);
+        mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height);
         for (int i = 0; i < MAX_SUGGESTIONS; i++) {
-            View v = inflater.inflate(R.layout.candidate, null);
-            TextView tv = (TextView)v.findViewById(R.id.candidate_word);
+            final TextView tv;
+            switch (i) {
+            case 0:
+                tv = (TextView)findViewById(R.id.candidate_left);
+                tv.setPadding(mCandidatePadding, 0, 0, 0);
+                break;
+            case 1:
+                tv = (TextView)findViewById(R.id.candidate_center);
+                break;
+            case 2:
+                tv = (TextView)findViewById(R.id.candidate_right);
+                break;
+            default:
+                tv = (TextView)inflater.inflate(R.layout.candidate, null);
+                break;
+            }
+            if (i < NUM_CANDIDATES_IN_STRIP)
+                setLayoutWeight(tv, 1.0f);
             tv.setTag(i);
             tv.setOnClickListener(this);
             if (i == 0)
                 tv.setOnLongClickListener(this);
-            ImageView divider = (ImageView)v.findViewById(R.id.candidate_divider);
-            // Do not display divider of first candidate.
-            divider.setVisibility(i == 0 ? GONE : VISIBLE);
-            mWords.add(v);
+            mWords.add(tv);
+            if (i > 0) {
+                final View divider = inflater.inflate(R.layout.candidate_divider, null);
+                divider.measure(UNSPECIFIED_MEASURESPEC, UNSPECIFIED_MEASURESPEC);
+                mDividers.add(divider);
+            }
         }
 
-        scrollTo(0, getScrollY());
+        final TypedArray a = context.obtainStyledAttributes(
+                attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle);
+        mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0);
+        mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0);
+        mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0);
+        mColorSuggestedCandidate = a.getColor(R.styleable.CandidateView_colorSuggested, 0);
+        mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff);
+        mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord);
+
+        mExpandCandidatesPane = (ImageView)findViewById(R.id.expand_candidates_pane);
+        mExpandCandidatesPane.setImageDrawable(
+                a.getDrawable(R.styleable.CandidateView_iconExpandPane));
+        mExpandCandidatesPane.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                expandCandidatesPane();
+            }
+        });
+        mCloseCandidatesPane = (ImageView)findViewById(R.id.close_candidates_pane);
+        mCloseCandidatesPane.setImageDrawable(
+                a.getDrawable(R.styleable.CandidateView_iconClosePane));
+        mCloseCandidatesPane.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                closeCandidatesPane();
+            }
+        });
+
+        a.recycle();
     }
 
     /**
-     * A connection back to the service to communicate with the text field
+     * A connection back to the input method.
      * @param listener
      */
-    public void setService(LatinIME listener) {
-        mService = listener;
+    public void setListener(Listener listener, View inputView) {
+        mListener = listener;
+        mKeyboardView = inputView.findViewById(R.id.keyboard_view);
+        mCandidatesPane = FrameLayoutCompatUtils.getPlacer(
+                (ViewGroup)inputView.findViewById(R.id.candidates_pane));
+        mCandidatesPane.setOnClickListener(this);
+        mCandidatesPaneContainer = (ViewGroup)inputView.findViewById(
+                R.id.candidates_pane_container);
     }
 
     public void setSuggestions(SuggestedWords suggestions) {
@@ -177,80 +263,163 @@
         }
     }
 
+    private static void setLayoutWeight(View v, float weight) {
+        ViewGroup.LayoutParams lp = v.getLayoutParams();
+        if (lp instanceof LinearLayout.LayoutParams) {
+            LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp;
+            llp.width = 0;
+            llp.weight = weight;
+        }
+    }
+
+    private CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect) {
+        if (!isAutoCorrect)
+            return word;
+        final Spannable spannedWord = new SpannableString(word);
+        if ((mAutoCorrectHighlight & AUTO_CORRECT_BOLD) != 0)
+            spannedWord.setSpan(BOLD_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        if ((mAutoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0)
+            spannedWord.setSpan(UNDERLINE_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        return spannedWord;
+    }
+
+    private int getCandidateTextColor(boolean isAutoCorrect, boolean isSuggestedCandidate,
+            SuggestedWordInfo info) {
+        final int color;
+        if (isAutoCorrect) {
+            color = mColorAutoCorrect;
+        } else if (isSuggestedCandidate) {
+            color = mColorSuggestedCandidate;
+        } else {
+            color = mColorTypedWord;
+        }
+        if (info != null && info.isPreviousSuggestedWord()) {
+            final int newAlpha = (int)(Color.alpha(color) * 0.5f);
+            return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color));
+        } else {
+            return color;
+        }
+    }
+
     private void updateSuggestions() {
         final SuggestedWords suggestions = mSuggestions;
+        final List<SuggestedWordInfo> suggestedWordInfoList = suggestions.mSuggestedWordInfoList;
+
         clear();
-        final int count = suggestions.size();
+        final int paneWidth = getWidth();
+        final int dividerWidth = mDividers.get(0).getMeasuredWidth();
+        int x = 0;
+        int y = 0;
+        int fromIndex = NUM_CANDIDATES_IN_STRIP;
+        final int count = Math.min(mWords.size(), suggestions.size());
+        closeCandidatesPane();
+        mExpandCandidatesPane.setEnabled(count >= NUM_CANDIDATES_IN_STRIP);
         for (int i = 0; i < count; i++) {
-            CharSequence word = suggestions.getWord(i);
+            final CharSequence word = suggestions.getWord(i);
             if (word == null) continue;
-            final int wordLength = word.length();
-            final List<SuggestedWordInfo> suggestedWordInfoList =
-                    suggestions.mSuggestedWordInfoList;
 
-            final View v = mWords.get(i);
-            final TextView tv = (TextView)v.findViewById(R.id.candidate_word);
-            final TextView dv = (TextView)v.findViewById(R.id.candidate_debug_info);
-            tv.setTextColor(mColorNormal);
-            // TODO: Needs safety net?
-            if (suggestions.mHasMinimalSuggestion
+            final SuggestedWordInfo info = (suggestedWordInfoList != null)
+                    ? suggestedWordInfoList.get(i) : null;
+            final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion
                     && ((i == 1 && !suggestions.mTypedWordValid)
-                            || (i == 0 && suggestions.mTypedWordValid))) {
-                final CharacterStyle style;
-                if (mConfigCandidateHighlightFontColorEnabled) {
-                    style = BOLD_SPAN;
-                    tv.setTextColor(mColorRecommended);
-                } else {
-                    style = UNDERLINE_SPAN;
-                }
-                final Spannable spannedWord = new SpannableString(word);
-                spannedWord.setSpan(style, 0, wordLength, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-                word = spannedWord;
-            } else if (i != 0 || (wordLength == 1 && count > 1)) {
-                // HACK: even if i == 0, we use mColorOther when this
-                // suggestion's length is 1
-                // and there are multiple suggestions, such as the default
-                // punctuation list.
-                if (mConfigCandidateHighlightFontColorEnabled)
-                    tv.setTextColor(mColorOther);
-            }
-            tv.setText(word);
-            tv.setClickable(true);
+                            || (i == 0 && suggestions.mTypedWordValid));
+            // HACK: even if i == 0, we use mColorOther when this suggestion's length is 1
+            // and there are multiple suggestions, such as the default punctuation list.
+            // TODO: Need to revisit this logic with bigram suggestions
+            final boolean isSuggestedCandidate = (i != 0);
+            final boolean isPunctuationSuggestions = (word.length() == 1 && count > 1);
 
-            if (suggestedWordInfoList != null && suggestedWordInfoList.get(i) != null) {
-                final SuggestedWordInfo info = suggestedWordInfoList.get(i);
-                if (info.isPreviousSuggestedWord()) {
-                    int color = tv.getCurrentTextColor();
-                    tv.setTextColor(Color.argb((int)(Color.alpha(color) * 0.5f), Color.red(color),
-                            Color.green(color), Color.blue(color)));
+            final TextView tv = mWords.get(i);
+            // TODO: Reorder candidates in strip as appropriate. The center candidate should hold
+            // the word when space is typed (valid typed word or auto corrected word).
+            tv.setTextColor(getCandidateTextColor(isAutoCorrect,
+                    isSuggestedCandidate || isPunctuationSuggestions, info));
+            tv.setText(getStyledCandidateWord(word, isAutoCorrect));
+            // TODO: call TextView.setTextScaleX() to fit the candidate in single line.
+            if (i >= NUM_CANDIDATES_IN_STRIP) {
+                tv.measure(UNSPECIFIED_MEASURESPEC, UNSPECIFIED_MEASURESPEC);
+                final int width = tv.getMeasuredWidth();
+                // TODO: Handle overflow case.
+                if (dividerWidth + x + width >= paneWidth) {
+                    centeringCandidates(fromIndex, i - 1, x, paneWidth);
+                    x = 0;
+                    y += mCandidateStripHeight;
+                    fromIndex = i;
                 }
-                final String debugString = info.getDebugString();
-                if (DBG) {
-                    if (TextUtils.isEmpty(debugString)) {
-                        dv.setVisibility(GONE);
-                    } else {
-                        dv.setText(debugString);
-                        dv.setVisibility(VISIBLE);
-                    }
-                } else {
-                    dv.setVisibility(GONE);
+                if (x != 0) {
+                    final View divider = mDividers.get(i - NUM_CANDIDATES_IN_STRIP);
+                    addCandidateAt(divider, x, y);
+                    x += dividerWidth;
                 }
-            } else {
-                dv.setVisibility(GONE);
+                addCandidateAt(tv, x, y);
+                x += width;
             }
-            addView(v);
+
+            if (DBG && info != null) {
+                final TextView dv = new TextView(getContext(), null);
+                dv.setTextSize(10.0f);
+                dv.setTextColor(0xff808080);
+                dv.setText(info.getDebugString());
+                // TODO: debug view for candidate strip needed.
+//                mCandidatesPane.addView(dv);
+//                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)dv.getLayoutParams();
+//                lp.gravity = Gravity.BOTTOM;
+            }
         }
+        if (x != 0) {
+            // Centering last candidates row.
+            centeringCandidates(fromIndex, count - 1, x, paneWidth);
+        }
+    }
 
-        scrollTo(0, getScrollY());
-        requestLayout();
+    private void addCandidateAt(View v, int x, int y) {
+        final int width = v.getMeasuredWidth();
+        final int height = v.getMeasuredHeight();
+        final MarginLayoutParams marginLayoutParams = FrameLayoutCompatUtils.newLayoutParam(
+                mCandidatesPane, width, height);
+        marginLayoutParams.setMargins(x, y + (mCandidateStripHeight - height) / 2, 0, 0);
+        mCandidatesPane.addView(v, marginLayoutParams);
+    }
+
+    private void centeringCandidates(int from, int to, int width, int paneWidth) {
+        final ViewGroup pane = mCandidatesPane;
+        final int fromIndex = pane.indexOfChild(mWords.get(from));
+        final int toIndex = pane.indexOfChild(mWords.get(to));
+        final int offset = (paneWidth - width) / 2;
+        for (int index = fromIndex; index <= toIndex; index++) {
+            offsetMargin(pane.getChildAt(index), offset, 0);
+        }
+    }
+
+    private static void offsetMargin(View v, int dx, int dy) {
+        if (v == null)
+            return;
+        ViewGroup.LayoutParams lp = v.getLayoutParams();
+        if (lp instanceof ViewGroup.MarginLayoutParams) {
+            ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp;
+            mlp.setMargins(mlp.leftMargin + dx, mlp.topMargin + dy, 0, 0);
+        }
+    }
+
+    private void expandCandidatesPane() {
+        mExpandCandidatesPane.setVisibility(View.GONE);
+        mCloseCandidatesPane.setVisibility(View.VISIBLE);
+        mCandidatesPaneContainer.setMinimumHeight(mKeyboardView.getMeasuredHeight());
+        mCandidatesPaneContainer.setVisibility(View.VISIBLE);
+        mKeyboardView.setVisibility(View.GONE);
+    }
+
+    private void closeCandidatesPane() {
+        mExpandCandidatesPane.setVisibility(View.VISIBLE);
+        mCloseCandidatesPane.setVisibility(View.GONE);
+        mCandidatesPaneContainer.setVisibility(View.GONE);
+        mKeyboardView.setVisibility(View.VISIBLE);
     }
 
     public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) {
-        // Displaying auto corrected word as inverted is enabled only when highlighting candidate
-        // with color is disabled.
-        if (mConfigCandidateHighlightFontColorEnabled)
+        if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0)
             return;
-        final TextView tv = (TextView)mWords.get(1).findViewById(R.id.candidate_word);
+        final TextView tv = mWords.get(1);
         final Spannable word = new SpannableString(autoCorrectedWord);
         final int wordLength = word.length();
         word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength,
@@ -261,10 +430,6 @@
         mShowingAutoCorrectionInverted = true;
     }
 
-    public boolean isConfigCandidateHighlightFontColorEnabled() {
-        return mConfigCandidateHighlightFontColorEnabled;
-    }
-
     public boolean isShowingAddToDictionaryHint() {
         return mShowingAddToDictionary;
     }
@@ -276,7 +441,7 @@
         setSuggestions(builder.build());
         mShowingAddToDictionary = true;
         // Disable R.string.hint_add_to_dictionary button
-        TextView tv = (TextView)getChildAt(1).findViewById(R.id.candidate_word);
+        TextView tv = mWords.get(1);
         tv.setClickable(false);
     }
 
@@ -293,7 +458,9 @@
     public void clear() {
         mShowingAddToDictionary = false;
         mShowingAutoCorrectionInverted = false;
-        removeAllViews();
+        for (int i = 0; i < NUM_CANDIDATES_IN_STRIP; i++)
+            mWords.get(i).setText(null);
+        mCandidatesPane.removeAllViews();
     }
 
     private void hidePreview() {
@@ -305,11 +472,11 @@
             return;
 
         final TextView previewText = mPreviewText;
-        previewText.setTextColor(mColorNormal);
+        previewText.setTextColor(mColorTypedWord);
         previewText.setText(word);
         previewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-        View v = getChildAt(index);
+        View v = mWords.get(index);
         final int[] offsetInWindow = new int[2];
         v.getLocationInWindow(offsetInWindow);
         final int posX = offsetInWindow[0];
@@ -325,16 +492,20 @@
     }
 
     private void addToDictionary(CharSequence word) {
-        if (mService.addWordToDictionary(word.toString())) {
+        if (mListener.addWordToDictionary(word.toString())) {
             showPreview(0, getContext().getString(R.string.added_word, word));
         }
     }
 
     @Override
     public boolean onLongClick(View view) {
-        final int index = (Integer) view.getTag();
+        final Object tag = view.getTag();
+        if (!(tag instanceof Integer))
+            return true;
+        final int index = (Integer) tag;
         if (index >= mSuggestions.size())
             return true;
+
         final CharSequence word = mSuggestions.getWord(index);
         if (word.length() < 2)
             return false;
@@ -344,15 +515,22 @@
 
     @Override
     public void onClick(View view) {
-        final int index = (Integer) view.getTag();
+        final Object tag = view.getTag();
+        if (!(tag instanceof Integer))
+            return;
+        final int index = (Integer) tag;
         if (index >= mSuggestions.size())
             return;
+
         final CharSequence word = mSuggestions.getWord(index);
         if (mShowingAddToDictionary && index == 0) {
             addToDictionary(word);
         } else {
-            mService.pickSuggestionManually(index, word);
+            mListener.pickSuggestionManually(index, word);
         }
+        // Because some punctuation letters are not treated as word separator depending on locale,
+        // {@link #setSuggestions} might not be called and candidates pane left opened.
+        closeCandidatesPane();
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/ContactsDictionary.java b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
index 048f72d..66a0415 100644
--- a/java/src/com/android/inputmethod/latin/ContactsDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
@@ -26,6 +26,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.inputmethod.keyboard.Keyboard;
+
 public class ContactsDictionary extends ExpandableDictionary {
 
     private static final String[] PROJECTION = {
@@ -38,7 +40,7 @@
     /**
      * Frequency for contacts information into the dictionary
      */
-    private static final int FREQUENCY_FOR_CONTACTS = 128;
+    private static final int FREQUENCY_FOR_CONTACTS = 40;
     private static final int FREQUENCY_FOR_CONTACTS_BIGRAM = 90;
 
     private static final int INDEX_NAME = 1;
@@ -95,6 +97,14 @@
         mLastLoadedContacts = SystemClock.uptimeMillis();
     }
 
+    @Override
+    public void getBigrams(final WordComposer codes, final CharSequence previousWord,
+            final WordCallback callback) {
+        // Do not return bigrams from Contacts when nothing was typed.
+        if (codes.size() <= 0) return;
+        super.getBigrams(codes, previousWord, callback);
+    }
+
     private void addWords(Cursor cursor) {
         clearDictionary();
 
@@ -104,7 +114,7 @@
                 while (!cursor.isAfterLast()) {
                     String name = cursor.getString(INDEX_NAME);
 
-                    if (name != null) {
+                    if (name != null && -1 == name.indexOf('@')) {
                         int len = name.length();
                         String prevWord = null;
 
@@ -115,8 +125,9 @@
                                 for (j = i + 1; j < len; j++) {
                                     char c = name.charAt(j);
 
-                                    if (!(c == '-' || c == '\'' ||
-                                          Character.isLetter(c))) {
+                                    if (!(c == Keyboard.CODE_DASH
+                                            || c == Keyboard.CODE_SINGLE_QUOTE
+                                            || Character.isLetter(c))) {
                                         break;
                                     }
                                 }
@@ -132,8 +143,6 @@
                                 if (wordLen < maxWordLength && wordLen > 1) {
                                     super.addWord(word, FREQUENCY_FOR_CONTACTS);
                                     if (!TextUtils.isEmpty(prevWord)) {
-                                        // TODO Do not add email address
-                                        // Not so critical
                                         super.setBigram(prevWord, word,
                                                 FREQUENCY_FOR_CONTACTS_BIGRAM);
                                     }
diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java
index 2f1e7c2..fd62d61 100644
--- a/java/src/com/android/inputmethod/latin/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/DebugSettings.java
@@ -33,6 +33,7 @@
 
     private boolean mServiceNeedsRestart = false;
     private CheckBoxPreference mDebugMode;
+    private CheckBoxPreference mUseSpacebarLanguageSwitch;
 
     @Override
     protected void onCreate(Bundle icicle) {
@@ -60,6 +61,13 @@
                 updateDebugMode();
                 mServiceNeedsRestart = true;
             }
+        } else if (key.equals(SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCH_KEY)) {
+            if (mUseSpacebarLanguageSwitch != null) {
+                mUseSpacebarLanguageSwitch.setChecked(
+                        prefs.getBoolean(SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCH_KEY,
+                                getResources().getBoolean(
+                                        R.bool.config_use_spacebar_language_switcher)));
+            }
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 56f0cc5..c7737b9 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -29,7 +29,7 @@
     /**
      * The weight to give to a word if it's length is the same as the number of typed characters.
      */
-    protected static final int FULL_WORD_FREQ_MULTIPLIER = 2;
+    protected static final int FULL_WORD_SCORE_MULTIPLIER = 2;
 
     public static enum DataType {
         UNIGRAM, BIGRAM
@@ -42,17 +42,17 @@
     public interface WordCallback {
         /**
          * Adds a word to a list of suggestions. The word is expected to be ordered based on
-         * the provided frequency.
+         * the provided score.
          * @param word the character array containing the word
          * @param wordOffset starting offset of the word in the character array
          * @param wordLength length of valid characters in the character array
-         * @param frequency the frequency of occurrence. This is normalized between 1 and 255, but
+         * @param score the score of occurrence. This is normalized between 1 and 255, but
          * can exceed those limits
          * @param dicTypeId of the dictionary where word was from
          * @param dataType tells type of this data
          * @return true if the word was added, false if no more words are required
          */
-        boolean addWord(char[] word, int wordOffset, int wordLength, int frequency, int dicTypeId,
+        boolean addWord(char[] word, int wordOffset, int wordLength, int score, int dicTypeId,
                 DataType dataType);
     }
 
@@ -61,7 +61,7 @@
      * words are added through the callback object.
      * @param composer the key sequence to match
      * @param callback the callback object to send matched words to as possible candidates
-     * @see WordCallback#addWord(char[], int, int)
+     * @see WordCallback#addWord(char[], int, int, int, int, DataType)
      */
     abstract public void getWords(final WordComposer composer, final WordCallback callback);
 
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
new file mode 100644
index 0000000..5e7de3e
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -0,0 +1,71 @@
+/*
+ * 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 java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Class for a collection of dictionaries that behave like one dictionary.
+ */
+public class DictionaryCollection extends Dictionary {
+
+    protected final List<Dictionary> mDictionaries;
+
+    public DictionaryCollection() {
+        mDictionaries = new CopyOnWriteArrayList<Dictionary>();
+    }
+
+    public DictionaryCollection(Dictionary... dictionaries) {
+        mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
+    }
+
+    public DictionaryCollection(Collection<Dictionary> dictionaries) {
+        mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
+    }
+
+    @Override
+    public void getWords(final WordComposer composer, final WordCallback callback) {
+        for (final Dictionary dict : mDictionaries)
+            dict.getWords(composer, callback);
+    }
+
+    @Override
+    public void getBigrams(final WordComposer composer, final CharSequence previousWord,
+            final WordCallback callback) {
+        for (final Dictionary dict : mDictionaries)
+            dict.getBigrams(composer, previousWord, callback);
+    }
+
+    @Override
+    public boolean isValidWord(CharSequence word) {
+        for (int i = mDictionaries.size() - 1; i >= 0; --i)
+            if (mDictionaries.get(i).isValidWord(word)) return true;
+        return false;
+    }
+
+    @Override
+    public void close() {
+        for (final Dictionary dict : mDictionaries)
+            dict.close();
+    }
+
+    public void addDictionary(Dictionary newDict) {
+        mDictionaries.add(newDict);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
new file mode 100644
index 0000000..bba3318
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -0,0 +1,177 @@
+/*
+ * 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.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.util.Log;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Factory for dictionary instances.
+ */
+public class DictionaryFactory {
+
+    private static String TAG = DictionaryFactory.class.getSimpleName();
+
+    /**
+     * Initializes a dictionary from a dictionary pack.
+     *
+     * This searches for a content provider providing a dictionary pack for the specified
+     * locale. If none is found, it falls back to using the resource passed as fallBackResId
+     * as a dictionary.
+     * @param context application context for reading resources
+     * @param locale the locale for which to create the dictionary
+     * @param fallbackResId the id of the resource to use as a fallback if no pack is found
+     * @return an initialized instance of Dictionary
+     */
+    public static Dictionary createDictionaryFromManager(Context context, Locale locale,
+            int fallbackResId) {
+        if (null == locale) {
+            Log.e(TAG, "No locale defined for dictionary");
+            return new DictionaryCollection(createBinaryDictionary(context, fallbackResId));
+        }
+
+        final List<Dictionary> dictList = new LinkedList<Dictionary>();
+        for (final AssetFileAddress f : BinaryDictionaryGetter.getDictionaryFiles(locale,
+                context, fallbackResId)) {
+            dictList.add(new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength, null));
+        }
+
+        if (null == dictList) return null;
+        return new DictionaryCollection(dictList);
+    }
+
+    /**
+     * Initializes a dictionary from a raw resource file
+     * @param context application context for reading resources
+     * @param resId the resource containing the raw binary dictionary
+     * @return an initialized instance of BinaryDictionary
+     */
+    protected static BinaryDictionary createBinaryDictionary(Context context, int resId) {
+        AssetFileDescriptor afd = null;
+        try {
+            afd = context.getResources().openRawResourceFd(resId);
+            if (afd == null) {
+                Log.e(TAG, "Found the resource but it is compressed. resId=" + resId);
+                return null;
+            }
+            if (!isFullDictionary(afd)) return null;
+            final String sourceDir = context.getApplicationInfo().sourceDir;
+            final File packagePath = new File(sourceDir);
+            // TODO: Come up with a way to handle a directory.
+            if (!packagePath.isFile()) {
+                Log.e(TAG, "sourceDir is not a file: " + sourceDir);
+                return null;
+            }
+            return new BinaryDictionary(context,
+                    sourceDir, afd.getStartOffset(), afd.getLength(), null);
+        } catch (android.content.res.Resources.NotFoundException e) {
+            Log.e(TAG, "Could not find the resource. resId=" + resId);
+            return null;
+        } finally {
+            if (null != afd) {
+                try {
+                    afd.close();
+                } catch (java.io.IOException e) {
+                    /* IOException on close ? What am I supposed to do ? */
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a dictionary from passed data. This is intended for unit tests only.
+     * @param context the test context to create this data from.
+     * @param dictionary the file to read
+     * @param startOffset the offset in the file where the data starts
+     * @param length the length of the data
+     * @param flagArray the flags to use with this data for testing
+     * @return the created dictionary, or null.
+     */
+    public static Dictionary createDictionaryForTest(Context context, File dictionary,
+            long startOffset, long length, Flag[] flagArray) {
+        if (dictionary.isFile()) {
+            return new BinaryDictionary(context, dictionary.getAbsolutePath(), startOffset, length,
+                    flagArray);
+        } else {
+            Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath());
+            return null;
+        }
+    }
+
+    /**
+     * Find out whether a dictionary is available for this locale.
+     * @param context the context on which to check resources.
+     * @param locale the locale to check for.
+     * @return whether a (non-placeholder) dictionary is available or not.
+     */
+    public static boolean isDictionaryAvailable(Context context, Locale locale) {
+        final Resources res = context.getResources();
+        final Locale saveLocale = Utils.setSystemLocale(res, locale);
+
+        final int resourceId = Utils.getMainDictionaryResourceId(res);
+        final AssetFileDescriptor afd = res.openRawResourceFd(resourceId);
+        final boolean hasDictionary = isFullDictionary(afd);
+        try {
+            if (null != afd) afd.close();
+        } catch (java.io.IOException e) {
+            /* Um, what can we do here exactly? */
+        }
+
+        Utils.setSystemLocale(res, saveLocale);
+        return hasDictionary;
+    }
+
+    // TODO: Do not use the size of the dictionary as an unique dictionary ID.
+    public static Long getDictionaryId(Context context, Locale locale) {
+        final Resources res = context.getResources();
+        final Locale saveLocale = Utils.setSystemLocale(res, locale);
+
+        final int resourceId = Utils.getMainDictionaryResourceId(res);
+        final AssetFileDescriptor afd = res.openRawResourceFd(resourceId);
+        final Long size = (afd != null && afd.getLength() > PLACEHOLDER_LENGTH)
+                ? afd.getLength()
+                : null;
+        try {
+            if (null != afd) afd.close();
+        } catch (java.io.IOException e) {
+        }
+
+        Utils.setSystemLocale(res, saveLocale);
+        return size;
+    }
+
+    // TODO: Find the Right Way to find out whether the resource is a placeholder or not.
+    // Suggestion : strip the locale, open the placeholder file and store its offset.
+    // Upon opening the file, if it's the same offset, then it's the placeholder.
+    private static final long PLACEHOLDER_LENGTH = 34;
+    /**
+     * Finds out whether the data pointed out by an AssetFileDescriptor is a full
+     * dictionary (as opposed to null, or to a place holder).
+     * @param afd the file descriptor to test, or null
+     * @return true if the dictionary is a real full dictionary, false if it's null or a placeholder
+     */
+    protected static boolean isFullDictionary(final AssetFileDescriptor afd) {
+        return (afd != null && afd.getLength() > PLACEHOLDER_LENGTH);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
new file mode 100644
index 0000000..9d30af8
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
@@ -0,0 +1,89 @@
+/*
+ * 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.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
+
+/**
+ * Takes action to reload the necessary data when a dictionary pack was added/removed.
+ */
+public class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver {
+
+    final LatinIME mService;
+    /**
+     * The action of the intent for publishing that new dictionary data is available.
+     */
+    /* package */ static final String NEW_DICTIONARY_INTENT_ACTION =
+            "com.android.inputmethod.latin.dictionarypack.newdict";
+
+    public DictionaryPackInstallBroadcastReceiver(final LatinIME service) {
+        mService = service;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        final PackageManager manager = context.getPackageManager();
+
+        // We need to reread the dictionary if a new dictionary package is installed.
+        if (action.equals(Intent.ACTION_PACKAGE_ADDED)) {
+            final Uri packageUri = intent.getData();
+            if (null == packageUri) return; // No package name : we can't do anything
+            final String packageName = packageUri.getSchemeSpecificPart();
+            if (null == packageName) return;
+            final PackageInfo packageInfo;
+            try {
+                packageInfo = manager.getPackageInfo(packageName, PackageManager.GET_PROVIDERS);
+            } catch (android.content.pm.PackageManager.NameNotFoundException e) {
+                return; // No package info : we can't do anything
+            }
+            final ProviderInfo[] providers = packageInfo.providers;
+            if (null == providers) return; // No providers : it is not a dictionary.
+
+            // Search for some dictionary pack in the just-installed package. If found, reread.
+            for (ProviderInfo info : providers) {
+                if (BinaryDictionary.DICTIONARY_PACK_AUTHORITY.equals(info.authority)) {
+                    mService.resetSuggestMainDict();
+                    return;
+                }
+            }
+            // If we come here none of the authorities matched the one we searched for.
+            // We can exit safely.
+            return;
+        } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
+                && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+            // When the dictionary package is removed, we need to reread dictionary (to use the
+            // next-priority one, or stop using a dictionary at all if this was the only one,
+            // since this is the user request).
+            // If we are replacing the package, we will receive ADDED right away so no need to
+            // remove the dictionary at the moment, since we will do it when we receive the
+            // ADDED broadcast.
+
+            // TODO: Only reload dictionary on REMOVED when the removed package is the one we
+            // read dictionary from?
+            mService.resetSuggestMainDict();
+        } else if (action.equals(NEW_DICTIONARY_INTENT_ACTION)) {
+            mService.resetSuggestMainDict();
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/EditingUtils.java b/java/src/com/android/inputmethod/latin/EditingUtils.java
index 90c250d..e56aa69 100644
--- a/java/src/com/android/inputmethod/latin/EditingUtils.java
+++ b/java/src/com/android/inputmethod/latin/EditingUtils.java
@@ -16,13 +16,13 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.compat.InputConnectionCompatUtils;
+
 import android.text.TextUtils;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.util.regex.Pattern;
 
 /**
@@ -34,11 +34,6 @@
      */
     private static final int LOOKBACK_CHARACTER_NUM = 15;
 
-    // Cache Method pointers
-    private static boolean sMethodsInitialized;
-    private static Method sMethodGetSelectedText;
-    private static Method sMethodSetComposingRegion;
-
     private EditingUtils() {
         // Unintentional empty constructor for singleton.
     }
@@ -78,7 +73,7 @@
 
     /**
      * @param connection connection to the current text field.
-     * @param sep characters which may separate words
+     * @param separators characters which may separate words
      * @return the word that surrounds the cursor, including up to one trailing
      *   separator. For example, if the field contains "he|llo world", where |
      *   represents the cursor, then "hello " will be returned.
@@ -166,23 +161,62 @@
 
     private static final Pattern spaceRegex = Pattern.compile("\\s+");
 
+
     public static CharSequence getPreviousWord(InputConnection connection,
             String sentenceSeperators) {
         //TODO: Should fix this. This could be slow!
         CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
-        if (prev == null) {
-            return null;
-        }
+        return getPreviousWord(prev, sentenceSeperators);
+    }
+
+    // Get the word before the whitespace preceding the non-whitespace preceding the cursor.
+    // Also, it won't return words that end in a separator.
+    // Example :
+    // "abc def|" -> abc
+    // "abc def |" -> abc
+    // "abc def. |" -> abc
+    // "abc def . |" -> def
+    // "abc|" -> null
+    // "abc |" -> null
+    // "abc. def|" -> null
+    public static CharSequence getPreviousWord(CharSequence prev, String sentenceSeperators) {
+        if (prev == null) return null;
         String[] w = spaceRegex.split(prev);
-        if (w.length >= 2 && w[w.length-2].length() > 0) {
-            char lastChar = w[w.length-2].charAt(w[w.length-2].length() -1);
-            if (sentenceSeperators.contains(String.valueOf(lastChar))) {
-                return null;
-            }
-            return w[w.length-2];
-        } else {
-            return null;
-        }
+
+        // If we can't find two words, or we found an empty word, return null.
+        if (w.length < 2 || w[w.length - 2].length() <= 0) return null;
+
+        // If ends in a separator, return null
+        char lastChar = w[w.length - 2].charAt(w[w.length - 2].length() - 1);
+        if (sentenceSeperators.contains(String.valueOf(lastChar))) return null;
+
+        return w[w.length - 2];
+    }
+
+    public static CharSequence getThisWord(InputConnection connection, String sentenceSeperators) {
+        final CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
+        return getThisWord(prev, sentenceSeperators);
+    }
+
+    // Get the word immediately before the cursor, even if there is whitespace between it and
+    // the cursor - but not if there is punctuation.
+    // Example :
+    // "abc def|" -> def
+    // "abc def |" -> def
+    // "abc def. |" -> null
+    // "abc def . |" -> null
+    public static CharSequence getThisWord(CharSequence prev, String sentenceSeperators) {
+        if (prev == null) return null;
+        String[] w = spaceRegex.split(prev);
+
+        // No word : return null
+        if (w.length < 1 || w[w.length - 1].length() <= 0) return null;
+
+        // If ends in a separator, return null
+        char lastChar = w[w.length - 1].charAt(w[w.length - 1].length() - 1);
+        if (sentenceSeperators.contains(String.valueOf(lastChar))) return null;
+
+        return w[w.length - 1];
     }
 
     public static class SelectedWord {
@@ -241,7 +275,8 @@
             }
 
             // Extract the selection alone
-            CharSequence touching = getSelectedText(ic, selStart, selEnd);
+            CharSequence touching = InputConnectionCompatUtils.getSelectedText(
+                    ic, selStart, selEnd);
             if (TextUtils.isEmpty(touching)) return null;
             // Is any part of the selection a separator? If so, return null.
             final int length = touching.length();
@@ -255,74 +290,4 @@
         }
         return null;
     }
-
-    /**
-     * Cache method pointers for performance
-     */
-    private static void initializeMethodsForReflection() {
-        try {
-            // These will either both exist or not, so no need for separate try/catch blocks.
-            // If other methods are added later, use separate try/catch blocks.
-            sMethodGetSelectedText = InputConnection.class.getMethod("getSelectedText", int.class);
-            sMethodSetComposingRegion = InputConnection.class.getMethod("setComposingRegion",
-                    int.class, int.class);
-        } catch (NoSuchMethodException exc) {
-            // Ignore
-        }
-        sMethodsInitialized = true;
-    }
-
-    /**
-     * Returns the selected text between the selStart and selEnd positions.
-     */
-    private static CharSequence getSelectedText(InputConnection ic, int selStart, int selEnd) {
-        // Use reflection, for backward compatibility
-        CharSequence result = null;
-        if (!sMethodsInitialized) {
-            initializeMethodsForReflection();
-        }
-        if (sMethodGetSelectedText != null) {
-            try {
-                result = (CharSequence) sMethodGetSelectedText.invoke(ic, 0);
-                return result;
-            } catch (InvocationTargetException exc) {
-                // Ignore
-            } catch (IllegalArgumentException e) {
-                // Ignore
-            } catch (IllegalAccessException e) {
-                // Ignore
-            }
-        }
-        // Reflection didn't work, try it the poor way, by moving the cursor to the start,
-        // getting the text after the cursor and moving the text back to selected mode.
-        // TODO: Verify that this works properly in conjunction with 
-        // LatinIME#onUpdateSelection
-        ic.setSelection(selStart, selEnd);
-        result = ic.getTextAfterCursor(selEnd - selStart, 0);
-        ic.setSelection(selStart, selEnd);
-        return result;
-    }
-
-    /**
-     * Tries to set the text into composition mode if there is support for it in the framework.
-     */
-    public static void underlineWord(InputConnection ic, SelectedWord word) {
-        // Use reflection, for backward compatibility
-        // If method not found, there's nothing we can do. It still works but just wont underline
-        // the word.
-        if (!sMethodsInitialized) {
-            initializeMethodsForReflection();
-        }
-        if (sMethodSetComposingRegion != null) {
-            try {
-                sMethodSetComposingRegion.invoke(ic, word.mStart, word.mEnd);
-            } catch (InvocationTargetException exc) {
-                // Ignore
-            } catch (IllegalArgumentException e) {
-                // Ignore
-            } catch (IllegalAccessException e) {
-                // Ignore
-            }
-        }
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index 0318175..97a4a18 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -19,6 +19,8 @@
 import android.content.Context;
 import android.os.AsyncTask;
 
+import com.android.inputmethod.keyboard.Keyboard;
+
 import java.util.LinkedList;
 
 /**
@@ -32,14 +34,14 @@
      */
     protected static final int MAX_WORD_LENGTH = 32;
 
+    // Bigram frequency is a fixed point number with 1 meaning 1.2 and 255 meaning 1.8.
+    protected static final int BIGRAM_MAX_FREQUENCY = 255;
+
     private Context mContext;
     private char[] mWordBuilder = new char[MAX_WORD_LENGTH];
     private int mDicTypeId;
     private int mMaxDepth;
     private int mInputLength;
-    private StringBuilder sb = new StringBuilder(MAX_WORD_LENGTH);
-
-    private static final char QUOTE = '\'';
 
     private boolean mRequiresReload;
 
@@ -98,6 +100,7 @@
 
         public int addFrequency(int add) {
             mFrequency += add;
+            if (mFrequency > BIGRAM_MAX_FREQUENCY) mFrequency = BIGRAM_MAX_FREQUENCY;
             return mFrequency;
         }
     }
@@ -226,6 +229,7 @@
      * Returns the word's frequency or -1 if not found
      */
     protected int getWordFrequency(CharSequence word) {
+        // Case-sensitive search
         Node node = searchNode(mRoots, word, 0, word.length());
         return (node == null) ? -1 : node.mFrequency;
     }
@@ -301,7 +305,8 @@
                     getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex,
                             skipPos, callback);
                 }
-            } else if ((c == QUOTE && currentChars[0] != QUOTE) || depth == skipPos) {
+            } else if ((c == Keyboard.CODE_SINGLE_QUOTE
+                    && currentChars[0] != Keyboard.CODE_SINGLE_QUOTE) || depth == skipPos) {
                 // Skip the ' and continue deeper
                 word[depth] = c;
                 if (children != null) {
@@ -327,7 +332,7 @@
                                     final int finalFreq;
                                     if (skipPos < 0) {
                                         finalFreq = freq * snr * addedAttenuation
-                                                * FULL_WORD_FREQ_MULTIPLIER;
+                                                * FULL_WORD_SCORE_MULTIPLIER;
                                     } else {
                                         finalFreq = computeSkippedWordFinalFreq(freq,
                                                 snr * addedAttenuation, mInputLength);
@@ -362,12 +367,16 @@
 
     /**
      * Adds bigrams to the in-memory trie structure that is being used to retrieve any word
-     * @param frequency frequency for this bigrams
-     * @param addFrequency if true, it adds to current frequency
+     * @param frequency frequency for this bigram
+     * @param addFrequency if true, it adds to current frequency, else it overwrites the old value
      * @return returns the final frequency
      */
     private int addOrSetBigram(String word1, String word2, int frequency, boolean addFrequency) {
-        Node firstWord = searchWord(mRoots, word1, 0, null);
+        // We don't want results to be different according to case of the looked up left hand side
+        // word. We do want however to return the correct case for the right hand side.
+        // So we want to squash the case of the left hand side, and preserve that of the right
+        // hand side word.
+        Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null);
         Node secondWord = searchWord(mRoots, word2, 0, null);
         LinkedList<NextWord> bigram = firstWord.mNGrams;
         if (bigram == null || bigram.size() == 0) {
@@ -433,8 +442,12 @@
         }
     }
 
-    private void runReverseLookUp(final CharSequence previousWord, final WordCallback callback) {
-        Node prevWord = searchNode(mRoots, previousWord, 0, previousWord.length());
+    private void runBigramReverseLookUp(final CharSequence previousWord,
+            final WordCallback callback) {
+        // Search for the lowercase version of the word only, because that's where bigrams
+        // store their sons.
+        Node prevWord = searchNode(mRoots, previousWord.toString().toLowerCase(), 0,
+                previousWord.length());
         if (prevWord != null && prevWord.mNGrams != null) {
             reverseLookUp(prevWord.mNGrams, callback);
         }
@@ -444,7 +457,7 @@
     public void getBigrams(final WordComposer codes, final CharSequence previousWord,
             final WordCallback callback) {
         if (!reloadDictionaryIfRequired()) {
-            runReverseLookUp(previousWord, callback);
+            runBigramReverseLookUp(previousWord, callback);
         }
     }
 
@@ -462,6 +475,9 @@
         }
     }
 
+    // Local to reverseLookUp, but do not allocate each time.
+    private final char[] mLookedUpString = new char[MAX_WORD_LENGTH];
+
     /**
      * reverseLookUp retrieves the full word given a list of terminal nodes and adds those words
      * through callback.
@@ -474,30 +490,33 @@
         for (NextWord nextWord : terminalNodes) {
             node = nextWord.mWord;
             freq = nextWord.getFrequency();
-            // TODO Not the best way to limit suggestion threshold
-            if (freq >= UserBigramDictionary.SUGGEST_THRESHOLD) {
-                sb.setLength(0);
-                do {
-                    sb.insert(0, node.mCode);
-                    node = node.mParent;
-                } while(node != null);
+            int index = MAX_WORD_LENGTH;
+            do {
+                --index;
+                mLookedUpString[index] = node.mCode;
+                node = node.mParent;
+            } while (node != null);
 
-                // TODO better way to feed char array?
-                callback.addWord(sb.toString().toCharArray(), 0, sb.length(), freq, mDicTypeId,
-                        DataType.BIGRAM);
-            }
+            callback.addWord(mLookedUpString, index, MAX_WORD_LENGTH - index, freq, mDicTypeId,
+                    DataType.BIGRAM);
         }
     }
 
     /**
-     * Search for the terminal node of the word
+     * Recursively search for the terminal node of the word.
+     *
+     * One iteration takes the full word to search for and the current index of the recursion.
+     *
+     * @param children the node of the trie to search under.
+     * @param word the word to search for. Only read [offset..length] so there may be trailing chars
+     * @param offset the index in {@code word} this recursion should operate on.
+     * @param length the length of the input word.
      * @return Returns the terminal node of the word if the word exists
      */
     private Node searchNode(final NodeArray children, final CharSequence word, final int offset,
             final int length) {
-        // TODO Consider combining with addWordRec
         final int count = children.mLength;
-        char currentChar = word.charAt(offset);
+        final char currentChar = word.charAt(offset);
         for (int j = 0; j < count; j++) {
             final Node node = children.mData[j];
             if (node.mCode == currentChar) {
diff --git a/java/src/com/android/inputmethod/latin/Flag.java b/java/src/com/android/inputmethod/latin/Flag.java
new file mode 100644
index 0000000..3cb8f7e
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/Flag.java
@@ -0,0 +1,64 @@
+/*
+ * 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.content.res.Resources;
+
+public class Flag {
+    public final String mName;
+    public final int mResource;
+    public final int mMask;
+    public final int mSource;
+
+    static private final int SOURCE_CONFIG = 1;
+    static private final int SOURCE_EXTRAVALUE = 2;
+
+    public Flag(int resourceId, int mask) {
+        mName = null;
+        mResource = resourceId;
+        mSource = SOURCE_CONFIG;
+        mMask = mask;
+    }
+
+    public Flag(String name, int mask) {
+        mName = name;
+        mResource = 0;
+        mSource = SOURCE_EXTRAVALUE;
+        mMask = mask;
+    }
+
+    // If context/switcher are null, set all related flags in flagArray to on.
+    public static int initFlags(Flag[] flagArray, Context context, SubtypeSwitcher switcher) {
+        int flags = 0;
+        final Resources res = null == context ? null : context.getResources();
+        for (Flag entry : flagArray) {
+            switch (entry.mSource) {
+                case Flag.SOURCE_CONFIG:
+                    if (res == null || res.getBoolean(entry.mResource))
+                        flags |= entry.mMask;
+                    break;
+                case Flag.SOURCE_EXTRAVALUE:
+                    if (switcher == null ||
+                            switcher.currentSubtypeContainsExtraValueKey(entry.mName))
+                        flags |= entry.mMask;
+                    break;
+            }
+        }
+        return flags;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
deleted file mode 100644
index b58a57e..0000000
--- a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2008-2009 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.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.preference.CheckBoxPreference;
-import android.preference.PreferenceActivity;
-import android.preference.PreferenceGroup;
-import android.preference.PreferenceManager;
-import android.text.TextUtils;
-
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Locale;
-
-public class InputLanguageSelection extends PreferenceActivity {
-
-    private SharedPreferences mPrefs;
-    private String mSelectedLanguages;
-    private ArrayList<Loc> mAvailableLanguages = new ArrayList<Loc>();
-    private static final String[] BLACKLIST_LANGUAGES = {
-        "ko", "ja", "zh", "el", "zz"
-    };
-
-    private static class Loc implements Comparable<Object> {
-        private static Collator sCollator = Collator.getInstance();
-
-        private String mLabel;
-        public final Locale mLocale;
-
-        public Loc(String label, Locale locale) {
-            this.mLabel = label;
-            this.mLocale = locale;
-        }
-
-        public void setLabel(String label) {
-            this.mLabel = label;
-        }
-
-        @Override
-        public String toString() {
-            return this.mLabel;
-        }
-
-        @Override
-        public int compareTo(Object o) {
-            return sCollator.compare(this.mLabel, ((Loc) o).mLabel);
-        }
-    }
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        addPreferencesFromResource(R.xml.language_prefs);
-        // Get the settings preferences
-        mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
-        mSelectedLanguages = mPrefs.getString(Settings.PREF_SELECTED_LANGUAGES, "");
-        String[] languageList = mSelectedLanguages.split(",");
-        mAvailableLanguages = getUniqueLocales();
-        PreferenceGroup parent = getPreferenceScreen();
-        for (int i = 0; i < mAvailableLanguages.size(); i++) {
-            CheckBoxPreference pref = new CheckBoxPreference(this);
-            Locale locale = mAvailableLanguages.get(i).mLocale;
-            pref.setTitle(SubtypeSwitcher.getFullDisplayName(locale, true));
-            boolean checked = isLocaleIn(locale, languageList);
-            pref.setChecked(checked);
-            if (hasDictionary(locale)) {
-                pref.setSummary(R.string.has_dictionary);
-            }
-            parent.addPreference(pref);
-        }
-    }
-
-    private boolean isLocaleIn(Locale locale, String[] list) {
-        String lang = get5Code(locale);
-        for (int i = 0; i < list.length; i++) {
-            if (lang.equalsIgnoreCase(list[i])) return true;
-        }
-        return false;
-    }
-
-    private boolean hasDictionary(Locale locale) {
-        final Resources res = getResources();
-        final Configuration conf = res.getConfiguration();
-        final Locale saveLocale = conf.locale;
-        boolean haveDictionary = false;
-        conf.locale = locale;
-        res.updateConfiguration(conf, res.getDisplayMetrics());
-
-        int mainDicResId = Utils.getMainDictionaryResourceId(res);
-        BinaryDictionary bd = BinaryDictionary.initDictionary(this, mainDicResId, Suggest.DIC_MAIN);
-
-        // Is the dictionary larger than a placeholder? Arbitrarily chose a lower limit of
-        // 4000-5000 words, whereas the LARGE_DICTIONARY is about 20000+ words.
-        if (bd.getSize() > Suggest.LARGE_DICTIONARY_THRESHOLD / 4) {
-            haveDictionary = true;
-        }
-        bd.close();
-        conf.locale = saveLocale;
-        res.updateConfiguration(conf, res.getDisplayMetrics());
-        return haveDictionary;
-    }
-
-    private String get5Code(Locale locale) {
-        String country = locale.getCountry();
-        return locale.getLanguage()
-                + (TextUtils.isEmpty(country) ? "" : "_" + country);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        // Save the selected languages
-        String checkedLanguages = "";
-        PreferenceGroup parent = getPreferenceScreen();
-        int count = parent.getPreferenceCount();
-        for (int i = 0; i < count; i++) {
-            CheckBoxPreference pref = (CheckBoxPreference) parent.getPreference(i);
-            if (pref.isChecked()) {
-                Locale locale = mAvailableLanguages.get(i).mLocale;
-                checkedLanguages += get5Code(locale) + ",";
-            }
-        }
-        if (checkedLanguages.length() < 1) checkedLanguages = null; // Save null
-        Editor editor = mPrefs.edit();
-        editor.putString(Settings.PREF_SELECTED_LANGUAGES, checkedLanguages);
-        SharedPreferencesCompat.apply(editor);
-    }
-
-    public ArrayList<Loc> getUniqueLocales() {
-        String[] locales = getAssets().getLocales();
-        Arrays.sort(locales);
-        ArrayList<Loc> uniqueLocales = new ArrayList<Loc>();
-
-        final int origSize = locales.length;
-        Loc[] preprocess = new Loc[origSize];
-        int finalSize = 0;
-        for (int i = 0 ; i < origSize; i++ ) {
-            String s = locales[i];
-            int len = s.length();
-            if (len == 5) {
-                String language = s.substring(0, 2);
-                String country = s.substring(3, 5);
-                Locale l = new Locale(language, country);
-
-                // Exclude languages that are not relevant to LatinIME
-                if (arrayContains(BLACKLIST_LANGUAGES, language)) continue;
-
-                if (finalSize == 0) {
-                    preprocess[finalSize++] =
-                            new Loc(SubtypeSwitcher.getFullDisplayName(l, true), l);
-                } else {
-                    // check previous entry:
-                    //  same lang and a country -> upgrade to full name and
-                    //    insert ours with full name
-                    //  diff lang -> insert ours with lang-only name
-                    if (preprocess[finalSize-1].mLocale.getLanguage().equals(
-                            language)) {
-                        preprocess[finalSize-1].setLabel(SubtypeSwitcher.getFullDisplayName(
-                                preprocess[finalSize-1].mLocale, false));
-                        preprocess[finalSize++] =
-                                new Loc(SubtypeSwitcher.getFullDisplayName(l, false), l);
-                    } else {
-                        String displayName;
-                        if (s.equals("zz_ZZ")) {
-                            // ignore this locale
-                        } else {
-                            displayName = SubtypeSwitcher.getFullDisplayName(l, true);
-                            preprocess[finalSize++] = new Loc(displayName, l);
-                        }
-                    }
-                }
-            }
-        }
-        for (int i = 0; i < finalSize ; i++) {
-            uniqueLocales.add(preprocess[i]);
-        }
-        return uniqueLocales;
-    }
-
-    private boolean arrayContains(String[] array, String value) {
-        for (int i = 0; i < array.length; i++) {
-            if (array[i].equalsIgnoreCase(value)) return true;
-        }
-        return false;
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index bf831bf..9c6465d 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -33,7 +33,6 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.SystemClock;
-import android.os.Vibrator;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceManager;
 import android.text.InputType;
@@ -42,46 +41,45 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
-import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-import android.widget.FrameLayout;
-import android.widget.HorizontalScrollView;
-import android.widget.LinearLayout;
 
+import com.android.inputmethod.accessibility.AccessibilityUtils;
+import com.android.inputmethod.compat.CompatUtils;
+import com.android.inputmethod.compat.EditorInfoCompatUtils;
+import com.android.inputmethod.compat.InputConnectionCompatUtils;
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
+import com.android.inputmethod.compat.InputTypeCompatUtils;
+import com.android.inputmethod.compat.SuggestionSpanUtils;
+import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
+import com.android.inputmethod.deprecated.VoiceProxy;
+import com.android.inputmethod.deprecated.recorrection.Recorrection;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardActionListener;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.KeyboardView;
 import com.android.inputmethod.keyboard.LatinKeyboard;
 import com.android.inputmethod.keyboard.LatinKeyboardView;
-import com.android.inputmethod.latin.Utils.RingCharBuffer;
-import com.android.inputmethod.voice.VoiceIMEConnector;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Locale;
 
 /**
  * Input method implementation for Qwerty'ish keyboard.
  */
-public class LatinIME extends InputMethodService implements KeyboardActionListener {
+public class LatinIME extends InputMethodServiceCompatWrapper implements KeyboardActionListener,
+        CandidateView.Listener {
     private static final String TAG = LatinIME.class.getSimpleName();
     private static final boolean PERF_DEBUG = false;
     private static final boolean TRACE = false;
@@ -94,6 +92,7 @@
      *
      * @deprecated Use {@link LatinIME#IME_OPTION_NO_MICROPHONE} with package name prefixed.
      */
+    @SuppressWarnings("dep-ann")
     public static final String IME_OPTION_NO_MICROPHONE_COMPAT = "nm";
 
     /**
@@ -109,9 +108,6 @@
      */
     public static final String IME_OPTION_NO_SETTINGS_KEY = "noSettingsKey";
 
-    private static final int DELAY_UPDATE_SUGGESTIONS = 180;
-    private static final int DELAY_UPDATE_OLD_SUGGESTIONS = 300;
-    private static final int DELAY_UPDATE_SHIFT_STATE = 300;
     private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100;
 
     // How many continuous deletes at which to start deleting at a higher speed.
@@ -119,6 +115,12 @@
     // Key events coming any faster than this are long-presses.
     private static final int QUICK_PRESS = 200;
 
+    /**
+     * The name of the scheme used by the Package Manager to warn of a new package installation,
+     * replacement or removal.
+     */
+    private static final String SCHEME_PACKAGE = "package";
+
     private int mSuggestionVisibility;
     private static final int SUGGESTION_VISIBILILTY_SHOW_VALUE
             = R.string.prefs_suggestion_visibility_show_value;
@@ -133,55 +135,47 @@
         SUGGESTION_VISIBILILTY_HIDE_VALUE
     };
 
+    private Settings.Values mSettingsValues;
+
     private View mCandidateViewContainer;
+    private int mCandidateStripHeight;
     private CandidateView mCandidateView;
     private Suggest mSuggest;
     private CompletionInfo[] mApplicationSpecifiedCompletions;
 
     private AlertDialog mOptionsDialog;
 
-    private InputMethodManager mImm;
+    private InputMethodManagerCompatWrapper mImm;
     private Resources mResources;
     private SharedPreferences mPrefs;
     private String mInputMethodId;
     private KeyboardSwitcher mKeyboardSwitcher;
     private SubtypeSwitcher mSubtypeSwitcher;
-    private VoiceIMEConnector mVoiceConnector;
+    private VoiceProxy mVoiceProxy;
+    private Recorrection mRecorrection;
 
     private UserDictionary mUserDictionary;
     private UserBigramDictionary mUserBigramDictionary;
-    private ContactsDictionary mContactsDictionary;
     private AutoDictionary mAutoDictionary;
 
+    // TODO: Create an inner class to group options and pseudo-options to improve readability.
     // These variables are initialized according to the {@link EditorInfo#inputType}.
-    private boolean mAutoSpace;
+    private boolean mShouldInsertMagicSpace;
     private boolean mInputTypeNoAutoCorrect;
     private boolean mIsSettingsSuggestionStripOn;
     private boolean mApplicationSpecifiedCompletionOn;
 
-    private AccessibilityUtils mAccessibilityUtils;
-
     private final StringBuilder mComposing = new StringBuilder();
     private WordComposer mWord = new WordComposer();
     private CharSequence mBestWord;
-    private boolean mHasValidSuggestions;
+    private boolean mHasUncommittedTypedChars;
     private boolean mHasDictionary;
-    private boolean mJustAddedAutoSpace;
-    private boolean mAutoCorrectEnabled;
-    private boolean mRecorrectionEnabled;
-    private boolean mBigramSuggestionEnabled;
-    private boolean mAutoCorrectOn;
-    private boolean mVibrateOn;
-    private boolean mSoundOn;
-    private boolean mPopupOn;
-    private boolean mAutoCap;
-    private boolean mQuickFixes;
-    private boolean mConfigEnableShowSubtypeSettings;
-    private boolean mConfigSwipeDownDismissKeyboardEnabled;
-    private int mConfigDelayBeforeFadeoutLanguageOnSpacebar;
-    private int mConfigDurationOfFadeoutLanguageOnSpacebar;
-    private float mConfigFinalFadeoutFactorOfLanguageOnSpacebar;
-    private long mConfigDoubleSpacesTurnIntoPeriodTimeout;
+    // Magic space: a space that should disappear on space/apostrophe insertion, move after the
+    // punctuation on punctuation insertion, and become a real space on alpha char insertion.
+    private boolean mJustAddedMagicSpace; // This indicates whether the last char is a magic space.
+    // This indicates whether the last keypress resulted in processing of double space replacement
+    // with period-space.
+    private boolean mJustReplacedDoubleSpace;
 
     private int mCorrectionMode;
     private int mCommittedLength;
@@ -189,76 +183,28 @@
     // Keep track of the last selection range to decide if we need to show word alternatives
     private int mLastSelectionStart;
     private int mLastSelectionEnd;
-    private SuggestedWords mSuggestPuncList;
 
-    // Indicates whether the suggestion strip is to be on in landscape
-    private boolean mJustAccepted;
+    // Whether we are expecting an onUpdateSelection event to fire. If it does when we don't
+    // "expect" it, it means the user actually moved the cursor.
+    private boolean mExpectingUpdateSelection;
     private int mDeleteCount;
     private long mLastKeyTime;
 
     private AudioManager mAudioManager;
     // Align sound effect volume on music volume
     private static final float FX_VOLUME = -1.0f;
-    private boolean mSilentMode;
+    private boolean mSilentModeOn; // System-wide current configuration
 
-    /* package */ String mWordSeparators;
-    private String mSentenceSeparators;
-    private String mSuggestPuncs;
-    // TODO: Move this flag to VoiceIMEConnector
+    // TODO: Move this flag to VoiceProxy
     private boolean mConfigurationChanging;
 
+    // Object for reacting to adding/removing a dictionary pack.
+    private BroadcastReceiver mDictionaryPackInstallReceiver =
+            new DictionaryPackInstallBroadcastReceiver(this);
+
     // Keeps track of most recently inserted text (multi-character key) for reverting
     private CharSequence mEnteredText;
 
-    private final ArrayList<WordAlternatives> mWordHistory = new ArrayList<WordAlternatives>();
-
-    public abstract static class WordAlternatives {
-        protected CharSequence mChosenWord;
-
-        public WordAlternatives() {
-            // Nothing
-        }
-
-        public WordAlternatives(CharSequence chosenWord) {
-            mChosenWord = chosenWord;
-        }
-
-        @Override
-        public int hashCode() {
-            return mChosenWord.hashCode();
-        }
-
-        public abstract CharSequence getOriginalWord();
-
-        public CharSequence getChosenWord() {
-            return mChosenWord;
-        }
-
-        public abstract SuggestedWords.Builder getAlternatives();
-    }
-
-    public class TypedWordAlternatives extends WordAlternatives {
-        private WordComposer word;
-
-        public TypedWordAlternatives() {
-            // Nothing
-        }
-
-        public TypedWordAlternatives(CharSequence chosenWord, WordComposer wordComposer) {
-            super(chosenWord);
-            word = wordComposer;
-        }
-
-        @Override
-        public CharSequence getOriginalWord() {
-            return word.getTypedWord();
-        }
-
-        @Override
-        public SuggestedWords.Builder getAlternatives() {
-            return getTypedSuggestions(word);
-        }
-    }
 
     public final UIHandler mHandler = new UIHandler();
 
@@ -270,44 +216,54 @@
         private static final int MSG_FADEOUT_LANGUAGE_ON_SPACEBAR = 4;
         private static final int MSG_DISMISS_LANGUAGE_ON_SPACEBAR = 5;
         private static final int MSG_SPACE_TYPED = 6;
+        private static final int MSG_SET_BIGRAM_PREDICTIONS = 7;
 
         @Override
         public void handleMessage(Message msg) {
             final KeyboardSwitcher switcher = mKeyboardSwitcher;
-            final LatinKeyboardView inputView = switcher.getInputView();
+            final LatinKeyboardView inputView = switcher.getKeyboardView();
             switch (msg.what) {
             case MSG_UPDATE_SUGGESTIONS:
                 updateSuggestions();
                 break;
             case MSG_UPDATE_OLD_SUGGESTIONS:
-                setOldSuggestions();
+                mRecorrection.fetchAndDisplayRecorrectionSuggestions(mVoiceProxy, mCandidateView,
+                        mSuggest, mKeyboardSwitcher, mWord, mHasUncommittedTypedChars,
+                        mLastSelectionStart, mLastSelectionEnd, mSettingsValues.mWordSeparators);
                 break;
             case MSG_UPDATE_SHIFT_STATE:
                 switcher.updateShiftState();
                 break;
+            case MSG_SET_BIGRAM_PREDICTIONS:
+                updateBigramPredictions();
+                break;
             case MSG_VOICE_RESULTS:
-                mVoiceConnector.handleVoiceResults(preferCapitalization()
+                mVoiceProxy.handleVoiceResults(preferCapitalization()
                         || (switcher.isAlphabetMode() && switcher.isShiftedOrShiftLocked()));
                 break;
             case MSG_FADEOUT_LANGUAGE_ON_SPACEBAR:
-                if (inputView != null)
+                if (inputView != null) {
                     inputView.setSpacebarTextFadeFactor(
-                            (1.0f + mConfigFinalFadeoutFactorOfLanguageOnSpacebar) / 2,
+                            (1.0f + mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar) / 2,
                             (LatinKeyboard)msg.obj);
+                }
                 sendMessageDelayed(obtainMessage(MSG_DISMISS_LANGUAGE_ON_SPACEBAR, msg.obj),
-                        mConfigDurationOfFadeoutLanguageOnSpacebar);
+                        mSettingsValues.mDurationOfFadeoutLanguageOnSpacebar);
                 break;
             case MSG_DISMISS_LANGUAGE_ON_SPACEBAR:
-                if (inputView != null)
+                if (inputView != null) {
                     inputView.setSpacebarTextFadeFactor(
-                            mConfigFinalFadeoutFactorOfLanguageOnSpacebar, (LatinKeyboard)msg.obj);
+                            mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar,
+                            (LatinKeyboard)msg.obj);
+                }
                 break;
             }
         }
 
         public void postUpdateSuggestions() {
             removeMessages(MSG_UPDATE_SUGGESTIONS);
-            sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS), DELAY_UPDATE_SUGGESTIONS);
+            sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS),
+                    mSettingsValues.mDelayUpdateSuggestions);
         }
 
         public void cancelUpdateSuggestions() {
@@ -321,7 +277,7 @@
         public void postUpdateOldSuggestions() {
             removeMessages(MSG_UPDATE_OLD_SUGGESTIONS);
             sendMessageDelayed(obtainMessage(MSG_UPDATE_OLD_SUGGESTIONS),
-                    DELAY_UPDATE_OLD_SUGGESTIONS);
+                    mSettingsValues.mDelayUpdateOldSuggestions);
         }
 
         public void cancelUpdateOldSuggestions() {
@@ -330,13 +286,24 @@
 
         public void postUpdateShiftKeyState() {
             removeMessages(MSG_UPDATE_SHIFT_STATE);
-            sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), DELAY_UPDATE_SHIFT_STATE);
+            sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE),
+                    mSettingsValues.mDelayUpdateShiftState);
         }
 
         public void cancelUpdateShiftState() {
             removeMessages(MSG_UPDATE_SHIFT_STATE);
         }
 
+        public void postUpdateBigramPredictions() {
+            removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
+            sendMessageDelayed(obtainMessage(MSG_SET_BIGRAM_PREDICTIONS),
+                    mSettingsValues.mDelayUpdateSuggestions);
+        }
+
+        public void cancelUpdateBigramPredictions() {
+            removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
+        }
+
         public void updateVoiceResults() {
             sendMessage(obtainMessage(MSG_VOICE_RESULTS));
         }
@@ -344,17 +311,21 @@
         public void startDisplayLanguageOnSpacebar(boolean localeChanged) {
             removeMessages(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR);
             removeMessages(MSG_DISMISS_LANGUAGE_ON_SPACEBAR);
-            final LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
+            final LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
             if (inputView != null) {
                 final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard();
-                // The language is never displayed when the delay is zero.
-                if (mConfigDelayBeforeFadeoutLanguageOnSpacebar != 0)
-                    inputView.setSpacebarTextFadeFactor(localeChanged ? 1.0f
-                            : mConfigFinalFadeoutFactorOfLanguageOnSpacebar, keyboard);
                 // The language is always displayed when the delay is negative.
-                if (localeChanged && mConfigDelayBeforeFadeoutLanguageOnSpacebar > 0) {
+                final boolean needsToDisplayLanguage = localeChanged
+                        || mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar < 0;
+                // The language is never displayed when the delay is zero.
+                if (mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar != 0) {
+                    inputView.setSpacebarTextFadeFactor(needsToDisplayLanguage ? 1.0f
+                            : mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar, keyboard);
+                }
+                // The fadeout animation will start when the delay is positive.
+                if (localeChanged && mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar > 0) {
                     sendMessageDelayed(obtainMessage(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR, keyboard),
-                            mConfigDelayBeforeFadeoutLanguageOnSpacebar);
+                            mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar);
                 }
             }
         }
@@ -362,7 +333,7 @@
         public void startDoubleSpacesTimer() {
             removeMessages(MSG_SPACE_TYPED);
             sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED),
-                    mConfigDoubleSpacesTurnIntoPeriodTimeout);
+                    mSettingsValues.mDoubleSpacesTurnIntoPeriodTimeout);
         }
 
         public void cancelDoubleSpacesTimer() {
@@ -379,44 +350,26 @@
         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
         mPrefs = prefs;
         LatinImeLogger.init(this, prefs);
+        LanguageSwitcherProxy.init(this, prefs);
         SubtypeSwitcher.init(this, prefs);
         KeyboardSwitcher.init(this, prefs);
+        Recorrection.init(this, prefs);
         AccessibilityUtils.init(this, prefs);
 
         super.onCreate();
 
-        mImm = ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE));
+        mImm = InputMethodManagerCompatWrapper.getInstance(this);
         mInputMethodId = Utils.getInputMethodId(mImm, getPackageName());
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         mKeyboardSwitcher = KeyboardSwitcher.getInstance();
-        mAccessibilityUtils = AccessibilityUtils.getInstance();
+        mRecorrection = Recorrection.getInstance();
         DEBUG = LatinImeLogger.sDBG;
 
+        loadSettings();
+
         final Resources res = getResources();
         mResources = res;
 
-        // If the option should not be shown, do not read the recorrection preference
-        // but always use the default setting defined in the resources.
-        if (res.getBoolean(R.bool.config_enable_show_recorrection_option)) {
-            mRecorrectionEnabled = prefs.getBoolean(Settings.PREF_RECORRECTION_ENABLED,
-                    res.getBoolean(R.bool.config_default_recorrection_enabled));
-        } else {
-            mRecorrectionEnabled = res.getBoolean(R.bool.config_default_recorrection_enabled);
-        }
-
-        mConfigEnableShowSubtypeSettings = res.getBoolean(
-                R.bool.config_enable_show_subtype_settings);
-        mConfigSwipeDownDismissKeyboardEnabled = res.getBoolean(
-                R.bool.config_swipe_down_dismiss_keyboard_enabled);
-        mConfigDelayBeforeFadeoutLanguageOnSpacebar = res.getInteger(
-                R.integer.config_delay_before_fadeout_language_on_spacebar);
-        mConfigDurationOfFadeoutLanguageOnSpacebar = res.getInteger(
-                R.integer.config_duration_of_fadeout_language_on_spacebar);
-        mConfigFinalFadeoutFactorOfLanguageOnSpacebar = res.getInteger(
-                R.integer.config_final_fadeout_percentage_of_language_on_spacebar) / 100.0f;
-        mConfigDoubleSpacesTurnIntoPeriodTimeout = res.getInteger(
-                R.integer.config_double_spaces_turn_into_period_timeout);
-
         Utils.GCUtils.getInstance().reset();
         boolean tryGC = true;
         for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
@@ -429,49 +382,80 @@
         }
 
         mOrientation = res.getConfiguration().orientation;
-        initSuggestPuncList();
 
-        // register to receive ringer mode change and network state change.
+        // Register to receive ringer mode change and network state change.
+        // Also receive installation and removal of a dictionary pack.
         final IntentFilter filter = new IntentFilter();
         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         registerReceiver(mReceiver, filter);
-        mVoiceConnector = VoiceIMEConnector.init(this, prefs, mHandler);
+        mVoiceProxy = VoiceProxy.init(this, prefs, mHandler);
+
+        final IntentFilter packageFilter = new IntentFilter();
+        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        packageFilter.addDataScheme(SCHEME_PACKAGE);
+        registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
+
+        final IntentFilter newDictFilter = new IntentFilter();
+        newDictFilter.addAction(
+                DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION);
+        registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
+    }
+
+    // Has to be package-visible for unit tests
+    /* package */ void loadSettings() {
+        if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+        if (null == mSubtypeSwitcher) mSubtypeSwitcher = SubtypeSwitcher.getInstance();
+        mSettingsValues = new Settings.Values(mPrefs, this, mSubtypeSwitcher.getInputLocaleStr());
+        resetContactsDictionary();
     }
 
     private void initSuggest() {
-        String locale = mSubtypeSwitcher.getInputLocaleStr();
+        final String localeStr = mSubtypeSwitcher.getInputLocaleStr();
+        final Locale keyboardLocale = Utils.constructLocaleFromString(localeStr);
 
-        Locale savedLocale = mSubtypeSwitcher.changeSystemLocale(new Locale(locale));
+        final Resources res = mResources;
+        final Locale savedLocale = Utils.setSystemLocale(res, keyboardLocale);
         if (mSuggest != null) {
             mSuggest.close();
         }
-        final SharedPreferences prefs = mPrefs;
-        mQuickFixes = isQuickFixesEnabled(prefs);
 
-        final Resources res = mResources;
         int mainDicResId = Utils.getMainDictionaryResourceId(res);
-        mSuggest = new Suggest(this, mainDicResId);
-        loadAndSetAutoCorrectionThreshold(prefs);
+        mSuggest = new Suggest(this, mainDicResId, keyboardLocale);
+        if (mSettingsValues.mAutoCorrectEnabled) {
+            mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
+        }
         updateAutoTextEnabled();
 
-        mUserDictionary = new UserDictionary(this, locale);
+        mUserDictionary = new UserDictionary(this, localeStr);
         mSuggest.setUserDictionary(mUserDictionary);
 
-        mContactsDictionary = new ContactsDictionary(this, Suggest.DIC_CONTACTS);
-        mSuggest.setContactsDictionary(mContactsDictionary);
+        resetContactsDictionary();
 
-        mAutoDictionary = new AutoDictionary(this, this, locale, Suggest.DIC_AUTO);
+        mAutoDictionary = new AutoDictionary(this, this, localeStr, Suggest.DIC_AUTO);
         mSuggest.setAutoDictionary(mAutoDictionary);
 
-        mUserBigramDictionary = new UserBigramDictionary(this, this, locale, Suggest.DIC_USER);
+        mUserBigramDictionary = new UserBigramDictionary(this, this, localeStr, Suggest.DIC_USER);
         mSuggest.setUserBigramDictionary(mUserBigramDictionary);
 
         updateCorrectionMode();
-        mWordSeparators = res.getString(R.string.word_separators);
-        mSentenceSeparators = res.getString(R.string.sentence_separators);
 
-        mSubtypeSwitcher.changeSystemLocale(savedLocale);
+        Utils.setSystemLocale(res, savedLocale);
+    }
+
+    private void resetContactsDictionary() {
+        if (null == mSuggest) return;
+        ContactsDictionary contactsDictionary = mSettingsValues.mUseContactsDict
+                ? new ContactsDictionary(this, Suggest.DIC_CONTACTS) : null;
+        mSuggest.setContactsDictionary(contactsDictionary);
+    }
+
+    /* package private */ void resetSuggestMainDict() {
+        final String localeStr = mSubtypeSwitcher.getInputLocaleStr();
+        final Locale keyboardLocale = Utils.constructLocaleFromString(localeStr);
+        int mainDicResId = Utils.getMainDictionaryResourceId(mResources);
+        mSuggest.resetMainDict(this, mainDicResId, keyboardLocale);
     }
 
     @Override
@@ -481,7 +465,8 @@
             mSuggest = null;
         }
         unregisterReceiver(mReceiver);
-        mVoiceConnector.destroy();
+        unregisterReceiver(mDictionaryPackInstallReceiver);
+        mVoiceProxy.destroy();
         LatinImeLogger.commit();
         LatinImeLogger.onDestroy();
         super.onDestroy();
@@ -502,8 +487,11 @@
 
         mConfigurationChanging = true;
         super.onConfigurationChanged(conf);
-        mVoiceConnector.onConfigurationChanged(conf);
+        mVoiceProxy.onConfigurationChanged(conf);
         mConfigurationChanging = false;
+
+        // This will work only when the subtype is not supported.
+        LanguageSwitcherProxy.onConfigurationChanged(conf);
     }
 
     @Override
@@ -512,30 +500,29 @@
     }
 
     @Override
-    public View onCreateCandidatesView() {
-        LayoutInflater inflater = getLayoutInflater();
-        LinearLayout container = (LinearLayout)inflater.inflate(R.layout.candidates, null);
-        mCandidateViewContainer = container;
-        if (container.getPaddingRight() != 0) {
-            HorizontalScrollView scrollView =
-                    (HorizontalScrollView) container.findViewById(R.id.candidates_scroll_view);
-            scrollView.setOverScrollMode(View.OVER_SCROLL_NEVER);
-            container.setGravity(Gravity.CENTER_HORIZONTAL);
-        }
-        mCandidateView = (CandidateView) container.findViewById(R.id.candidates);
-        mCandidateView.setService(this);
-        setCandidatesViewShown(true);
-        return container;
+    public void setInputView(View view) {
+        super.setInputView(view);
+        mCandidateViewContainer = view.findViewById(R.id.candidates_container);
+        mCandidateView = (CandidateView) view.findViewById(R.id.candidates);
+        mCandidateView.setListener(this, view);
+        mCandidateStripHeight = (int)mResources.getDimension(R.dimen.candidate_strip_height);
+    }
+
+    @Override
+    public void setCandidatesView(View view) {
+        // To ensure that CandidatesView will never be set.
+        return;
     }
 
     @Override
     public void onStartInputView(EditorInfo attribute, boolean restarting) {
         final KeyboardSwitcher switcher = mKeyboardSwitcher;
-        LatinKeyboardView inputView = switcher.getInputView();
+        LatinKeyboardView inputView = switcher.getKeyboardView();
 
         if (DEBUG) {
-            Log.d(TAG, "onStartInputView: inputType=" + ((attribute == null) ? "none"
-                    : String.format("0x%08x", attribute.inputType)));
+            Log.d(TAG, "onStartInputView: attribute:" + ((attribute == null) ? "none"
+                    : String.format("inputType=0x%08x imeOptions=0x%08x",
+                            attribute.inputType, attribute.imeOptions)));
         }
         // In landscape mode, this method gets called without the input view being created.
         if (inputView == null) {
@@ -549,20 +536,32 @@
         // Most such things we decide below in initializeInputAttributesAndGetMode, but we need to
         // know now whether this is a password text field, because we need to know now whether we
         // want to enable the voice button.
-        final VoiceIMEConnector voiceIme = mVoiceConnector;
-        voiceIme.resetVoiceStates(Utils.isPasswordInputType(attribute.inputType)
-                || Utils.isVisiblePasswordInputType(attribute.inputType));
+        final VoiceProxy voiceIme = mVoiceProxy;
+        voiceIme.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(attribute.inputType)
+                || InputTypeCompatUtils.isVisiblePasswordInputType(attribute.inputType));
 
         initializeInputAttributes(attribute);
 
         inputView.closing();
         mEnteredText = null;
         mComposing.setLength(0);
-        mHasValidSuggestions = false;
+        mHasUncommittedTypedChars = false;
         mDeleteCount = 0;
-        mJustAddedAutoSpace = false;
+        mJustAddedMagicSpace = false;
+        mJustReplacedDoubleSpace = false;
 
-        loadSettings(attribute);
+        loadSettings();
+        updateCorrectionMode();
+        updateAutoTextEnabled();
+        updateSuggestionVisibility(mPrefs, mResources);
+
+        if (mSuggest != null && mSettingsValues.mAutoCorrectEnabled) {
+            mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
+         }
+        mVoiceProxy.loadSettings(attribute, mPrefs);
+        // This will work only when the subtype is not supported.
+        LanguageSwitcherProxy.loadSettings();
+
         if (mSubtypeSwitcher.isKeyboardMode()) {
             switcher.loadKeyboard(attribute,
                     mSubtypeSwitcher.isShortcutImeEnabled() && voiceIme.isVoiceButtonEnabled(),
@@ -570,20 +569,17 @@
             switcher.updateShiftState();
         }
 
-        setCandidatesViewShownInternal(isCandidateStripVisible(),
-                false /* needsInputViewShown */ );
+        setSuggestionStripShownInternal(isCandidateStripVisible(), /* needsInputViewShown */ false);
         // Delay updating suggestions because keyboard input view may not be shown at this point.
         mHandler.postUpdateSuggestions();
 
         updateCorrectionMode();
 
-        final boolean accessibilityEnabled = mAccessibilityUtils.isAccessibilityEnabled();
-
-        inputView.setPreviewEnabled(mPopupOn);
+        inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn,
+                mSettingsValues.mKeyPreviewPopupDismissDelay);
         inputView.setProximityCorrectionEnabled(true);
-        inputView.setAccessibilityEnabled(accessibilityEnabled);
         // If we just entered a text field, maybe it has some old text that requires correction
-        checkRecorrectionOnStart();
+        mRecorrection.checkRecorrectionOnStart();
         inputView.setForeground(true);
 
         voiceIme.onStartInputView(inputView.getWindowToken());
@@ -596,7 +592,7 @@
             return;
         final int inputType = attribute.inputType;
         final int variation = inputType & InputType.TYPE_MASK_VARIATION;
-        mAutoSpace = false;
+        mShouldInsertMagicSpace = false;
         mInputTypeNoAutoCorrect = false;
         mIsSettingsSuggestionStripOn = false;
         mApplicationSpecifiedCompletionOn = false;
@@ -605,17 +601,17 @@
         if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
             mIsSettingsSuggestionStripOn = true;
             // Make sure that passwords are not displayed in candidate view
-            if (Utils.isPasswordInputType(inputType)
-                    || Utils.isVisiblePasswordInputType(inputType)) {
+            if (InputTypeCompatUtils.isPasswordInputType(inputType)
+                    || InputTypeCompatUtils.isVisiblePasswordInputType(inputType)) {
                 mIsSettingsSuggestionStripOn = false;
             }
-            if (Utils.isEmailVariation(variation)
+            if (InputTypeCompatUtils.isEmailVariation(variation)
                     || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) {
-                mAutoSpace = false;
+                mShouldInsertMagicSpace = false;
             } else {
-                mAutoSpace = true;
+                mShouldInsertMagicSpace = true;
             }
-            if (Utils.isEmailVariation(variation)) {
+            if (InputTypeCompatUtils.isEmailVariation(variation)) {
                 mIsSettingsSuggestionStripOn = false;
             } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) {
                 mIsSettingsSuggestionStripOn = false;
@@ -646,32 +642,11 @@
         }
     }
 
-    private void checkRecorrectionOnStart() {
-        if (!mRecorrectionEnabled) return;
-
-        final InputConnection ic = getCurrentInputConnection();
-        if (ic == null) return;
-        // There could be a pending composing span.  Clean it up first.
-        ic.finishComposingText();
-
-        if (isShowingSuggestionsStrip() && isSuggestionsRequested()) {
-            // First get the cursor position. This is required by setOldSuggestions(), so that
-            // it can pass the correct range to setComposingRegion(). At this point, we don't
-            // have valid values for mLastSelectionStart/End because onUpdateSelection() has
-            // not been called yet.
-            ExtractedTextRequest etr = new ExtractedTextRequest();
-            etr.token = 0; // anything is fine here
-            ExtractedText et = ic.getExtractedText(etr, 0);
-            if (et == null) return;
-
-            mLastSelectionStart = et.startOffset + et.selectionStart;
-            mLastSelectionEnd = et.startOffset + et.selectionEnd;
-
-            // Then look for possible corrections in a delayed fashion
-            if (!TextUtils.isEmpty(et.text) && isCursorTouchingWord()) {
-                mHandler.postUpdateOldSuggestions();
-            }
-        }
+    @Override
+    public void onWindowHidden() {
+        super.onWindowHidden();
+        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+        if (inputView != null) inputView.closing();
     }
 
     @Override
@@ -681,9 +656,9 @@
         LatinImeLogger.commit();
         mKeyboardSwitcher.onAutoCorrectionStateChanged(false);
 
-        mVoiceConnector.flushVoiceInputLogs(mConfigurationChanging);
+        mVoiceProxy.flushVoiceInputLogs(mConfigurationChanging);
 
-        KeyboardView inputView = mKeyboardSwitcher.getInputView();
+        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
         if (inputView != null) inputView.closing();
         if (mAutoDictionary != null) mAutoDictionary.flushPendingWrites();
         if (mUserBigramDictionary != null) mUserBigramDictionary.flushPendingWrites();
@@ -692,7 +667,7 @@
     @Override
     public void onFinishInputView(boolean finishingInput) {
         super.onFinishInputView(finishingInput);
-        KeyboardView inputView = mKeyboardSwitcher.getInputView();
+        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
         if (inputView != null) inputView.setForeground(false);
         // Remove pending messages related to update suggestions
         mHandler.cancelUpdateSuggestions();
@@ -702,7 +677,7 @@
     @Override
     public void onUpdateExtractedText(int token, ExtractedText text) {
         super.onUpdateExtractedText(token, text);
-        mVoiceConnector.showPunctuationHintIfNecessary();
+        mVoiceProxy.showPunctuationHintIfNecessary();
     }
 
     @Override
@@ -723,67 +698,62 @@
                     + ", ce=" + candidatesEnd);
         }
 
-        mVoiceConnector.setCursorAndSelection(newSelEnd, newSelStart);
+        mVoiceProxy.setCursorAndSelection(newSelEnd, newSelStart);
 
         // If the current selection in the text view changes, we should
         // clear whatever candidate text we have.
         final boolean selectionChanged = (newSelStart != candidatesEnd
                 || newSelEnd != candidatesEnd) && mLastSelectionStart != newSelStart;
         final boolean candidatesCleared = candidatesStart == -1 && candidatesEnd == -1;
-        if (((mComposing.length() > 0 && mHasValidSuggestions)
-                || mVoiceConnector.isVoiceInputHighlighted())
+        if (((mComposing.length() > 0 && mHasUncommittedTypedChars)
+                || mVoiceProxy.isVoiceInputHighlighted())
                 && (selectionChanged || candidatesCleared)) {
             if (candidatesCleared) {
                 // If the composing span has been cleared, save the typed word in the history for
                 // recorrection before we reset the candidate strip.  Then, we'll be able to show
                 // suggestions for recorrection right away.
-                saveWordInHistory(mComposing);
+                mRecorrection.saveRecorrectionSuggestion(mWord, mComposing);
             }
             mComposing.setLength(0);
-            mHasValidSuggestions = false;
-            mHandler.postUpdateSuggestions();
+            mHasUncommittedTypedChars = false;
+            if (isCursorTouchingWord()) {
+                mHandler.cancelUpdateBigramPredictions();
+                mHandler.postUpdateSuggestions();
+            } else {
+                setPunctuationSuggestions();
+            }
             TextEntryState.reset();
             InputConnection ic = getCurrentInputConnection();
             if (ic != null) {
                 ic.finishComposingText();
             }
-            mVoiceConnector.setVoiceInputHighlighted(false);
-        } else if (!mHasValidSuggestions && !mJustAccepted) {
+            mVoiceProxy.setVoiceInputHighlighted(false);
+        } else if (!mHasUncommittedTypedChars && !mExpectingUpdateSelection) {
             if (TextEntryState.isAcceptedDefault() || TextEntryState.isSpaceAfterPicked()) {
                 if (TextEntryState.isAcceptedDefault())
                     TextEntryState.reset();
-                mJustAddedAutoSpace = false; // The user moved the cursor.
             }
         }
-        mJustAccepted = false;
+        if (!mExpectingUpdateSelection) {
+          mJustAddedMagicSpace = false; // The user moved the cursor.
+          mJustReplacedDoubleSpace = false;
+        }
+        mExpectingUpdateSelection = false;
         mHandler.postUpdateShiftKeyState();
 
         // Make a note of the cursor position
         mLastSelectionStart = newSelStart;
         mLastSelectionEnd = newSelEnd;
 
-        if (mRecorrectionEnabled && isShowingSuggestionsStrip()) {
-            // Don't look for corrections if the keyboard is not visible
-            if (mKeyboardSwitcher.isInputViewShown()) {
-                // Check if we should go in or out of correction mode.
-                if (isSuggestionsRequested()
-                        && (candidatesStart == candidatesEnd || newSelStart != oldSelStart
-                                || TextEntryState.isRecorrecting())
-                                && (newSelStart < newSelEnd - 1 || !mHasValidSuggestions)) {
-                    if (isCursorTouchingWord() || mLastSelectionStart < mLastSelectionEnd) {
-                        mHandler.postUpdateOldSuggestions();
-                    } else {
-                        abortRecorrection(false);
-                        // Show the punctuation suggestions list if the current one is not
-                        // and if not showing "Touch again to save".
-                        if (mCandidateView != null && !isShowingPunctuationList()
-                                && !mCandidateView.isShowingAddToDictionaryHint()) {
-                            setPunctuationSuggestions();
-                        }
-                    }
-                }
-            }
-        }
+        mRecorrection.updateRecorrectionSelection(mKeyboardSwitcher,
+                mCandidateView, candidatesStart, candidatesEnd, newSelStart,
+                newSelEnd, oldSelStart, mLastSelectionStart,
+                mLastSelectionEnd, mHasUncommittedTypedChars);
+    }
+
+    public void setLastSelection(int start, int end) {
+        mLastSelectionStart = start;
+        mLastSelectionEnd = end;
     }
 
     /**
@@ -796,7 +766,7 @@
      */
     @Override
     public void onExtractedTextClicked() {
-        if (mRecorrectionEnabled && isSuggestionsRequested()) return;
+        if (mRecorrection.isRecorrectionEnabled() && isSuggestionsRequested()) return;
 
         super.onExtractedTextClicked();
     }
@@ -812,7 +782,7 @@
      */
     @Override
     public void onExtractedCursorMovement(int dx, int dy) {
-        if (mRecorrectionEnabled && isSuggestionsRequested()) return;
+        if (mRecorrection.isRecorrectionEnabled() && isSuggestionsRequested()) return;
 
         super.onExtractedCursorMovement(dx, dy);
     }
@@ -827,8 +797,8 @@
             mOptionsDialog.dismiss();
             mOptionsDialog = null;
         }
-        mVoiceConnector.hideVoiceWindow(mConfigurationChanging);
-        mWordHistory.clear();
+        mVoiceProxy.hideVoiceWindow(mConfigurationChanging);
+        mRecorrection.clearWordsInHistory();
         super.hideWindow();
     }
 
@@ -856,57 +826,58 @@
             // When in fullscreen mode, show completions generated by the application
             setSuggestions(builder.build());
             mBestWord = null;
-            setCandidatesViewShown(true);
+            setSuggestionStripShown(true);
         }
     }
 
-    private void setCandidatesViewShownInternal(boolean shown, boolean needsInputViewShown) {
-        // TODO: Remove this if we support candidates with hard keyboard
+    private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) {
+        // TODO: Modify this if we support candidates with hard keyboard
         if (onEvaluateInputViewShown()) {
-            super.setCandidatesViewShown(shown
-                    && (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true));
+            final boolean shouldShowCandidates = shown
+                    && (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true);
+            if (isExtractViewShown()) {
+                // No need to have extra space to show the key preview.
+                mCandidateViewContainer.setMinimumHeight(0);
+                mCandidateViewContainer.setVisibility(
+                        shouldShowCandidates ? View.VISIBLE : View.GONE);
+            } else {
+                // We must control the visibility of the suggestion strip in order to avoid clipped
+                // key previews, even when we don't show the suggestion strip.
+                mCandidateViewContainer.setVisibility(
+                        shouldShowCandidates ? View.VISIBLE : View.INVISIBLE);
+            }
         }
     }
 
-    @Override
-    public void setCandidatesViewShown(boolean shown) {
-        setCandidatesViewShownInternal(shown, true /* needsInputViewShown */ );
+    private void setSuggestionStripShown(boolean shown) {
+        setSuggestionStripShownInternal(shown, /* needsInputViewShown */true);
     }
 
     @Override
     public void onComputeInsets(InputMethodService.Insets outInsets) {
         super.onComputeInsets(outInsets);
-        if (!isFullscreenMode()) {
-            outInsets.contentTopInsets = outInsets.visibleTopInsets;
-        }
-        KeyboardView inputView = mKeyboardSwitcher.getInputView();
+        final KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+        if (inputView == null || mCandidateViewContainer == null)
+            return;
+        final int containerHeight = mCandidateViewContainer.getHeight();
+        int touchY = containerHeight;
         // Need to set touchable region only if input view is being shown
-        if (inputView != null && mKeyboardSwitcher.isInputViewShown()) {
-            final int x = 0;
-            int y = 0;
-            final int width = inputView.getWidth();
-            int height = inputView.getHeight() + EXTENDED_TOUCHABLE_REGION_HEIGHT;
-            if (mCandidateViewContainer != null) {
-                ViewParent candidateParent = mCandidateViewContainer.getParent();
-                if (candidateParent instanceof FrameLayout) {
-                    FrameLayout fl = (FrameLayout) candidateParent;
-                    if (fl != null) {
-                        // Check frame layout's visibility
-                        if (fl.getVisibility() == View.INVISIBLE) {
-                            y = fl.getHeight();
-                            height += y;
-                        } else if (fl.getVisibility() == View.VISIBLE) {
-                            height += fl.getHeight();
-                        }
-                    }
-                }
+        if (mKeyboardSwitcher.isInputViewShown()) {
+            if (mCandidateViewContainer.getVisibility() == View.VISIBLE) {
+                touchY -= mCandidateStripHeight;
             }
+            final int touchWidth = inputView.getWidth();
+            final int touchHeight = inputView.getHeight() + containerHeight
+                    // Extend touchable region below the keyboard.
+                    + EXTENDED_TOUCHABLE_REGION_HEIGHT;
             if (DEBUG) {
-                Log.d(TAG, "Touchable region " + x + ", " + y + ", " + width + ", " + height);
+                Log.d(TAG, "Touchable region: y=" + touchY + " width=" + touchWidth
+                        + " height=" + touchHeight);
             }
-            outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
-            outInsets.touchableRegion.set(x, y, width, height);
+            setTouchableRegionCompat(outInsets, 0, touchY, touchWidth, touchHeight);
         }
+        outInsets.contentTopInsets = touchY;
+        outInsets.visibleTopInsets = touchY;
     }
 
     @Override
@@ -927,8 +898,8 @@
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         switch (keyCode) {
         case KeyEvent.KEYCODE_BACK:
-            if (event.getRepeatCount() == 0 && mKeyboardSwitcher.getInputView() != null) {
-                if (mKeyboardSwitcher.getInputView().handleBack()) {
+            if (event.getRepeatCount() == 0 && mKeyboardSwitcher.getKeyboardView() != null) {
+                if (mKeyboardSwitcher.getKeyboardView().handleBack()) {
                     return true;
                 }
             }
@@ -962,8 +933,8 @@
     }
 
     public void commitTyped(InputConnection inputConnection) {
-        if (mHasValidSuggestions) {
-            mHasValidSuggestions = false;
+        if (mHasUncommittedTypedChars) {
+            mHasUncommittedTypedChars = false;
             if (mComposing.length() > 0) {
                 if (inputConnection != null) {
                     inputConnection.commitText(mComposing, 1);
@@ -979,45 +950,29 @@
     public boolean getCurrentAutoCapsState() {
         InputConnection ic = getCurrentInputConnection();
         EditorInfo ei = getCurrentInputEditorInfo();
-        if (mAutoCap && ic != null && ei != null && ei.inputType != InputType.TYPE_NULL) {
+        if (mSettingsValues.mAutoCap && ic != null && ei != null
+                && ei.inputType != InputType.TYPE_NULL) {
             return ic.getCursorCapsMode(ei.inputType) != 0;
         }
         return false;
     }
 
-    private void swapPunctuationAndSpace() {
+    private void swapSwapperAndSpace() {
         final InputConnection ic = getCurrentInputConnection();
         if (ic == null) return;
         CharSequence lastTwo = ic.getTextBeforeCursor(2, 0);
+        // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
         if (lastTwo != null && lastTwo.length() == 2
-                && lastTwo.charAt(0) == Keyboard.CODE_SPACE
-                && isSentenceSeparator(lastTwo.charAt(1))) {
+                && lastTwo.charAt(0) == Keyboard.CODE_SPACE) {
             ic.beginBatchEdit();
             ic.deleteSurroundingText(2, 0);
             ic.commitText(lastTwo.charAt(1) + " ", 1);
             ic.endBatchEdit();
             mKeyboardSwitcher.updateShiftState();
-            mJustAddedAutoSpace = true;
         }
     }
 
-    private void reswapPeriodAndSpace() {
-        final InputConnection ic = getCurrentInputConnection();
-        if (ic == null) return;
-        CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
-        if (lastThree != null && lastThree.length() == 3
-                && lastThree.charAt(0) == Keyboard.CODE_PERIOD
-                && lastThree.charAt(1) == Keyboard.CODE_SPACE
-                && lastThree.charAt(2) == Keyboard.CODE_PERIOD) {
-            ic.beginBatchEdit();
-            ic.deleteSurroundingText(3, 0);
-            ic.commitText(" ..", 1);
-            ic.endBatchEdit();
-            mKeyboardSwitcher.updateShiftState();
-        }
-    }
-
-    private void doubleSpace() {
+    private void maybeDoubleSpace() {
         if (mCorrectionMode == Suggest.CORRECTION_NONE) return;
         final InputConnection ic = getCurrentInputConnection();
         if (ic == null) return;
@@ -1033,7 +988,7 @@
             ic.commitText(". ", 1);
             ic.endBatchEdit();
             mKeyboardSwitcher.updateShiftState();
-            mJustAddedAutoSpace = true;
+            mJustReplacedDoubleSpace = true;
         } else {
             mHandler.startDoubleSpacesTimer();
         }
@@ -1064,6 +1019,7 @@
         }
     }
 
+    @Override
     public boolean addWordToDictionary(String word) {
         mUserDictionary.addWord(word, 128);
         // Suggestion strip should be updated after the operation of adding word to the
@@ -1081,14 +1037,14 @@
     }
 
     private void onSettingsKeyPressed() {
-        if (!isShowingOptionDialog()) {
-            if (!mConfigEnableShowSubtypeSettings) {
-                showSubtypeSelectorAndSettings();
-            } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) {
-                showOptionsMenu();
-            } else {
-                launchSettings();
-            }
+        if (isShowingOptionDialog())
+            return;
+        if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) {
+            showSubtypeSelectorAndSettings();
+        } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) {
+            showOptionsMenu();
+        } else {
+            launchSettings();
         }
     }
 
@@ -1115,22 +1071,24 @@
         }
         mLastKeyTime = when;
         KeyboardSwitcher switcher = mKeyboardSwitcher;
-        final boolean accessibilityEnabled = switcher.isAccessibilityEnabled();
         final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
+        final boolean lastStateOfJustReplacedDoubleSpace = mJustReplacedDoubleSpace;
+        mJustReplacedDoubleSpace = false;
         switch (primaryCode) {
         case Keyboard.CODE_DELETE:
-            handleBackspace();
+            handleBackspace(lastStateOfJustReplacedDoubleSpace);
             mDeleteCount++;
+            mExpectingUpdateSelection = true;
             LatinImeLogger.logOnDelete();
             break;
         case Keyboard.CODE_SHIFT:
             // Shift key is handled in onPress() when device has distinct multi-touch panel.
-            if (!distinctMultiTouch || accessibilityEnabled)
+            if (!distinctMultiTouch)
                 switcher.toggleShift();
             break;
         case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
             // Symbol key is handled in onPress() when device has distinct multi-touch panel.
-            if (!distinctMultiTouch || accessibilityEnabled)
+            if (!distinctMultiTouch)
                 switcher.changeKeyboardMode();
             break;
         case Keyboard.CODE_CANCEL:
@@ -1144,32 +1102,37 @@
         case Keyboard.CODE_SETTINGS_LONGPRESS:
             onSettingsKeyLongPressed();
             break;
-        case Keyboard.CODE_NEXT_LANGUAGE:
-            toggleLanguage(false, true);
+        case LatinKeyboard.CODE_NEXT_LANGUAGE:
+            toggleLanguage(true);
             break;
-        case Keyboard.CODE_PREV_LANGUAGE:
-            toggleLanguage(false, false);
+        case LatinKeyboard.CODE_PREV_LANGUAGE:
+            toggleLanguage(false);
             break;
         case Keyboard.CODE_CAPSLOCK:
             switcher.toggleCapsLock();
             break;
-        case Keyboard.CODE_VOICE:
+        case Keyboard.CODE_SHORTCUT:
             mSubtypeSwitcher.switchToShortcutIME();
             break;
         case Keyboard.CODE_TAB:
             handleTab();
+            // There are two cases for tab. Either we send a "next" event, that may change the
+            // focus but will never move the cursor. Or, we send a real tab keycode, which some
+            // applications may accept or ignore, and we don't know whether this will move the
+            // cursor or not. So actually, we don't really know.
+            // So to go with the safer option, we'd rather behave as if the user moved the
+            // cursor when they didn't than the opposite. We also expect that most applications
+            // will actually use tab only for focus movement.
+            // To sum it up: do not update mExpectingUpdateSelection here.
             break;
         default:
-            if (primaryCode != Keyboard.CODE_ENTER) {
-                mJustAddedAutoSpace = false;
-            }
-            RingCharBuffer.getInstance().push((char)primaryCode, x, y);
-            LatinImeLogger.logOnInputChar();
-            if (isWordSeparator(primaryCode)) {
-                handleSeparator(primaryCode);
+            if (mSettingsValues.isWordSeparator(primaryCode)) {
+                handleSeparator(primaryCode, x, y);
             } else {
                 handleCharacter(primaryCode, keyCodes, x, y);
             }
+            mExpectingUpdateSelection = true;
+            break;
         }
         switcher.onKey(primaryCode);
         // Reset after any single keystroke
@@ -1178,10 +1141,10 @@
 
     @Override
     public void onTextInput(CharSequence text) {
-        mVoiceConnector.commitVoiceInput();
+        mVoiceProxy.commitVoiceInput();
         InputConnection ic = getCurrentInputConnection();
         if (ic == null) return;
-        abortRecorrection(false);
+        mRecorrection.abortRecorrection(false);
         ic.beginBatchEdit();
         commitTyped(ic);
         maybeRemovePreviousPeriod(text);
@@ -1189,7 +1152,7 @@
         ic.endBatchEdit();
         mKeyboardSwitcher.updateShiftState();
         mKeyboardSwitcher.onKey(Keyboard.CODE_DUMMY);
-        mJustAddedAutoSpace = false;
+        mJustAddedMagicSpace = false;
         mEnteredText = text;
     }
 
@@ -1199,26 +1162,33 @@
         mKeyboardSwitcher.onCancelInput();
     }
 
-    private void handleBackspace() {
-        if (mVoiceConnector.logAndRevertVoiceInput()) return;
+    private void handleBackspace(boolean justReplacedDoubleSpace) {
+        if (mVoiceProxy.logAndRevertVoiceInput()) return;
 
         final InputConnection ic = getCurrentInputConnection();
         if (ic == null) return;
         ic.beginBatchEdit();
 
-        mVoiceConnector.handleBackspace();
+        mVoiceProxy.handleBackspace();
 
         boolean deleteChar = false;
-        if (mHasValidSuggestions) {
+        if (mHasUncommittedTypedChars) {
             final int length = mComposing.length();
             if (length > 0) {
                 mComposing.delete(length - 1, length);
                 mWord.deleteLast();
                 ic.setComposingText(mComposing, 1);
                 if (mComposing.length() == 0) {
-                    mHasValidSuggestions = false;
+                    mHasUncommittedTypedChars = false;
                 }
-                mHandler.postUpdateSuggestions();
+                if (1 == length) {
+                    // 1 == length means we are about to erase the last character of the word,
+                    // so we can show bigrams.
+                    mHandler.postUpdateBigramPredictions();
+                } else {
+                    // length > 1, so we still have letters to deduce a suggestion from.
+                    mHandler.postUpdateSuggestions();
+                }
             } else {
                 ic.deleteSurroundingText(1, 0);
             }
@@ -1233,6 +1203,12 @@
             ic.endBatchEdit();
             return;
         }
+        if (justReplacedDoubleSpace) {
+            if (revertDoubleSpace()) {
+              ic.endBatchEdit();
+              return;
+            }
+        }
 
         if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) {
             ic.deleteSurroundingText(mEnteredText.length(), 0);
@@ -1258,9 +1234,8 @@
 
     private void handleTab() {
         final int imeOptions = getCurrentInputEditorInfo().imeOptions;
-        final int navigationFlags =
-                EditorInfo.IME_FLAG_NAVIGATE_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS;
-        if ((imeOptions & navigationFlags) == 0) {
+        if (!EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions)
+                && !EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions)) {
             sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB);
             return;
         }
@@ -1271,42 +1246,37 @@
 
         // True if keyboard is in either chording shift or manual temporary upper case mode.
         final boolean isManualTemporaryUpperCase = mKeyboardSwitcher.isManualTemporaryUpperCase();
-        if ((imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0
+        if (EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions)
                 && !isManualTemporaryUpperCase) {
-            ic.performEditorAction(EditorInfo.IME_ACTION_NEXT);
-        } else if ((imeOptions & EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS) != 0
+            EditorInfoCompatUtils.performEditorActionNext(ic);
+        } else if (EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions)
                 && isManualTemporaryUpperCase) {
-            ic.performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
-        }
-    }
-
-    private void abortRecorrection(boolean force) {
-        if (force || TextEntryState.isRecorrecting()) {
-            TextEntryState.onAbortRecorrection();
-            setCandidatesViewShown(isCandidateStripVisible());
-            getCurrentInputConnection().finishComposingText();
-            clearSuggestions();
+            EditorInfoCompatUtils.performEditorActionPrevious(ic);
         }
     }
 
     private void handleCharacter(int primaryCode, int[] keyCodes, int x, int y) {
-        mVoiceConnector.handleCharacter();
+        mVoiceProxy.handleCharacter();
 
-        if (mLastSelectionStart == mLastSelectionEnd && TextEntryState.isRecorrecting()) {
-            abortRecorrection(false);
+        if (mJustAddedMagicSpace && mSettingsValues.isMagicSpaceStripper(primaryCode)) {
+            removeTrailingSpace();
+        }
+
+        if (mLastSelectionStart == mLastSelectionEnd) {
+            mRecorrection.abortRecorrection(false);
         }
 
         int code = primaryCode;
         if (isAlphabet(code) && isSuggestionsRequested() && !isCursorTouchingWord()) {
-            if (!mHasValidSuggestions) {
-                mHasValidSuggestions = true;
+            if (!mHasUncommittedTypedChars) {
+                mHasUncommittedTypedChars = true;
                 mComposing.setLength(0);
-                saveWordInHistory(mBestWord);
+                mRecorrection.saveRecorrectionSuggestion(mWord, mBestWord);
                 mWord.reset();
                 clearSuggestions();
             }
         }
-        KeyboardSwitcher switcher = mKeyboardSwitcher;
+        final KeyboardSwitcher switcher = mKeyboardSwitcher;
         if (switcher.isShiftedOrShiftLocked()) {
             if (keyCodes == null || keyCodes[0] < Character.MIN_CODE_POINT
                     || keyCodes[0] > Character.MAX_CODE_POINT) {
@@ -1314,18 +1284,20 @@
             }
             code = keyCodes[0];
             if (switcher.isAlphabetMode() && Character.isLowerCase(code)) {
-                int upperCaseCode = Character.toUpperCase(code);
-                if (upperCaseCode != code) {
-                    code = upperCaseCode;
+                // In some locales, such as Turkish, Character.toUpperCase() may return a wrong
+                // character because it doesn't take care of locale.
+                final String upperCaseString = new String(new int[] {code}, 0, 1)
+                        .toUpperCase(mSubtypeSwitcher.getInputLocale());
+                if (upperCaseString.codePointCount(0, upperCaseString.length()) == 1) {
+                    code = upperCaseString.codePointAt(0);
                 } else {
                     // Some keys, such as [eszett], have upper case as multi-characters.
-                    String upperCase = new String(new int[] {code}, 0, 1).toUpperCase();
-                    onTextInput(upperCase);
+                    onTextInput(upperCaseString);
                     return;
                 }
             }
         }
-        if (mHasValidSuggestions) {
+        if (mHasUncommittedTypedChars) {
             if (mComposing.length() == 0 && switcher.isAlphabetMode()
                     && switcher.isShiftedOrShiftLocked()) {
                 mWord.setFirstCharCapitalized(true);
@@ -1344,16 +1316,23 @@
         } else {
             sendKeyChar((char)code);
         }
+        if (mJustAddedMagicSpace && mSettingsValues.isMagicSpaceSwapper(primaryCode)) {
+            swapSwapperAndSpace();
+        } else {
+            mJustAddedMagicSpace = false;
+        }
+
         switcher.updateShiftState();
         if (LatinIME.PERF_DEBUG) measureCps();
-        TextEntryState.typedCharacter((char) code, isWordSeparator(code));
+        TextEntryState.typedCharacter((char) code, mSettingsValues.isWordSeparator(code), x, y);
     }
 
-    private void handleSeparator(int primaryCode) {
-        mVoiceConnector.handleSeparator();
+    private void handleSeparator(int primaryCode, int x, int y) {
+        mVoiceProxy.handleSeparator();
 
         // Should dismiss the "Touch again to save" message when handling separator
         if (mCandidateView != null && mCandidateView.dismissAddToDictionaryHint()) {
+            mHandler.cancelUpdateBigramPredictions();
             mHandler.postUpdateSuggestions();
         }
 
@@ -1362,54 +1341,61 @@
         final InputConnection ic = getCurrentInputConnection();
         if (ic != null) {
             ic.beginBatchEdit();
-            abortRecorrection(false);
+            mRecorrection.abortRecorrection(false);
         }
-        if (mHasValidSuggestions) {
+        if (mHasUncommittedTypedChars) {
             // In certain languages where single quote is a separator, it's better
             // not to auto correct, but accept the typed word. For instance,
             // in Italian dov' should not be expanded to dove' because the elision
             // requires the last vowel to be removed.
-            if (mAutoCorrectOn && primaryCode != '\'') {
-                pickedDefault = pickDefaultSuggestion();
-                // Picked the suggestion by the space key.  We consider this
-                // as "added an auto space".
-                if (primaryCode == Keyboard.CODE_SPACE) {
-                    mJustAddedAutoSpace = true;
-                }
+            final boolean shouldAutoCorrect =
+                    (mSettingsValues.mAutoCorrectEnabled || mSettingsValues.mQuickFixes)
+                    && !mInputTypeNoAutoCorrect && mHasDictionary;
+            if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
+                pickedDefault = pickDefaultSuggestion(primaryCode);
             } else {
                 commitTyped(ic);
             }
         }
-        if (mJustAddedAutoSpace && primaryCode == Keyboard.CODE_ENTER) {
-            removeTrailingSpace();
-            mJustAddedAutoSpace = false;
-        }
-        sendKeyChar((char)primaryCode);
 
-        // Handle the case of ". ." -> " .." with auto-space if necessary
-        // before changing the TextEntryState.
-        if (TextEntryState.isPunctuationAfterAccepted() && primaryCode == Keyboard.CODE_PERIOD) {
-            reswapPeriodAndSpace();
+        if (mJustAddedMagicSpace) {
+            if (mSettingsValues.isMagicSpaceSwapper(primaryCode)) {
+                sendKeyChar((char)primaryCode);
+                swapSwapperAndSpace();
+            } else {
+                if (mSettingsValues.isMagicSpaceStripper(primaryCode)) removeTrailingSpace();
+                sendKeyChar((char)primaryCode);
+                mJustAddedMagicSpace = false;
+            }
+        } else {
+            sendKeyChar((char)primaryCode);
         }
 
-        TextEntryState.typedCharacter((char) primaryCode, true);
-        if (TextEntryState.isPunctuationAfterAccepted() && primaryCode != Keyboard.CODE_ENTER) {
-            swapPunctuationAndSpace();
-        } else if (isSuggestionsRequested() && primaryCode == Keyboard.CODE_SPACE) {
-            doubleSpace();
+        if (isSuggestionsRequested() && primaryCode == Keyboard.CODE_SPACE) {
+            maybeDoubleSpace();
         }
+
+        TextEntryState.typedCharacter((char) primaryCode, true, x, y);
+
         if (pickedDefault) {
             CharSequence typedWord = mWord.getTypedWord();
             TextEntryState.backToAcceptedDefault(typedWord);
             if (!TextUtils.isEmpty(typedWord) && !typedWord.equals(mBestWord)) {
-                if (ic != null) {
-                    CorrectionInfo correctionInfo = new CorrectionInfo(
-                            mLastSelectionEnd - typedWord.length(), typedWord, mBestWord);
-                    ic.commitCorrection(correctionInfo);
-                }
+                InputConnectionCompatUtils.commitCorrection(
+                        ic, mLastSelectionEnd - typedWord.length(), typedWord, mBestWord);
                 if (mCandidateView != null)
                     mCandidateView.onAutoCorrectionInverted(mBestWord);
             }
+        }
+        if (Keyboard.CODE_SPACE == primaryCode) {
+            if (!isCursorTouchingWord()) {
+                mHandler.cancelUpdateSuggestions();
+                mHandler.cancelUpdateOldSuggestions();
+                mHandler.postUpdateBigramPredictions();
+            }
+        } else {
+            // Set punctuation right away. onUpdateSelection will fire but tests whether it is
+            // already displayed or not, so it's okay.
             setPunctuationSuggestions();
         }
         mKeyboardSwitcher.updateShiftState();
@@ -1420,45 +1406,29 @@
 
     private void handleClose() {
         commitTyped(getCurrentInputConnection());
-        mVoiceConnector.handleClose();
+        mVoiceProxy.handleClose();
         requestHideSelf(0);
-        LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
+        LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
         if (inputView != null)
             inputView.closing();
     }
 
-    private void saveWordInHistory(CharSequence result) {
-        if (mWord.size() <= 1) {
-            return;
-        }
-        // Skip if result is null. It happens in some edge case.
-        if (TextUtils.isEmpty(result)) {
-            return;
-        }
-
-        // Make a copy of the CharSequence, since it is/could be a mutable CharSequence
-        final String resultCopy = result.toString();
-        TypedWordAlternatives entry = new TypedWordAlternatives(resultCopy,
-                new WordComposer(mWord));
-        mWordHistory.add(entry);
-    }
-
-    private boolean isSuggestionsRequested() {
+    public boolean isSuggestionsRequested() {
         return mIsSettingsSuggestionStripOn
                 && (mCorrectionMode > 0 || isShowingSuggestionsStrip());
     }
 
-    private boolean isShowingPunctuationList() {
-        return mSuggestPuncList == mCandidateView.getSuggestions();
+    public boolean isShowingPunctuationList() {
+        return mSettingsValues.mSuggestPuncList == mCandidateView.getSuggestions();
     }
 
-    private boolean isShowingSuggestionsStrip() {
+    public boolean isShowingSuggestionsStrip() {
         return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
                 || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
                         && mOrientation == Configuration.ORIENTATION_PORTRAIT);
     }
 
-    private boolean isCandidateStripVisible() {
+    public boolean isCandidateStripVisible() {
         if (mCandidateView == null)
             return false;
         if (mCandidateView.isShowingAddToDictionaryHint() || TextEntryState.isRecorrecting())
@@ -1474,7 +1444,7 @@
         if (DEBUG) {
             Log.d(TAG, "Switch to keyboard view.");
         }
-        View v = mKeyboardSwitcher.getInputView();
+        View v = mKeyboardSwitcher.getKeyboardView();
         if (v != null) {
             // Confirms that the keyboard view doesn't have parent view.
             ViewParent p = v.getParent();
@@ -1483,7 +1453,7 @@
             }
             setInputView(v);
         }
-        setCandidatesViewShown(isCandidateStripVisible());
+        setSuggestionStripShown(isCandidateStripVisible());
         updateInputViewShown();
         mHandler.postUpdateSuggestions();
     }
@@ -1493,49 +1463,33 @@
     }
 
     public void setSuggestions(SuggestedWords words) {
-        if (mVoiceConnector.getAndResetIsShowingHint()) {
-             setCandidatesView(mCandidateViewContainer);
-        }
-
         if (mCandidateView != null) {
             mCandidateView.setSuggestions(words);
-            if (mCandidateView.isConfigCandidateHighlightFontColorEnabled()) {
-                mKeyboardSwitcher.onAutoCorrectionStateChanged(
-                        words.hasWordAboveAutoCorrectionScoreThreshold());
-            }
+            mKeyboardSwitcher.onAutoCorrectionStateChanged(
+                    words.hasWordAboveAutoCorrectionScoreThreshold());
         }
     }
 
     public void updateSuggestions() {
         // Check if we have a suggestion engine attached.
         if ((mSuggest == null || !isSuggestionsRequested())
-                && !mVoiceConnector.isVoiceInputHighlighted()) {
+                && !mVoiceProxy.isVoiceInputHighlighted()) {
             return;
         }
 
-        if (!mHasValidSuggestions) {
+        if (!mHasUncommittedTypedChars) {
             setPunctuationSuggestions();
             return;
         }
         showSuggestions(mWord);
     }
 
-    private SuggestedWords.Builder getTypedSuggestions(WordComposer word) {
-        return mSuggest.getSuggestedWordBuilder(mKeyboardSwitcher.getInputView(), word, null);
-    }
-
-    private void showCorrections(WordAlternatives alternatives) {
-        SuggestedWords.Builder builder = alternatives.getAlternatives();
-        builder.setTypedWordValid(false).setHasMinimalSuggestion(false);
-        showSuggestions(builder.build(), alternatives.getOriginalWord());
-    }
-
     private void showSuggestions(WordComposer word) {
         // TODO: May need a better way of retrieving previous word
         CharSequence prevWord = EditingUtils.getPreviousWord(getCurrentInputConnection(),
-                mWordSeparators);
+                mSettingsValues.mWordSeparators);
         SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
-                mKeyboardSwitcher.getInputView(), word, prevWord);
+                mKeyboardSwitcher.getKeyboardView(), word, prevWord);
 
         boolean correctionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection();
         final CharSequence typedWord = word.getTypedWord();
@@ -1556,19 +1510,22 @@
         // in most cases, suggestion count is 1 when typed word's length is 1, but we do always
         // need to clear the previous state when the user starts typing a word (i.e. typed word's
         // length == 1).
-        if (builder.size() > 1 || typedWord.length() == 1 || typedWordValid
-                || mCandidateView.isShowingAddToDictionaryHint()) {
-            builder.setTypedWordValid(typedWordValid).setHasMinimalSuggestion(correctionAvailable);
-        } else {
-            final SuggestedWords previousSuggestions = mCandidateView.getSuggestions();
-            if (previousSuggestions == mSuggestPuncList)
-                return;
-            builder.addTypedWordAndPreviousSuggestions(typedWord, previousSuggestions);
+        if (typedWord != null) {
+            if (builder.size() > 1 || typedWord.length() == 1 || typedWordValid
+                    || mCandidateView.isShowingAddToDictionaryHint()) {
+                builder.setTypedWordValid(typedWordValid).setHasMinimalSuggestion(
+                        correctionAvailable);
+            } else {
+                final SuggestedWords previousSuggestions = mCandidateView.getSuggestions();
+                if (previousSuggestions == mSettingsValues.mSuggestPuncList)
+                    return;
+                builder.addTypedWordAndPreviousSuggestions(typedWord, previousSuggestions);
+            }
         }
         showSuggestions(builder.build(), typedWord);
     }
 
-    private void showSuggestions(SuggestedWords suggestedWords, CharSequence typedWord) {
+    public void showSuggestions(SuggestedWords suggestedWords, CharSequence typedWord) {
         setSuggestions(suggestedWords);
         if (suggestedWords.size() > 0) {
             if (Utils.shouldBlockedBySafetyNetForAutoCorrection(suggestedWords, mSuggest)) {
@@ -1581,30 +1538,31 @@
         } else {
             mBestWord = null;
         }
-        setCandidatesViewShown(isCandidateStripVisible());
+        setSuggestionStripShown(isCandidateStripVisible());
     }
 
-    private boolean pickDefaultSuggestion() {
+    private boolean pickDefaultSuggestion(int separatorCode) {
         // Complete any pending candidate query first
         if (mHandler.hasPendingUpdateSuggestions()) {
             mHandler.cancelUpdateSuggestions();
             updateSuggestions();
         }
         if (mBestWord != null && mBestWord.length() > 0) {
-            TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord);
-            mJustAccepted = true;
-            pickSuggestion(mBestWord);
+            TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord, separatorCode);
+            mExpectingUpdateSelection = true;
+            commitBestWord(mBestWord);
             // Add the word to the auto dictionary if it's not a known word
             addToAutoAndUserBigramDictionaries(mBestWord, AutoDictionary.FREQUENCY_FOR_TYPED);
             return true;
-
         }
         return false;
     }
 
+    @Override
     public void pickSuggestionManually(int index, CharSequence suggestion) {
         SuggestedWords suggestions = mCandidateView.getSuggestions();
-        mVoiceConnector.flushAndLogAllTextModificationCounters(index, suggestion, mWordSeparators);
+        mVoiceProxy.flushAndLogAllTextModificationCounters(index, suggestion,
+                mSettingsValues.mWordSeparators);
 
         final boolean recorrecting = TextEntryState.isRecorrecting();
         InputConnection ic = getCurrentInputConnection();
@@ -1629,23 +1587,38 @@
         }
 
         // If this is a punctuation, apply it through the normal key press
-        if (suggestion.length() == 1 && (isWordSeparator(suggestion.charAt(0))
-                || isSuggestedPunctuation(suggestion.charAt(0)))) {
+        if (suggestion.length() == 1 && (mSettingsValues.isWordSeparator(suggestion.charAt(0))
+                || mSettingsValues.isSuggestedPunctuation(suggestion.charAt(0)))) {
             // Word separators are suggested before the user inputs something.
             // So, LatinImeLogger logs "" as a user's input.
             LatinImeLogger.logOnManualSuggestion(
                     "", suggestion.toString(), index, suggestions.mWords);
+            // Find out whether the previous character is a space. If it is, as a special case
+            // for punctuation entered through the suggestion strip, it should be considered
+            // a magic space even if it was a normal space. This is meant to help in case the user
+            // pressed space on purpose of displaying the suggestion strip punctuation.
             final char primaryCode = suggestion.charAt(0);
+            final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : "";
+            final int toLeft = (ic == null || TextUtils.isEmpty(beforeText))
+                    ? 0 : beforeText.charAt(0);
+            final boolean oldMagicSpace = mJustAddedMagicSpace;
+            if (Keyboard.CODE_SPACE == toLeft) mJustAddedMagicSpace = true;
             onCodeInput(primaryCode, new int[] { primaryCode },
                     KeyboardActionListener.NOT_A_TOUCH_COORDINATE,
                     KeyboardActionListener.NOT_A_TOUCH_COORDINATE);
+            mJustAddedMagicSpace = oldMagicSpace;
             if (ic != null) {
                 ic.endBatchEdit();
             }
             return;
         }
-        mJustAccepted = true;
-        pickSuggestion(suggestion);
+        if (!mHasUncommittedTypedChars) {
+            // If we are not composing a word, then it was a suggestion inferred from
+            // context - no user input. We should reset the word composer.
+            mWord.reset();
+        }
+        mExpectingUpdateSelection = true;
+        commitBestWord(suggestion);
         // Add the word to the auto dictionary if it's not a known word
         if (index == 0) {
             addToAutoAndUserBigramDictionaries(suggestion, AutoDictionary.FREQUENCY_FOR_PICKED);
@@ -1656,9 +1629,8 @@
                 index, suggestions.mWords);
         TextEntryState.acceptedSuggestion(mComposing.toString(), suggestion);
         // Follow it with a space
-        if (mAutoSpace && !recorrecting) {
-            sendSpace();
-            mJustAddedAutoSpace = true;
+        if (mShouldInsertMagicSpace && !recorrecting) {
+            sendMagicSpace();
         }
 
         // We should show the hint if the user pressed the first entry AND either:
@@ -1680,13 +1652,16 @@
             // Fool the state watcher so that a subsequent backspace will not do a revert, unless
             // we just did a correction, in which case we need to stay in
             // TextEntryState.State.PICKED_SUGGESTION state.
-            TextEntryState.typedCharacter((char) Keyboard.CODE_SPACE, true);
-            setPunctuationSuggestions();
-        } else if (!showingAddToDictionaryHint) {
+            TextEntryState.typedCharacter((char) Keyboard.CODE_SPACE, true,
+                    WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
+        }
+        if (!showingAddToDictionaryHint) {
             // If we're not showing the "Touch again to save", then show corrections again.
             // In case the cursor position doesn't change, make sure we show the suggestions again.
-            clearSuggestions();
-            mHandler.postUpdateOldSuggestions();
+            updateBigramPredictions();
+            // Updating the predictions right away may be slow and feel unresponsive on slower
+            // terminals. On the other hand if we just postUpdateBigramPredictions() it will
+            // take a noticeable delay to update them which may feel uneasy.
         }
         if (showingAddToDictionaryHint) {
             mCandidateView.showAddToDictionaryHint(suggestion);
@@ -1699,107 +1674,50 @@
     /**
      * Commits the chosen word to the text field and saves it for later
      * retrieval.
-     * @param suggestion the suggestion picked by the user to be committed to
-     *            the text field
      */
-    private void pickSuggestion(CharSequence suggestion) {
+    private void commitBestWord(CharSequence bestWord) {
         KeyboardSwitcher switcher = mKeyboardSwitcher;
         if (!switcher.isKeyboardAvailable())
             return;
         InputConnection ic = getCurrentInputConnection();
         if (ic != null) {
-            mVoiceConnector.rememberReplacedWord(suggestion, mWordSeparators);
-            ic.commitText(suggestion, 1);
+            mVoiceProxy.rememberReplacedWord(bestWord, mSettingsValues.mWordSeparators);
+            SuggestedWords suggestedWords = mCandidateView.getSuggestions();
+            ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
+                    this, bestWord, suggestedWords), 1);
         }
-        saveWordInHistory(suggestion);
-        mHasValidSuggestions = false;
-        mCommittedLength = suggestion.length();
+        mRecorrection.saveRecorrectionSuggestion(mWord, bestWord);
+        mHasUncommittedTypedChars = false;
+        mCommittedLength = bestWord.length();
     }
 
-    /**
-     * Tries to apply any typed alternatives for the word if we have any cached alternatives,
-     * otherwise tries to find new corrections and completions for the word.
-     * @param touching The word that the cursor is touching, with position information
-     * @return true if an alternative was found, false otherwise.
-     */
-    private boolean applyTypedAlternatives(EditingUtils.SelectedWord touching) {
-        // If we didn't find a match, search for result in typed word history
-        WordComposer foundWord = null;
-        WordAlternatives alternatives = null;
-        // Search old suggestions to suggest re-corrected suggestions.
-        for (WordAlternatives entry : mWordHistory) {
-            if (TextUtils.equals(entry.getChosenWord(), touching.mWord)) {
-                if (entry instanceof TypedWordAlternatives) {
-                    foundWord = ((TypedWordAlternatives) entry).word;
-                }
-                alternatives = entry;
-                break;
-            }
-        }
-        // If we didn't find a match, at least suggest corrections as re-corrected suggestions.
-        if (foundWord == null
-                && (AutoCorrection.isValidWord(
-                        mSuggest.getUnigramDictionaries(), touching.mWord, true))) {
-            foundWord = new WordComposer();
-            for (int i = 0; i < touching.mWord.length(); i++) {
-                foundWord.add(touching.mWord.charAt(i), new int[] {
-                    touching.mWord.charAt(i)
-                }, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
-            }
-            foundWord.setFirstCharCapitalized(Character.isUpperCase(touching.mWord.charAt(0)));
-        }
-        // Found a match, show suggestions
-        if (foundWord != null || alternatives != null) {
-            if (alternatives == null) {
-                alternatives = new TypedWordAlternatives(touching.mWord, foundWord);
-            }
-            showCorrections(alternatives);
-            if (foundWord != null) {
-                mWord = new WordComposer(foundWord);
-            } else {
-                mWord.reset();
-            }
-            return true;
-        }
-        return false;
-    }
+    private static final WordComposer sEmptyWordComposer = new WordComposer();
+    public void updateBigramPredictions() {
+        if (mSuggest == null || !isSuggestionsRequested())
+            return;
 
-    private void setOldSuggestions() {
-        mVoiceConnector.setShowingVoiceSuggestions(false);
-        if (mCandidateView != null && mCandidateView.isShowingAddToDictionaryHint()) {
+        if (!mSettingsValues.mBigramPredictionEnabled) {
+            setPunctuationSuggestions();
             return;
         }
-        InputConnection ic = getCurrentInputConnection();
-        if (ic == null) return;
-        if (!mHasValidSuggestions) {
-            // Extract the selected or touching text
-            EditingUtils.SelectedWord touching = EditingUtils.getWordAtCursorOrSelection(ic,
-                    mLastSelectionStart, mLastSelectionEnd, mWordSeparators);
 
-            if (touching != null && touching.mWord.length() > 1) {
-                ic.beginBatchEdit();
+        final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(),
+                mSettingsValues.mWordSeparators);
+        SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
+                mKeyboardSwitcher.getKeyboardView(), sEmptyWordComposer, prevWord);
 
-                if (!mVoiceConnector.applyVoiceAlternatives(touching)
-                        && !applyTypedAlternatives(touching)) {
-                    abortRecorrection(true);
-                } else {
-                    TextEntryState.selectedForRecorrection();
-                    EditingUtils.underlineWord(ic, touching);
-                }
-
-                ic.endBatchEdit();
-            } else {
-                abortRecorrection(true);
-                setPunctuationSuggestions();  // Show the punctuation suggestions list
-            }
+        if (builder.size() > 0) {
+            // Explicitly supply an empty typed word (the no-second-arg version of
+            // showSuggestions will retrieve the word near the cursor, we don't want that here)
+            showSuggestions(builder.build(), "");
         } else {
-            abortRecorrection(true);
+            if (!isShowingPunctuationList()) setPunctuationSuggestions();
         }
     }
 
-    private void setPunctuationSuggestions() {
-        setSuggestions(mSuggestPuncList);
-        setCandidatesViewShown(isCandidateStripVisible());
+    public void setPunctuationSuggestions() {
+        setSuggestions(mSettingsValues.mSuggestPuncList);
+        setSuggestionStripShown(isCandidateStripVisible());
     }
 
     private void addToAutoAndUserBigramDictionaries(CharSequence suggestion, int frequencyDelta) {
@@ -1837,27 +1755,30 @@
         }
 
         if (mUserBigramDictionary != null) {
+            // We don't want to register as bigrams words separated by a separator.
+            // For example "I will, and you too" : we don't want the pair ("will" "and") to be
+            // a bigram.
             CharSequence prevWord = EditingUtils.getPreviousWord(getCurrentInputConnection(),
-                    mSentenceSeparators);
+                    mSettingsValues.mWordSeparators);
             if (!TextUtils.isEmpty(prevWord)) {
                 mUserBigramDictionary.addBigrams(prevWord.toString(), suggestion.toString());
             }
         }
     }
 
-    private boolean isCursorTouchingWord() {
+    public boolean isCursorTouchingWord() {
         InputConnection ic = getCurrentInputConnection();
         if (ic == null) return false;
         CharSequence toLeft = ic.getTextBeforeCursor(1, 0);
         CharSequence toRight = ic.getTextAfterCursor(1, 0);
         if (!TextUtils.isEmpty(toLeft)
-                && !isWordSeparator(toLeft.charAt(0))
-                && !isSuggestedPunctuation(toLeft.charAt(0))) {
+                && !mSettingsValues.isWordSeparator(toLeft.charAt(0))
+                && !mSettingsValues.isSuggestedPunctuation(toLeft.charAt(0))) {
             return true;
         }
         if (!TextUtils.isEmpty(toRight)
-                && !isWordSeparator(toRight.charAt(0))
-                && !isSuggestedPunctuation(toRight.charAt(0))) {
+                && !mSettingsValues.isWordSeparator(toRight.charAt(0))
+                && !mSettingsValues.isSuggestedPunctuation(toRight.charAt(0))) {
             return true;
         }
         return false;
@@ -1870,53 +1791,64 @@
 
     public void revertLastWord(boolean deleteChar) {
         final int length = mComposing.length();
-        if (!mHasValidSuggestions && length > 0) {
+        if (!mHasUncommittedTypedChars && length > 0) {
             final InputConnection ic = getCurrentInputConnection();
             final CharSequence punctuation = ic.getTextBeforeCursor(1, 0);
             if (deleteChar) ic.deleteSurroundingText(1, 0);
             int toDelete = mCommittedLength;
             final CharSequence toTheLeft = ic.getTextBeforeCursor(mCommittedLength, 0);
-            if (!TextUtils.isEmpty(toTheLeft) && isWordSeparator(toTheLeft.charAt(0))) {
+            if (!TextUtils.isEmpty(toTheLeft)
+                    && mSettingsValues.isWordSeparator(toTheLeft.charAt(0))) {
                 toDelete--;
             }
             ic.deleteSurroundingText(toDelete, 0);
             // Re-insert punctuation only when the deleted character was word separator and the
             // composing text wasn't equal to the auto-corrected text.
             if (deleteChar
-                    && !TextUtils.isEmpty(punctuation) && isWordSeparator(punctuation.charAt(0))
+                    && !TextUtils.isEmpty(punctuation)
+                    && mSettingsValues.isWordSeparator(punctuation.charAt(0))
                     && !TextUtils.equals(mComposing, toTheLeft)) {
                 ic.commitText(mComposing, 1);
                 TextEntryState.acceptedTyped(mComposing);
                 ic.commitText(punctuation, 1);
-                TextEntryState.typedCharacter(punctuation.charAt(0), true);
+                TextEntryState.typedCharacter(punctuation.charAt(0), true,
+                        WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
                 // Clear composing text
                 mComposing.setLength(0);
             } else {
-                mHasValidSuggestions = true;
+                mHasUncommittedTypedChars = true;
                 ic.setComposingText(mComposing, 1);
                 TextEntryState.backspace();
             }
+            mHandler.cancelUpdateBigramPredictions();
             mHandler.postUpdateSuggestions();
         } else {
             sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
         }
     }
 
-    protected String getWordSeparators() {
-        return mWordSeparators;
+    public boolean revertDoubleSpace() {
+        mHandler.cancelDoubleSpacesTimer();
+        final InputConnection ic = getCurrentInputConnection();
+        // Here we test whether we indeed have a period and a space before us. This should not
+        // be needed, but it's there just in case something went wrong.
+        final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
+        if (!". ".equals(textBeforeCursor))
+            return false;
+        ic.beginBatchEdit();
+        ic.deleteSurroundingText(2, 0);
+        ic.commitText("  ", 1);
+        ic.endBatchEdit();
+        return true;
     }
 
     public boolean isWordSeparator(int code) {
-        String separators = getWordSeparators();
-        return separators.contains(String.valueOf((char)code));
+        return mSettingsValues.isWordSeparator(code);
     }
 
-    private boolean isSentenceSeparator(int code) {
-        return mSentenceSeparators.contains(String.valueOf((char)code));
-    }
-
-    private void sendSpace() {
+    private void sendMagicSpace() {
         sendKeyChar((char)Keyboard.CODE_SPACE);
+        mJustAddedMagicSpace = true;
         mKeyboardSwitcher.updateShiftState();
     }
 
@@ -1924,28 +1856,32 @@
         return mWord.isFirstCharCapitalized();
     }
 
-    // Notify that language or mode have been changed and toggleLanguage will update KeyboaredID
+    // Notify that language or mode have been changed and toggleLanguage will update KeyboardID
     // according to new language or mode.
     public void onRefreshKeyboard() {
-        toggleLanguage(true, true);
-    }
-
-    // "reset" and "next" are used only for USE_SPACEBAR_LANGUAGE_SWITCHER.
-    private void toggleLanguage(boolean reset, boolean next) {
-        if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()) {
-            mSubtypeSwitcher.toggleLanguage(reset, next);
-        }
         // Reload keyboard because the current language has been changed.
         mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(),
-                mSubtypeSwitcher.isShortcutImeEnabled() && mVoiceConnector.isVoiceButtonEnabled(),
-                mVoiceConnector.isVoiceButtonOnPrimary());
+                mSubtypeSwitcher.isShortcutImeEnabled() && mVoiceProxy.isVoiceButtonEnabled(),
+                mVoiceProxy.isVoiceButtonOnPrimary());
         initSuggest();
+        loadSettings();
         mKeyboardSwitcher.updateShiftState();
     }
 
+    // "reset" and "next" are used only for USE_SPACEBAR_LANGUAGE_SWITCHER.
+    private void toggleLanguage(boolean next) {
+        if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()) {
+            mSubtypeSwitcher.toggleLanguage(next);
+        }
+        // The following is necessary because on API levels < 10, we don't get notified when
+        // subtype changes.
+        if (!CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED)
+            onRefreshKeyboard();
+     }
+
     @Override
     public void onSwipeDown() {
-        if (mConfigSwipeDownDismissKeyboardEnabled)
+        if (mSettingsValues.mSwipeDownDismissKeyboardEnabled)
             handleClose();
     }
 
@@ -1964,21 +1900,18 @@
         } else {
             switcher.onOtherKeyPressed();
         }
-        mAccessibilityUtils.onPress(primaryCode, switcher);
     }
 
     @Override
     public void onRelease(int primaryCode, boolean withSliding) {
         KeyboardSwitcher switcher = mKeyboardSwitcher;
         // Reset any drag flags in the keyboard
-        switcher.keyReleased();
         final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
         if (distinctMultiTouch && primaryCode == Keyboard.CODE_SHIFT) {
             switcher.onReleaseShift(withSliding);
         } else if (distinctMultiTouch && primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
             switcher.onReleaseSymbol();
         }
-        mAccessibilityUtils.onRelease(primaryCode, switcher);
     }
 
 
@@ -2001,7 +1934,7 @@
             mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
         }
         if (mAudioManager != null) {
-            mSilentMode = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL);
+            mSilentModeOn = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL);
         }
     }
 
@@ -2009,11 +1942,11 @@
         // if mAudioManager is null, we don't have the ringer state yet
         // mAudioManager will be set by updateRingerMode
         if (mAudioManager == null) {
-            if (mKeyboardSwitcher.getInputView() != null) {
+            if (mKeyboardSwitcher.getKeyboardView() != null) {
                 updateRingerMode();
             }
         }
-        if (mSoundOn && !mSilentMode) {
+        if (isSoundOn()) {
             // FIXME: Volume and enable should come from UI settings
             // FIXME: These should be triggered after auto-repeat logic
             int sound = AudioManager.FX_KEYPRESS_STANDARD;
@@ -2033,10 +1966,10 @@
     }
 
     public void vibrate() {
-        if (!mVibrateOn) {
+        if (!mSettingsValues.mVibrateOn) {
             return;
         }
-        LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
+        LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
         if (inputView != null) {
             inputView.performHapticFeedback(
                     HapticFeedbackConstants.KEYBOARD_TAP,
@@ -2044,28 +1977,24 @@
         }
     }
 
-    public void promoteToUserDictionary(String word, int frequency) {
-        if (mUserDictionary.isValidWord(word)) return;
-        mUserDictionary.addWord(word, frequency);
-    }
-
     public WordComposer getCurrentWord() {
         return mWord;
     }
 
-    public boolean getPopupOn() {
-        return mPopupOn;
+    boolean isSoundOn() {
+        return mSettingsValues.mSoundOn && !mSilentModeOn;
     }
 
     private void updateCorrectionMode() {
         // TODO: cleanup messy flags
         mHasDictionary = mSuggest != null ? mSuggest.hasMainDictionary() : false;
-        mAutoCorrectOn = (mAutoCorrectEnabled || mQuickFixes)
-                && !mInputTypeNoAutoCorrect && mHasDictionary;
-        mCorrectionMode = (mAutoCorrectOn && mAutoCorrectEnabled)
+        final boolean shouldAutoCorrect = (mSettingsValues.mAutoCorrectEnabled
+                || mSettingsValues.mQuickFixes) && !mInputTypeNoAutoCorrect && mHasDictionary;
+        mCorrectionMode = (shouldAutoCorrect && mSettingsValues.mAutoCorrectEnabled)
                 ? Suggest.CORRECTION_FULL
-                : (mAutoCorrectOn ? Suggest.CORRECTION_BASIC : Suggest.CORRECTION_NONE);
-        mCorrectionMode = (mBigramSuggestionEnabled && mAutoCorrectOn && mAutoCorrectEnabled)
+                : (shouldAutoCorrect ? Suggest.CORRECTION_BASIC : Suggest.CORRECTION_NONE);
+        mCorrectionMode = (mSettingsValues.mBigramSuggestionEnabled && shouldAutoCorrect
+                && mSettingsValues.mAutoCorrectEnabled)
                 ? Suggest.CORRECTION_FULL_BIGRAM : mCorrectionMode;
         if (mSuggest != null) {
             mSuggest.setCorrectionMode(mCorrectionMode);
@@ -2074,12 +2003,11 @@
 
     private void updateAutoTextEnabled() {
         if (mSuggest == null) return;
-        mSuggest.setQuickFixesEnabled(mQuickFixes
+        mSuggest.setQuickFixesEnabled(mSettingsValues.mQuickFixes
                 && SubtypeSwitcher.getInstance().isSystemLanguageSameAsInputLanguage());
     }
 
-    private void updateSuggestionVisibility(SharedPreferences prefs) {
-        final Resources res = mResources;
+    private void updateSuggestionVisibility(final SharedPreferences prefs, final Resources res) {
         final String suggestionVisiblityStr = prefs.getString(
                 Settings.PREF_SHOW_SUGGESTIONS_SETTING,
                 res.getString(R.string.prefs_suggestion_visibility_default_value));
@@ -2107,121 +2035,6 @@
         startActivity(intent);
     }
 
-    private void loadSettings(EditorInfo attribute) {
-        // Get the settings preferences
-        final SharedPreferences prefs = mPrefs;
-        Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
-        mVibrateOn = vibrator != null && vibrator.hasVibrator()
-                && prefs.getBoolean(Settings.PREF_VIBRATE_ON, false);
-        mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON,
-                mResources.getBoolean(R.bool.config_default_sound_enabled));
-
-        mPopupOn = isPopupEnabled(prefs);
-        mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
-        mQuickFixes = isQuickFixesEnabled(prefs);
-
-        mAutoCorrectEnabled = isAutoCorrectEnabled(prefs);
-        mBigramSuggestionEnabled = mAutoCorrectEnabled && isBigramSuggestionEnabled(prefs);
-        loadAndSetAutoCorrectionThreshold(prefs);
-
-        mVoiceConnector.loadSettings(attribute, prefs);
-
-        updateCorrectionMode();
-        updateAutoTextEnabled();
-        updateSuggestionVisibility(prefs);
-        SubtypeSwitcher.getInstance().loadSettings();
-    }
-
-    /**
-     *  Load Auto correction threshold from SharedPreferences, and modify mSuggest's threshold.
-     */
-    private void loadAndSetAutoCorrectionThreshold(SharedPreferences sp) {
-        // When mSuggest is not initialized, cannnot modify mSuggest's threshold.
-        if (mSuggest == null) return;
-        // When auto correction setting is turned off, the threshold is ignored.
-        if (!isAutoCorrectEnabled(sp)) return;
-
-        final String currentAutoCorrectionSetting = sp.getString(
-                Settings.PREF_AUTO_CORRECTION_THRESHOLD,
-                mResources.getString(R.string.auto_correction_threshold_mode_index_modest));
-        final String[] autoCorrectionThresholdValues = mResources.getStringArray(
-                R.array.auto_correction_threshold_values);
-        // When autoCrrectionThreshold is greater than 1.0, auto correction is virtually turned off.
-        double autoCorrectionThreshold = Double.MAX_VALUE;
-        try {
-            final int arrayIndex = Integer.valueOf(currentAutoCorrectionSetting);
-            if (arrayIndex >= 0 && arrayIndex < autoCorrectionThresholdValues.length) {
-                autoCorrectionThreshold = Double.parseDouble(
-                        autoCorrectionThresholdValues[arrayIndex]);
-            }
-        } catch (NumberFormatException e) {
-            // Whenever the threshold settings are correct, never come here.
-            autoCorrectionThreshold = Double.MAX_VALUE;
-            Log.w(TAG, "Cannot load auto correction threshold setting."
-                    + " currentAutoCorrectionSetting: " + currentAutoCorrectionSetting
-                    + ", autoCorrectionThresholdValues: "
-                    + Arrays.toString(autoCorrectionThresholdValues));
-        }
-        // TODO: This should be refactored :
-        //           setAutoCorrectionThreshold should be called outside of this method.
-        mSuggest.setAutoCorrectionThreshold(autoCorrectionThreshold);
-    }
-
-    private boolean isPopupEnabled(SharedPreferences sp) {
-        final boolean showPopupOption = getResources().getBoolean(
-                R.bool.config_enable_show_popup_on_keypress_option);
-        if (!showPopupOption) return mResources.getBoolean(R.bool.config_default_popup_preview);
-        return sp.getBoolean(Settings.PREF_POPUP_ON,
-                mResources.getBoolean(R.bool.config_default_popup_preview));
-    }
-
-    private boolean isQuickFixesEnabled(SharedPreferences sp) {
-        final boolean showQuickFixesOption = mResources.getBoolean(
-                R.bool.config_enable_quick_fixes_option);
-        if (!showQuickFixesOption) {
-            return isAutoCorrectEnabled(sp);
-        }
-        return sp.getBoolean(Settings.PREF_QUICK_FIXES, mResources.getBoolean(
-                R.bool.config_default_quick_fixes));
-    }
-
-    private boolean isAutoCorrectEnabled(SharedPreferences sp) {
-        final String currentAutoCorrectionSetting = sp.getString(
-                Settings.PREF_AUTO_CORRECTION_THRESHOLD,
-                mResources.getString(R.string.auto_correction_threshold_mode_index_modest));
-        final String autoCorrectionOff = mResources.getString(
-                R.string.auto_correction_threshold_mode_index_off);
-        return !currentAutoCorrectionSetting.equals(autoCorrectionOff);
-    }
-
-    private boolean isBigramSuggestionEnabled(SharedPreferences sp) {
-        final boolean showBigramSuggestionsOption = mResources.getBoolean(
-                R.bool.config_enable_bigram_suggestions_option);
-        if (!showBigramSuggestionsOption) {
-            return isAutoCorrectEnabled(sp);
-        }
-        return sp.getBoolean(Settings.PREF_BIGRAM_SUGGESTIONS, mResources.getBoolean(
-                R.bool.config_default_bigram_suggestions));
-    }
-
-    private void initSuggestPuncList() {
-        if (mSuggestPuncs != null || mSuggestPuncList != null)
-            return;
-        SuggestedWords.Builder builder = new SuggestedWords.Builder();
-        String puncs = mResources.getString(R.string.suggested_punctuations);
-        if (puncs != null) {
-            for (int i = 0; i < puncs.length(); i++) {
-                builder.addWord(puncs.subSequence(i, i + 1));
-            }
-        }
-        mSuggestPuncList = builder.build();
-        mSuggestPuncs = puncs;
-    }
-
-    private boolean isSuggestedPunctuation(int code) {
-        return mSuggestPuncs.contains(String.valueOf((char)code));
-    }
-
     private void showSubtypeSelectorAndSettings() {
         final CharSequence title = getString(R.string.english_ime_input_options);
         final CharSequence[] items = new CharSequence[] {
@@ -2235,13 +2048,10 @@
                 di.dismiss();
                 switch (position) {
                 case 0:
-                    Intent intent = new Intent(
-                            android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
-                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    Intent intent = CompatUtils.getInputLanguageSelectionIntent(
+                            mInputMethodId, Intent.FLAG_ACTIVITY_NEW_TASK
                             | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
                             | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-                    intent.putExtra(android.provider.Settings.EXTRA_INPUT_METHOD_ID,
-                            mInputMethodId);
                     startActivity(intent);
                     break;
                 case 1:
@@ -2278,7 +2088,7 @@
 
     private void showOptionsMenuInternal(CharSequence title, CharSequence[] items,
             DialogInterface.OnClickListener listener) {
-        final IBinder windowToken = mKeyboardSwitcher.getInputView().getWindowToken();
+        final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
         if (windowToken == null) return;
         AlertDialog.Builder builder = new AlertDialog.Builder(this);
         builder.setCancelable(true);
@@ -2307,14 +2117,14 @@
         p.println("  mComposing=" + mComposing.toString());
         p.println("  mIsSuggestionsRequested=" + mIsSettingsSuggestionStripOn);
         p.println("  mCorrectionMode=" + mCorrectionMode);
-        p.println("  mHasValidSuggestions=" + mHasValidSuggestions);
-        p.println("  mAutoCorrectOn=" + mAutoCorrectOn);
-        p.println("  mAutoSpace=" + mAutoSpace);
+        p.println("  mHasUncommittedTypedChars=" + mHasUncommittedTypedChars);
+        p.println("  mAutoCorrectEnabled=" + mSettingsValues.mAutoCorrectEnabled);
+        p.println("  mShouldInsertMagicSpace=" + mShouldInsertMagicSpace);
         p.println("  mApplicationSpecifiedCompletionOn=" + mApplicationSpecifiedCompletionOn);
         p.println("  TextEntryState.state=" + TextEntryState.getState());
-        p.println("  mSoundOn=" + mSoundOn);
-        p.println("  mVibrateOn=" + mVibrateOn);
-        p.println("  mPopupOn=" + mPopupOn);
+        p.println("  mSoundOn=" + mSettingsValues.mSoundOn);
+        p.println("  mVibrateOn=" + mSettingsValues.mVibrateOn);
+        p.println("  mKeyPreviewPopupOn=" + mSettingsValues.mKeyPreviewPopupOn);
     }
 
     // Characters per second measurement
@@ -2334,9 +2144,4 @@
         for (int i = 0; i < CPS_BUFFER_SIZE; i++) total += mCpsIntervals[i];
         System.out.println("CPS = " + ((CPS_BUFFER_SIZE * 1000f) / total));
     }
-
-    @Override
-    public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
-        SubtypeSwitcher.getInstance().updateSubtype(subtype);
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
index aaecfff..e460471 100644
--- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java
+++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
@@ -45,10 +45,10 @@
             String before, String after, int position, List<CharSequence> suggestions) {
    }
 
-    public static void logOnAutoSuggestion(String before, String after) {
+    public static void logOnAutoCorrection(String before, String after, int separatorCode) {
     }
 
-    public static void logOnAutoSuggestionCanceled() {
+    public static void logOnAutoCorrectionCancelled() {
     }
 
     public static void logOnDelete() {
@@ -57,6 +57,9 @@
     public static void logOnInputChar() {
     }
 
+    public static void logOnInputSeparator() {
+    }
+
     public static void logOnException(String metaData, Throwable e) {
     }
 
diff --git a/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java
new file mode 100644
index 0000000..eb740e1
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java
@@ -0,0 +1,29 @@
+/*
+ * 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 java.util.List;
+import java.util.Locale;
+
+class PrivateBinaryDictionaryGetter {
+    private PrivateBinaryDictionaryGetter() {}
+    public static List<AssetFileAddress> getDictionaryFiles(Locale locale, Context context) {
+        return null;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 341d5ad..3ad2a59 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -16,17 +16,21 @@
 
 package com.android.inputmethod.latin;
 
-import com.android.inputmethod.voice.VoiceIMEConnector;
-import com.android.inputmethod.voice.VoiceInputLogger;
+import com.android.inputmethod.compat.CompatUtils;
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
+import com.android.inputmethod.deprecated.VoiceProxy;
+import com.android.inputmethod.compat.VibratorCompatWrapper;
 
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.backup.BackupManager;
+import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.res.Resources;
 import android.os.Bundle;
-import android.os.Vibrator;
 import android.preference.CheckBoxPreference;
 import android.preference.ListPreference;
 import android.preference.Preference;
@@ -41,6 +45,7 @@
 import android.util.Log;
 import android.widget.TextView;
 
+import java.util.Arrays;
 import java.util.Locale;
 
 public class Settings extends PreferenceActivity
@@ -51,7 +56,7 @@
     public static final String PREF_GENERAL_SETTINGS_KEY = "general_settings";
     public static final String PREF_VIBRATE_ON = "vibrate_on";
     public static final String PREF_SOUND_ON = "sound_on";
-    public static final String PREF_POPUP_ON = "popup_on";
+    public static final String PREF_KEY_PREVIEW_POPUP_ON = "popup_on";
     public static final String PREF_RECORRECTION_ENABLED = "recorrection_enabled";
     public static final String PREF_AUTO_CAP = "auto_cap";
     public static final String PREF_SETTINGS_KEY = "settings_key";
@@ -60,29 +65,248 @@
     public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
     public static final String PREF_SUBTYPES = "subtype_settings";
 
-    public static final String PREF_PREDICTION_SETTINGS_KEY = "prediction_settings";
+    public static final String PREF_CORRECTION_SETTINGS_KEY = "correction_settings";
     public static final String PREF_QUICK_FIXES = "quick_fixes";
     public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting";
     public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold";
+    public static final String PREF_DEBUG_SETTINGS = "debug_settings";
+
+    public static final String PREF_NGRAM_SETTINGS_KEY = "ngram_settings";
     public static final String PREF_BIGRAM_SUGGESTIONS = "bigram_suggestion";
+    public static final String PREF_BIGRAM_PREDICTIONS = "bigram_prediction";
+
+    public static final String PREF_MISC_SETTINGS_KEY = "misc_settings";
+
+    public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY =
+            "pref_key_preview_popup_dismiss_delay";
+    public static final String PREF_KEY_USE_CONTACTS_DICT =
+            "pref_key_use_contacts_dict";
 
     public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
 
     // Dialog ids
     private static final int VOICE_INPUT_CONFIRM_DIALOG = 0;
 
+    public static class Values {
+        // From resources:
+        public final boolean mSwipeDownDismissKeyboardEnabled;
+        public final int mDelayBeforeFadeoutLanguageOnSpacebar;
+        public final int mDelayUpdateSuggestions;
+        public final int mDelayUpdateOldSuggestions;
+        public final int mDelayUpdateShiftState;
+        public final int mDurationOfFadeoutLanguageOnSpacebar;
+        public final float mFinalFadeoutFactorOfLanguageOnSpacebar;
+        public final long mDoubleSpacesTurnIntoPeriodTimeout;
+        public final String mWordSeparators;
+        public final String mMagicSpaceStrippers;
+        public final String mMagicSpaceSwappers;
+        public final String mSuggestPuncs;
+        public final SuggestedWords mSuggestPuncList;
+
+        // From preferences:
+        public final boolean mSoundOn; // Sound setting private to Latin IME (see mSilentModeOn)
+        public final boolean mVibrateOn;
+        public final boolean mKeyPreviewPopupOn;
+        public final int mKeyPreviewPopupDismissDelay;
+        public final boolean mAutoCap;
+        public final boolean mQuickFixes;
+        public final boolean mAutoCorrectEnabled;
+        public final double mAutoCorrectionThreshold;
+        // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
+        public final boolean mBigramSuggestionEnabled;
+        // Prediction: use bigrams to predict the next word when there is no input for it yet
+        public final boolean mBigramPredictionEnabled;
+        public final boolean mUseContactsDict;
+
+        public Values(final SharedPreferences prefs, final Context context,
+                final String localeStr) {
+            final Resources res = context.getResources();
+            final Locale savedLocale;
+            if (null != localeStr) {
+                final Locale keyboardLocale = Utils.constructLocaleFromString(localeStr);
+                savedLocale = Utils.setSystemLocale(res, keyboardLocale);
+            } else {
+                savedLocale = null;
+            }
+
+            // Get the resources
+            mSwipeDownDismissKeyboardEnabled = res.getBoolean(
+                    R.bool.config_swipe_down_dismiss_keyboard_enabled);
+            mDelayBeforeFadeoutLanguageOnSpacebar = res.getInteger(
+                    R.integer.config_delay_before_fadeout_language_on_spacebar);
+            mDelayUpdateSuggestions =
+                    res.getInteger(R.integer.config_delay_update_suggestions);
+            mDelayUpdateOldSuggestions = res.getInteger(
+                    R.integer.config_delay_update_old_suggestions);
+            mDelayUpdateShiftState =
+                    res.getInteger(R.integer.config_delay_update_shift_state);
+            mDurationOfFadeoutLanguageOnSpacebar = res.getInteger(
+                    R.integer.config_duration_of_fadeout_language_on_spacebar);
+            mFinalFadeoutFactorOfLanguageOnSpacebar = res.getInteger(
+                    R.integer.config_final_fadeout_percentage_of_language_on_spacebar) / 100.0f;
+            mDoubleSpacesTurnIntoPeriodTimeout = res.getInteger(
+                    R.integer.config_double_spaces_turn_into_period_timeout);
+            mMagicSpaceStrippers = res.getString(R.string.magic_space_stripping_symbols);
+            mMagicSpaceSwappers = res.getString(R.string.magic_space_swapping_symbols);
+            String wordSeparators = mMagicSpaceStrippers + mMagicSpaceSwappers
+                    + res.getString(R.string.magic_space_promoting_symbols);
+            final String notWordSeparators = res.getString(R.string.non_word_separator_symbols);
+            for (int i = notWordSeparators.length() - 1; i >= 0; --i) {
+                wordSeparators = wordSeparators.replace(notWordSeparators.substring(i, i + 1), "");
+            }
+            mWordSeparators = wordSeparators;
+            mSuggestPuncs = res.getString(R.string.suggested_punctuations);
+            // TODO: it would be nice not to recreate this each time we change the configuration
+            mSuggestPuncList = createSuggestPuncList(mSuggestPuncs);
+
+            // Get the settings preferences
+            final boolean hasVibrator = VibratorCompatWrapper.getInstance(context).hasVibrator();
+            mVibrateOn = hasVibrator && prefs.getBoolean(Settings.PREF_VIBRATE_ON, false);
+            mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON,
+                    res.getBoolean(R.bool.config_default_sound_enabled));
+
+            mKeyPreviewPopupOn = isKeyPreviewPopupEnabled(prefs, res);
+            mKeyPreviewPopupDismissDelay = getKeyPreviewPopupDismissDelay(prefs, res);
+            mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
+            mQuickFixes = isQuickFixesEnabled(prefs, res);
+
+            mAutoCorrectEnabled = isAutoCorrectEnabled(prefs, res);
+            mBigramSuggestionEnabled = mAutoCorrectEnabled
+                    && isBigramSuggestionEnabled(prefs, res, mAutoCorrectEnabled);
+            mBigramPredictionEnabled = mBigramSuggestionEnabled
+                    && isBigramPredictionEnabled(prefs, res);
+
+            mAutoCorrectionThreshold = getAutoCorrectionThreshold(prefs, res);
+
+            mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
+
+            Utils.setSystemLocale(res, savedLocale);
+        }
+
+        public boolean isSuggestedPunctuation(int code) {
+            return mSuggestPuncs.contains(String.valueOf((char)code));
+        }
+
+        public boolean isWordSeparator(int code) {
+            return mWordSeparators.contains(String.valueOf((char)code));
+        }
+
+        public boolean isMagicSpaceStripper(int code) {
+            return mMagicSpaceStrippers.contains(String.valueOf((char)code));
+        }
+
+        public boolean isMagicSpaceSwapper(int code) {
+            return mMagicSpaceSwappers.contains(String.valueOf((char)code));
+        }
+
+        // Helper methods
+        private static boolean isQuickFixesEnabled(SharedPreferences sp, Resources resources) {
+            final boolean showQuickFixesOption = resources.getBoolean(
+                    R.bool.config_enable_quick_fixes_option);
+            if (!showQuickFixesOption) {
+                return isAutoCorrectEnabled(sp, resources);
+            }
+            return sp.getBoolean(Settings.PREF_QUICK_FIXES, resources.getBoolean(
+                    R.bool.config_default_quick_fixes));
+        }
+
+        private static boolean isAutoCorrectEnabled(SharedPreferences sp, Resources resources) {
+            final String currentAutoCorrectionSetting = sp.getString(
+                    Settings.PREF_AUTO_CORRECTION_THRESHOLD,
+                    resources.getString(R.string.auto_correction_threshold_mode_index_modest));
+            final String autoCorrectionOff = resources.getString(
+                    R.string.auto_correction_threshold_mode_index_off);
+            return !currentAutoCorrectionSetting.equals(autoCorrectionOff);
+        }
+
+        // Public to access from KeyboardSwitcher. Should it have access to some
+        // process-global instance instead?
+        public static boolean isKeyPreviewPopupEnabled(SharedPreferences sp, Resources resources) {
+            final boolean showPopupOption = resources.getBoolean(
+                    R.bool.config_enable_show_popup_on_keypress_option);
+            if (!showPopupOption) return resources.getBoolean(R.bool.config_default_popup_preview);
+            return sp.getBoolean(Settings.PREF_KEY_PREVIEW_POPUP_ON,
+                    resources.getBoolean(R.bool.config_default_popup_preview));
+        }
+
+        // Likewise
+        public static int getKeyPreviewPopupDismissDelay(SharedPreferences sp,
+                Resources resources) {
+            return Integer.parseInt(sp.getString(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
+                    Integer.toString(resources.getInteger(R.integer.config_delay_after_preview))));
+        }
+
+        private static boolean isBigramSuggestionEnabled(SharedPreferences sp, Resources resources,
+                boolean autoCorrectEnabled) {
+            final boolean showBigramSuggestionsOption = resources.getBoolean(
+                    R.bool.config_enable_bigram_suggestions_option);
+            if (!showBigramSuggestionsOption) {
+                return autoCorrectEnabled;
+            }
+            return sp.getBoolean(Settings.PREF_BIGRAM_SUGGESTIONS, resources.getBoolean(
+                    R.bool.config_default_bigram_suggestions));
+        }
+
+        private static boolean isBigramPredictionEnabled(SharedPreferences sp,
+                Resources resources) {
+            return sp.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, resources.getBoolean(
+                    R.bool.config_default_bigram_prediction));
+        }
+
+        private static double getAutoCorrectionThreshold(SharedPreferences sp,
+                Resources resources) {
+            final String currentAutoCorrectionSetting = sp.getString(
+                    Settings.PREF_AUTO_CORRECTION_THRESHOLD,
+                    resources.getString(R.string.auto_correction_threshold_mode_index_modest));
+            final String[] autoCorrectionThresholdValues = resources.getStringArray(
+                    R.array.auto_correction_threshold_values);
+            // When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off.
+            double autoCorrectionThreshold = Double.MAX_VALUE;
+            try {
+                final int arrayIndex = Integer.valueOf(currentAutoCorrectionSetting);
+                if (arrayIndex >= 0 && arrayIndex < autoCorrectionThresholdValues.length) {
+                    autoCorrectionThreshold = Double.parseDouble(
+                            autoCorrectionThresholdValues[arrayIndex]);
+                }
+            } catch (NumberFormatException e) {
+                // Whenever the threshold settings are correct, never come here.
+                autoCorrectionThreshold = Double.MAX_VALUE;
+                Log.w(TAG, "Cannot load auto correction threshold setting."
+                        + " currentAutoCorrectionSetting: " + currentAutoCorrectionSetting
+                        + ", autoCorrectionThresholdValues: "
+                        + Arrays.toString(autoCorrectionThresholdValues));
+            }
+            return autoCorrectionThreshold;
+        }
+
+        private static SuggestedWords createSuggestPuncList(final String puncs) {
+            SuggestedWords.Builder builder = new SuggestedWords.Builder();
+            if (puncs != null) {
+                for (int i = 0; i < puncs.length(); i++) {
+                    builder.addWord(puncs.subSequence(i, i + 1));
+                }
+            }
+            return builder.build();
+        }
+    }
+
     private PreferenceScreen mInputLanguageSelection;
     private CheckBoxPreference mQuickFixes;
     private ListPreference mVoicePreference;
     private ListPreference mSettingsKeyPreference;
     private ListPreference mShowCorrectionSuggestionsPreference;
     private ListPreference mAutoCorrectionThreshold;
+    private ListPreference mKeyPreviewPopupDismissDelay;
+    // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
     private CheckBoxPreference mBigramSuggestion;
+    // Prediction: use bigrams to predict the next word when there is no input for it yet
+    private CheckBoxPreference mBigramPrediction;
+    private Preference mDebugSettingsPreference;
     private boolean mVoiceOn;
 
     private AlertDialog mDialog;
 
-    private VoiceInputLogger mLogger;
+    private VoiceProxy.VoiceLoggerWrapper mVoiceLogger;
 
     private boolean mOkClicked = false;
     private String mVoiceModeOff;
@@ -92,11 +316,14 @@
                 R.string.auto_correction_threshold_mode_index_off);
         final String currentSetting = mAutoCorrectionThreshold.getValue();
         mBigramSuggestion.setEnabled(!currentSetting.equals(autoCorrectionOff));
+        mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff));
     }
 
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
+        final Resources res = getResources();
+
         addPreferencesFromResource(R.xml.prefs);
         mInputLanguageSelection = (PreferenceScreen) findPreference(PREF_SUBTYPES);
         mInputLanguageSelection.setOnPreferenceClickListener(this);
@@ -111,69 +338,92 @@
         mVoiceModeOff = getString(R.string.voice_mode_off);
         mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff)
                 .equals(mVoiceModeOff));
-        mLogger = VoiceInputLogger.getLogger(this);
+        mVoiceLogger = VoiceProxy.VoiceLoggerWrapper.getInstance(this);
 
         mAutoCorrectionThreshold = (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD);
         mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTIONS);
+        mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS);
+        mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS);
+        if (mDebugSettingsPreference != null) {
+            final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN);
+            debugSettingsIntent.setClassName(getPackageName(), DebugSettings.class.getName());
+            mDebugSettingsPreference.setIntent(debugSettingsIntent);
+        }
+
         ensureConsistencyOfAutoCorrectionSettings();
 
         final PreferenceGroup generalSettings =
                 (PreferenceGroup) findPreference(PREF_GENERAL_SETTINGS_KEY);
         final PreferenceGroup textCorrectionGroup =
-                (PreferenceGroup) findPreference(PREF_PREDICTION_SETTINGS_KEY);
+                (PreferenceGroup) findPreference(PREF_CORRECTION_SETTINGS_KEY);
 
-        final boolean showSettingsKeyOption = getResources().getBoolean(
+        final boolean showSettingsKeyOption = res.getBoolean(
                 R.bool.config_enable_show_settings_key_option);
         if (!showSettingsKeyOption) {
             generalSettings.removePreference(mSettingsKeyPreference);
         }
 
-        final boolean showVoiceKeyOption = getResources().getBoolean(
+        final boolean showVoiceKeyOption = res.getBoolean(
                 R.bool.config_enable_show_voice_key_option);
         if (!showVoiceKeyOption) {
             generalSettings.removePreference(mVoicePreference);
         }
 
-        Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
-        if (vibrator == null || !vibrator.hasVibrator()) {
+        if (!VibratorCompatWrapper.getInstance(this).hasVibrator()) {
             generalSettings.removePreference(findPreference(PREF_VIBRATE_ON));
         }
 
-        final boolean showSubtypeSettings = getResources().getBoolean(
-                R.bool.config_enable_show_subtype_settings);
-        if (!showSubtypeSettings) {
+        if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) {
             generalSettings.removePreference(findPreference(PREF_SUBTYPES));
         }
 
-        final boolean showPopupOption = getResources().getBoolean(
+        final boolean showPopupOption = res.getBoolean(
                 R.bool.config_enable_show_popup_on_keypress_option);
         if (!showPopupOption) {
-            generalSettings.removePreference(findPreference(PREF_POPUP_ON));
+            generalSettings.removePreference(findPreference(PREF_KEY_PREVIEW_POPUP_ON));
         }
 
-        final boolean showRecorrectionOption = getResources().getBoolean(
+        final boolean showRecorrectionOption = res.getBoolean(
                 R.bool.config_enable_show_recorrection_option);
         if (!showRecorrectionOption) {
             generalSettings.removePreference(findPreference(PREF_RECORRECTION_ENABLED));
         }
 
-        final boolean showQuickFixesOption = getResources().getBoolean(
+        final boolean showQuickFixesOption = res.getBoolean(
                 R.bool.config_enable_quick_fixes_option);
         if (!showQuickFixesOption) {
             textCorrectionGroup.removePreference(findPreference(PREF_QUICK_FIXES));
         }
 
-        final boolean showBigramSuggestionsOption = getResources().getBoolean(
+        final boolean showBigramSuggestionsOption = res.getBoolean(
                 R.bool.config_enable_bigram_suggestions_option);
         if (!showBigramSuggestionsOption) {
             textCorrectionGroup.removePreference(findPreference(PREF_BIGRAM_SUGGESTIONS));
+            textCorrectionGroup.removePreference(findPreference(PREF_BIGRAM_PREDICTIONS));
         }
 
-        final boolean showUsabilityModeStudyOption = getResources().getBoolean(
+        final boolean showUsabilityModeStudyOption = res.getBoolean(
                 R.bool.config_enable_usability_study_mode_option);
         if (!showUsabilityModeStudyOption) {
             getPreferenceScreen().removePreference(findPreference(PREF_USABILITY_STUDY_MODE));
         }
+
+        mKeyPreviewPopupDismissDelay =
+                (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
+        final String[] entries = new String[] {
+                res.getString(R.string.key_preview_popup_dismiss_no_delay),
+                res.getString(R.string.key_preview_popup_dismiss_default_delay),
+        };
+        final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
+                R.integer.config_delay_after_preview));
+        mKeyPreviewPopupDismissDelay.setEntries(entries);
+        mKeyPreviewPopupDismissDelay.setEntryValues(
+                new String[] { "0", popupDismissDelayDefaultValue });
+        if (null == mKeyPreviewPopupDismissDelay.getValue()) {
+            mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
+        }
+        mKeyPreviewPopupDismissDelay.setEnabled(
+                Settings.Values.isKeyPreviewPopupEnabled(prefs, res));
     }
 
     @Override
@@ -181,10 +431,10 @@
         super.onResume();
         int autoTextSize = AutoText.getSize(getListView());
         if (autoTextSize < 1) {
-            ((PreferenceGroup) findPreference(PREF_PREDICTION_SETTINGS_KEY))
+            ((PreferenceGroup) findPreference(PREF_CORRECTION_SETTINGS_KEY))
                     .removePreference(mQuickFixes);
         }
-        if (!VoiceIMEConnector.VOICE_INSTALLED
+        if (!VoiceProxy.VOICE_INSTALLED
                 || !SpeechRecognizer.isRecognitionAvailable(this)) {
             getPreferenceScreen().removePreference(mVoicePreference);
         } else {
@@ -192,6 +442,7 @@
         }
         updateSettingsKeySummary();
         updateShowCorrectionSuggestionsSummary();
+        updateKeyPreviewPopupDelaySummary();
     }
 
     @Override
@@ -210,6 +461,12 @@
                     .equals(mVoiceModeOff)) {
                 showVoiceConfirmation();
             }
+        } else if (key.equals(PREF_KEY_PREVIEW_POPUP_ON)) {
+            final ListPreference popupDismissDelay =
+                (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
+            if (null != popupDismissDelay) {
+                popupDismissDelay.setEnabled(prefs.getBoolean(PREF_KEY_PREVIEW_POPUP_ON, true));
+            }
         }
         ensureConsistencyOfAutoCorrectionSettings();
         mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff)
@@ -217,21 +474,15 @@
         updateVoiceModeSummary();
         updateSettingsKeySummary();
         updateShowCorrectionSuggestionsSummary();
+        updateKeyPreviewPopupDelaySummary();
     }
 
     @Override
     public boolean onPreferenceClick(Preference pref) {
         if (pref == mInputLanguageSelection) {
-            final String action;
-            if (android.os.Build.VERSION.SDK_INT
-                    >= /* android.os.Build.VERSION_CODES.HONEYCOMB */ 11) {
-                // Refer to android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS
-                // TODO: Can this be a constant instead of literal String constant?
-                action = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
-            } else {
-                action = "com.android.inputmethod.latin.INPUT_LANGUAGE_SELECTION";
-            }
-            startActivity(new Intent(action));
+            startActivity(CompatUtils.getInputLanguageSelectionIntent(
+                    Utils.getInputMethodId(InputMethodManagerCompatWrapper.getInstance(this),
+                            getApplicationInfo().packageName), 0));
             return true;
         }
         return false;
@@ -250,6 +501,11 @@
                 [mSettingsKeyPreference.findIndexOfValue(mSettingsKeyPreference.getValue())]);
     }
 
+    private void updateKeyPreviewPopupDelaySummary() {
+        final ListPreference lp = mKeyPreviewPopupDismissDelay;
+        lp.setSummary(lp.getEntries()[lp.findIndexOfValue(lp.getValue())]);
+    }
+
     private void showVoiceConfirmation() {
         mOkClicked = false;
         showDialog(VOICE_INPUT_CONFIRM_DIALOG);
@@ -277,10 +533,10 @@
                     public void onClick(DialogInterface dialog, int whichButton) {
                         if (whichButton == DialogInterface.BUTTON_NEGATIVE) {
                             mVoicePreference.setValue(mVoiceModeOff);
-                            mLogger.settingsWarningDialogCancel();
+                            mVoiceLogger.settingsWarningDialogCancel();
                         } else if (whichButton == DialogInterface.BUTTON_POSITIVE) {
                             mOkClicked = true;
-                            mLogger.settingsWarningDialogOk();
+                            mVoiceLogger.settingsWarningDialogOk();
                         }
                         updateVoicePreference();
                     }
@@ -311,7 +567,7 @@
                 AlertDialog dialog = builder.create();
                 mDialog = dialog;
                 dialog.setOnDismissListener(this);
-                mLogger.settingsWarningDialogShown();
+                mVoiceLogger.settingsWarningDialogShown();
                 return dialog;
             default:
                 Log.e(TAG, "unknown dialog " + id);
@@ -321,7 +577,7 @@
 
     @Override
     public void onDismiss(DialogInterface dialog) {
-        mLogger.settingsWarningDialogDismissed();
+        mVoiceLogger.settingsWarningDialogDismissed();
         if (!mOkClicked) {
             // This assumes that onPreferenceClick gets called first, and this if the user
             // agreed after the warning, we set the mOkClicked value to true.
@@ -331,10 +587,6 @@
 
     private void updateVoicePreference() {
         boolean isChecked = !mVoicePreference.getValue().equals(mVoiceModeOff);
-        if (isChecked) {
-            mLogger.voiceInputSettingEnabled();
-        } else {
-            mLogger.voiceInputSettingDisabled();
-        }
+        mVoiceLogger.voiceInputSettingEnabled(isChecked);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index dc14d77..6ca12c0 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -16,11 +16,12 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.compat.InputMethodInfoCompatWrapper;
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
+import com.android.inputmethod.deprecated.VoiceProxy;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.LatinKeyboard;
-import com.android.inputmethod.voice.SettingsUtil;
-import com.android.inputmethod.voice.VoiceIMEConnector;
-import com.android.inputmethod.voice.VoiceInput;
 
 import android.content.Context;
 import android.content.Intent;
@@ -31,12 +32,10 @@
 import android.graphics.drawable.Drawable;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.os.AsyncTask;
 import android.os.IBinder;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -53,32 +52,37 @@
     private static final String VOICE_MODE = "voice";
     private static final String SUBTYPE_EXTRAVALUE_REQUIRE_NETWORK_CONNECTIVITY =
             "requireNetworkConnectivity";
+    public static final String USE_SPACEBAR_LANGUAGE_SWITCH_KEY = "use_spacebar_language_switch";
+
     private final TextUtils.SimpleStringSplitter mLocaleSplitter =
             new TextUtils.SimpleStringSplitter(LOCALE_SEPARATER);
 
     private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
     private /* final */ LatinIME mService;
-    private /* final */ SharedPreferences mPrefs;
-    private /* final */ InputMethodManager mImm;
+    private /* final */ InputMethodManagerCompatWrapper mImm;
     private /* final */ Resources mResources;
     private /* final */ ConnectivityManager mConnectivityManager;
     private /* final */ boolean mConfigUseSpacebarLanguageSwitcher;
-    private final ArrayList<InputMethodSubtype> mEnabledKeyboardSubtypesOfCurrentInputMethod =
-            new ArrayList<InputMethodSubtype>();
+    private /* final */ SharedPreferences mPrefs;
+    private final ArrayList<InputMethodSubtypeCompatWrapper>
+            mEnabledKeyboardSubtypesOfCurrentInputMethod =
+                    new ArrayList<InputMethodSubtypeCompatWrapper>();
     private final ArrayList<String> mEnabledLanguagesOfCurrentInputMethod = new ArrayList<String>();
+    private final LanguageBarInfo mLanguageBarInfo = new LanguageBarInfo();
 
     /*-----------------------------------------------------------*/
     // Variants which should be changed only by reload functions.
     private boolean mNeedsToDisplayLanguage;
     private boolean mIsSystemLanguageSameAsInputLanguage;
-    private InputMethodInfo mShortcutInputMethodInfo;
-    private InputMethodSubtype mShortcutSubtype;
-    private List<InputMethodSubtype> mAllEnabledSubtypesOfCurrentInputMethod;
-    private InputMethodSubtype mCurrentSubtype;
+    private InputMethodInfoCompatWrapper mShortcutInputMethodInfo;
+    private InputMethodSubtypeCompatWrapper mShortcutSubtype;
+    private List<InputMethodSubtypeCompatWrapper> mAllEnabledSubtypesOfCurrentInputMethod;
+    private InputMethodSubtypeCompatWrapper mCurrentSubtype;
     private Locale mSystemLocale;
     private Locale mInputLocale;
     private String mInputLocaleStr;
-    private VoiceInput mVoiceInput;
+    private String mInputMethodId;
+    private VoiceProxy.VoiceInputWrapper mVoiceInputWrapper;
     /*-----------------------------------------------------------*/
 
     private boolean mIsNetworkConnected;
@@ -88,10 +92,9 @@
     }
 
     public static void init(LatinIME service, SharedPreferences prefs) {
+        SubtypeLocale.init(service);
         sInstance.initialize(service, prefs);
         sInstance.updateAllParameters();
-
-        SubtypeLocale.init(service);
     }
 
     private SubtypeSwitcher() {
@@ -100,9 +103,8 @@
 
     private void initialize(LatinIME service, SharedPreferences prefs) {
         mService = service;
-        mPrefs = prefs;
         mResources = service.getResources();
-        mImm = (InputMethodManager) service.getSystemService(Context.INPUT_METHOD_SERVICE);
+        mImm = InputMethodManagerCompatWrapper.getInstance(service);
         mConnectivityManager = (ConnectivityManager) service.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
         mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
@@ -112,15 +114,12 @@
         mInputLocaleStr = null;
         mCurrentSubtype = null;
         mAllEnabledSubtypesOfCurrentInputMethod = null;
-        // TODO: Voice input should be created here
-        mVoiceInput = null;
-        mConfigUseSpacebarLanguageSwitcher = mResources.getBoolean(
-                R.bool.config_use_spacebar_language_switcher);
-        if (mConfigUseSpacebarLanguageSwitcher)
-            initLanguageSwitcher(service);
+        mVoiceInputWrapper = null;
+        mPrefs = prefs;
 
         final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
         mIsNetworkConnected = (info != null && info.isConnected());
+        mInputMethodId = Utils.getInputMethodId(mImm, service.getPackageName());
     }
 
     // Update all parameters stored in SubtypeSwitcher.
@@ -134,11 +133,10 @@
     // Update parameters which are changed outside LatinIME. This parameters affect UI so they
     // should be updated every time onStartInputview.
     public void updateParametersOnStartInputView() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            updateForSpacebarLanguageSwitch();
-        } else {
-            updateEnabledSubtypes();
-        }
+        mConfigUseSpacebarLanguageSwitcher = mPrefs.getBoolean(USE_SPACEBAR_LANGUAGE_SWITCH_KEY,
+                mService.getResources().getBoolean(
+                        R.bool.config_use_spacebar_language_switcher));
+        updateEnabledSubtypes();
         updateShortcutIME();
     }
 
@@ -150,7 +148,7 @@
                 null, true);
         mEnabledLanguagesOfCurrentInputMethod.clear();
         mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
-        for (InputMethodSubtype ims: mAllEnabledSubtypesOfCurrentInputMethod) {
+        for (InputMethodSubtypeCompatWrapper ims : mAllEnabledSubtypesOfCurrentInputMethod) {
             final String locale = ims.getLocale();
             final String mode = ims.getMode();
             mLocaleSplitter.setString(locale);
@@ -172,6 +170,10 @@
                 Log.w(TAG, "Last subtype was disabled. Update to the current one.");
             }
             updateSubtype(mImm.getCurrentInputMethodSubtype());
+        } else {
+            // mLanguageBarInfo.update() will be called in updateSubtype so there is no need
+            // to call this in the if-clause above.
+            mLanguageBarInfo.update();
         }
     }
 
@@ -184,10 +186,10 @@
                             + ", " + mShortcutSubtype.getMode())));
         }
         // TODO: Update an icon for shortcut IME
-        Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
+        final Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>> shortcuts =
                 mImm.getShortcutInputMethodsAndSubtypes();
-        for (InputMethodInfo imi: shortcuts.keySet()) {
-            List<InputMethodSubtype> subtypes = shortcuts.get(imi);
+        for (InputMethodInfoCompatWrapper imi : shortcuts.keySet()) {
+            List<InputMethodSubtypeCompatWrapper> subtypes = shortcuts.get(imi);
             // TODO: Returns the first found IMI for now. Should handle all shortcuts as
             // appropriate.
             mShortcutInputMethodInfo = imi;
@@ -206,7 +208,7 @@
     }
 
     // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
-    public void updateSubtype(InputMethodSubtype newSubtype) {
+    public void updateSubtype(InputMethodSubtypeCompatWrapper newSubtype) {
         final String newLocale;
         final String newMode;
         final String oldMode = getCurrentSubtypeMode();
@@ -243,32 +245,33 @@
         // We cancel its status when we change mode, while we reset otherwise.
         if (isKeyboardMode()) {
             if (modeChanged) {
-                if (VOICE_MODE.equals(oldMode) && mVoiceInput != null) {
-                    mVoiceInput.cancel();
+                if (VOICE_MODE.equals(oldMode) && mVoiceInputWrapper != null) {
+                    mVoiceInputWrapper.cancel();
                 }
             }
             if (modeChanged || languageChanged) {
                 updateShortcutIME();
                 mService.onRefreshKeyboard();
             }
-        } else if (isVoiceMode() && mVoiceInput != null) {
+        } else if (isVoiceMode() && mVoiceInputWrapper != null) {
             if (VOICE_MODE.equals(oldMode)) {
-                mVoiceInput.reset();
+                mVoiceInputWrapper.reset();
             }
             // If needsToShowWarningDialog is true, voice input need to show warning before
             // show recognition view.
             if (languageChanged || modeChanged
-                    || VoiceIMEConnector.getInstance().needsToShowWarningDialog()) {
+                    || VoiceProxy.getInstance().needsToShowWarningDialog()) {
                 triggerVoiceIME();
             }
         } else {
             Log.w(TAG, "Unknown subtype mode: " + newMode);
-            if (VOICE_MODE.equals(oldMode) && mVoiceInput != null) {
+            if (VOICE_MODE.equals(oldMode) && mVoiceInputWrapper != null) {
                 // We need to reset the voice input to release the resources and to reset its status
                 // as it is not the current input mode.
-                mVoiceInput.reset();
+                mVoiceInputWrapper.reset();
             }
         }
+        mLanguageBarInfo.update();
     }
 
     // Update the current input locale from Locale string.
@@ -277,14 +280,8 @@
         // "en_US" --> language: en  & country: US
         // "en" --> language: en
         // "" --> the system locale
-        mLocaleSplitter.setString(inputLocaleStr);
-        if (mLocaleSplitter.hasNext()) {
-            String language = mLocaleSplitter.next();
-            if (mLocaleSplitter.hasNext()) {
-                mInputLocale = new Locale(language, mLocaleSplitter.next());
-            } else {
-                mInputLocale = new Locale(language);
-            }
+        if (!TextUtils.isEmpty(inputLocaleStr)) {
+            mInputLocale = Utils.constructLocaleFromString(inputLocaleStr);
             mInputLocaleStr = inputLocaleStr;
         } else {
             mInputLocale = mSystemLocale;
@@ -303,25 +300,47 @@
     ////////////////////////////
 
     public void switchToShortcutIME() {
-        final IBinder token = mService.getWindow().getWindow().getAttributes().token;
-        if (token == null || mShortcutInputMethodInfo == null) {
+        if (mShortcutInputMethodInfo == null) {
             return;
         }
+
         final String imiId = mShortcutInputMethodInfo.getId();
-        final InputMethodSubtype subtype = mShortcutSubtype;
-        new Thread("SwitchToShortcutIME") {
+        final InputMethodSubtypeCompatWrapper subtype = mShortcutSubtype;
+        switchToTargetIME(imiId, subtype);
+    }
+
+    private void switchToTargetIME(
+            final String imiId, final InputMethodSubtypeCompatWrapper subtype) {
+        final IBinder token = mService.getWindow().getWindow().getAttributes().token;
+        if (token == null) {
+            return;
+        }
+        new AsyncTask<Void, Void, Void>() {
             @Override
-            public void run() {
+            protected Void doInBackground(Void... params) {
                 mImm.setInputMethodAndSubtype(token, imiId, subtype);
+                return null;
             }
-        }.start();
+
+            @Override
+            protected void onPostExecute(Void result) {
+                // Calls in this method need to be done in the same thread as the thread which
+                // called switchToShortcutIME().
+
+                // Notify an event that the current subtype was changed. This event will be
+                // handled if "onCurrentInputMethodSubtypeChanged" can't be implemented
+                // when the API level is 10 or previous.
+                mService.notifyOnCurrentInputMethodSubtypeChanged(subtype);
+            }
+        }.execute();
     }
 
     public Drawable getShortcutIcon() {
         return getSubtypeIcon(mShortcutInputMethodInfo, mShortcutSubtype);
     }
 
-    private Drawable getSubtypeIcon(InputMethodInfo imi, InputMethodSubtype subtype) {
+    private Drawable getSubtypeIcon(
+            InputMethodInfoCompatWrapper imi, InputMethodSubtypeCompatWrapper subtype) {
         final PackageManager pm = mService.getPackageManager();
         if (imi != null) {
             final String imiPackageName = imi.getPackageName();
@@ -360,11 +379,16 @@
             return false;
         if (mShortcutSubtype == null)
             return true;
+        // For compatibility, if the shortcut subtype is dummy, we assume the shortcut IME
+        // (built-in voice dummy subtype) is available.
+        if (!mShortcutSubtype.hasOriginalObject()) return true;
         final boolean allowsImplicitlySelectedSubtypes = true;
-        for (final InputMethodSubtype enabledSubtype : mImm.getEnabledInputMethodSubtypeList(
-                mShortcutInputMethodInfo, allowsImplicitlySelectedSubtypes)) {
-            if (enabledSubtype.equals(mShortcutSubtype))
+        for (final InputMethodSubtypeCompatWrapper enabledSubtype :
+                mImm.getEnabledInputMethodSubtypeList(
+                        mShortcutInputMethodInfo, allowsImplicitlySelectedSubtypes)) {
+            if (enabledSubtype.equals(mShortcutSubtype)) {
                 return true;
+            }
         }
         return false;
     }
@@ -389,7 +413,7 @@
         final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
         final LatinKeyboard keyboard = switcher.getLatinKeyboard();
         if (keyboard != null) {
-            keyboard.updateShortcutKey(isShortcutImeReady(), switcher.getInputView());
+            keyboard.updateShortcutKey(isShortcutImeReady(), switcher.getKeyboardView());
         }
     }
 
@@ -398,11 +422,7 @@
     //////////////////////////////////
 
     public int getEnabledKeyboardLocaleCount() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return mLanguageSwitcher.getLocaleCount();
-        } else {
-            return mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
-        }
+        return mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
     }
 
     public boolean useSpacebarLanguageSwitcher() {
@@ -414,90 +434,40 @@
     }
 
     public Locale getInputLocale() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return mLanguageSwitcher.getInputLocale();
-        } else {
-            return mInputLocale;
-        }
+        return mInputLocale;
     }
 
     public String getInputLocaleStr() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            String inputLanguage = null;
-            inputLanguage = mLanguageSwitcher.getInputLanguage();
-            // Should return system locale if there is no Language available.
-            if (inputLanguage == null) {
-                inputLanguage = getSystemLocale().getLanguage();
-            }
-            return inputLanguage;
-        } else {
-            return mInputLocaleStr;
-        }
+        return mInputLocaleStr;
     }
 
     public String[] getEnabledLanguages() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return mLanguageSwitcher.getEnabledLanguages();
-        } else {
-            int enabledLanguageCount = mEnabledLanguagesOfCurrentInputMethod.size();
-            // Workaround for explicitly specifying the voice language
-            if (enabledLanguageCount == 1) {
-                mEnabledLanguagesOfCurrentInputMethod.add(
-                        mEnabledLanguagesOfCurrentInputMethod.get(0));
-                ++enabledLanguageCount;
-            }
-            return mEnabledLanguagesOfCurrentInputMethod.toArray(
-                    new String[enabledLanguageCount]);
+        int enabledLanguageCount = mEnabledLanguagesOfCurrentInputMethod.size();
+        // Workaround for explicitly specifying the voice language
+        if (enabledLanguageCount == 1) {
+            mEnabledLanguagesOfCurrentInputMethod.add(mEnabledLanguagesOfCurrentInputMethod
+                    .get(0));
+            ++enabledLanguageCount;
         }
+        return mEnabledLanguagesOfCurrentInputMethod.toArray(new String[enabledLanguageCount]);
     }
 
     public Locale getSystemLocale() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return mLanguageSwitcher.getSystemLocale();
-        } else {
-            return mSystemLocale;
-        }
+        return mSystemLocale;
     }
 
     public boolean isSystemLanguageSameAsInputLanguage() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return getSystemLocale().getLanguage().equalsIgnoreCase(
-                    getInputLocaleStr().substring(0, 2));
-        } else {
-            return mIsSystemLanguageSameAsInputLanguage;
-        }
+        return mIsSystemLanguageSameAsInputLanguage;
     }
 
     public void onConfigurationChanged(Configuration conf) {
         final Locale systemLocale = conf.locale;
         // If system configuration was changed, update all parameters.
         if (!TextUtils.equals(systemLocale.toString(), mSystemLocale.toString())) {
-            if (mConfigUseSpacebarLanguageSwitcher) {
-                // If the system locale changes and is different from the saved
-                // locale (mSystemLocale), then reload the input locale list from the
-                // latin ime settings (shared prefs) and reset the input locale
-                // to the first one.
-                mLanguageSwitcher.loadLocales(mPrefs);
-                mLanguageSwitcher.setSystemLocale(systemLocale);
-            } else {
-                updateAllParameters();
-            }
+            updateAllParameters();
         }
     }
 
-    /**
-     * Change system locale for this application
-     * @param newLocale
-     * @return oldLocale
-     */
-    public Locale changeSystemLocale(Locale newLocale) {
-        Configuration conf = mResources.getConfiguration();
-        Locale oldLocale = conf.locale;
-        conf.locale = newLocale;
-        mResources.updateConfiguration(conf, mResources.getDisplayMetrics());
-        return oldLocale;
-    }
-
     public boolean isKeyboardMode() {
         return KEYBOARD_MODE.equals(getCurrentSubtypeMode());
     }
@@ -507,9 +477,9 @@
     // Voice Input functions //
     ///////////////////////////
 
-    public boolean setVoiceInput(VoiceInput vi) {
-        if (mVoiceInput == null && vi != null) {
-            mVoiceInput = vi;
+    public boolean setVoiceInputWrapper(VoiceProxy.VoiceInputWrapper vi) {
+        if (mVoiceInputWrapper == null && vi != null) {
+            mVoiceInputWrapper = vi;
             if (isVoiceMode()) {
                 if (DBG) {
                     Log.d(TAG, "Set and call voice input.: " + getInputLocaleStr());
@@ -525,47 +495,112 @@
         return null == mCurrentSubtype ? false : VOICE_MODE.equals(getCurrentSubtypeMode());
     }
 
+    public boolean isDummyVoiceMode() {
+        return mCurrentSubtype != null && mCurrentSubtype.getOriginalObject() == null
+                && VOICE_MODE.equals(getCurrentSubtypeMode());
+    }
+
     private void triggerVoiceIME() {
         if (!mService.isInputViewShown()) return;
-        VoiceIMEConnector.getInstance().startListening(false,
-                KeyboardSwitcher.getInstance().getInputView().getWindowToken());
+        VoiceProxy.getInstance().startListening(false,
+                KeyboardSwitcher.getInstance().getKeyboardView().getWindowToken());
     }
 
     //////////////////////////////////////
     // Spacebar Language Switch support //
     //////////////////////////////////////
 
-    private LanguageSwitcher mLanguageSwitcher;
+    private class LanguageBarInfo {
+        private int mCurrentKeyboardSubtypeIndex;
+        private InputMethodSubtypeCompatWrapper mNextKeyboardSubtype;
+        private InputMethodSubtypeCompatWrapper mPreviousKeyboardSubtype;
+        private String mNextLanguage;
+        private String mPreviousLanguage;
+        public LanguageBarInfo() {
+            update();
+        }
+
+        private String getNextLanguage() {
+            return mNextLanguage;
+        }
+
+        private String getPreviousLanguage() {
+            return mPreviousLanguage;
+        }
+
+        public InputMethodSubtypeCompatWrapper getNextKeyboardSubtype() {
+            return mNextKeyboardSubtype;
+        }
+
+        public InputMethodSubtypeCompatWrapper getPreviousKeyboardSubtype() {
+            return mPreviousKeyboardSubtype;
+        }
+
+        public void update() {
+            if (!mConfigUseSpacebarLanguageSwitcher
+                    || mEnabledKeyboardSubtypesOfCurrentInputMethod == null
+                    || mEnabledKeyboardSubtypesOfCurrentInputMethod.size() == 0) return;
+            mCurrentKeyboardSubtypeIndex = getCurrentIndex();
+            mNextKeyboardSubtype = getNextKeyboardSubtypeInternal(mCurrentKeyboardSubtypeIndex);
+            Locale locale = Utils.constructLocaleFromString(mNextKeyboardSubtype.getLocale());
+            mNextLanguage = getFullDisplayName(locale, true);
+            mPreviousKeyboardSubtype = getPreviousKeyboardSubtypeInternal(
+                    mCurrentKeyboardSubtypeIndex);
+            locale = Utils.constructLocaleFromString(mPreviousKeyboardSubtype.getLocale());
+            mPreviousLanguage = getFullDisplayName(locale, true);
+        }
+
+        private int normalize(int index) {
+            final int N = mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
+            final int ret = index % N;
+            return ret < 0 ? ret + N : ret;
+        }
+
+        private int getCurrentIndex() {
+            final int N = mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
+            for (int i = 0; i < N; ++i) {
+                if (mEnabledKeyboardSubtypesOfCurrentInputMethod.get(i).equals(mCurrentSubtype)) {
+                    return i;
+                }
+            }
+            return 0;
+        }
+
+        private InputMethodSubtypeCompatWrapper getNextKeyboardSubtypeInternal(int index) {
+            return mEnabledKeyboardSubtypesOfCurrentInputMethod.get(normalize(index + 1));
+        }
+
+        private InputMethodSubtypeCompatWrapper getPreviousKeyboardSubtypeInternal(int index) {
+            return mEnabledKeyboardSubtypesOfCurrentInputMethod.get(normalize(index - 1));
+        }
+    }
 
     public static String getFullDisplayName(Locale locale, boolean returnsNameInThisLocale) {
         if (returnsNameInThisLocale) {
-            return toTitleCase(SubtypeLocale.getFullDisplayName(locale));
+            return toTitleCase(SubtypeLocale.getFullDisplayName(locale), locale);
         } else {
-            return toTitleCase(locale.getDisplayName());
+            return toTitleCase(locale.getDisplayName(), locale);
         }
     }
 
     public static String getDisplayLanguage(Locale locale) {
-        return toTitleCase(locale.getDisplayLanguage(locale));
+        return toTitleCase(SubtypeLocale.getFullDisplayName(locale), locale);
+    }
+
+    public static String getMiddleDisplayLanguage(Locale locale) {
+        return toTitleCase((Utils.constructLocaleFromString(
+                locale.getLanguage()).getDisplayLanguage(locale)), locale);
     }
 
     public static String getShortDisplayLanguage(Locale locale) {
-        return toTitleCase(locale.getLanguage());
+        return toTitleCase(locale.getLanguage(), locale);
     }
 
-    private static String toTitleCase(String s) {
+    private static String toTitleCase(String s, Locale locale) {
         if (s.length() == 0) {
             return s;
         }
-        return Character.toUpperCase(s.charAt(0)) + s.substring(1);
-    }
-
-    private void updateForSpacebarLanguageSwitch() {
-        // We need to update mNeedsToDisplayLanguage in onStartInputView because
-        // getEnabledKeyboardLocaleCount could have been changed.
-        mNeedsToDisplayLanguage = !(getEnabledKeyboardLocaleCount() <= 1
-                && getSystemLocale().getLanguage().equalsIgnoreCase(
-                        getInputLocale().getLanguage()));
+        return s.toUpperCase(locale).charAt(0) + s.substring(1);
     }
 
     public String getInputLanguageName() {
@@ -573,19 +608,11 @@
     }
 
     public String getNextInputLanguageName() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return getDisplayLanguage(mLanguageSwitcher.getNextInputLocale());
-        } else {
-            return "";
-        }
+        return mLanguageBarInfo.getNextLanguage();
     }
 
     public String getPreviousInputLanguageName() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            return getDisplayLanguage(mLanguageSwitcher.getPrevInputLocale());
-        } else {
-            return "";
-        }
+        return mLanguageBarInfo.getPreviousLanguage();
     }
 
     /////////////////////////////
@@ -612,60 +639,36 @@
     }
 
 
-    // A list of locales which are supported by default for voice input, unless we get a
-    // different list from Gservices.
-    private static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES =
-            "en " +
-            "en_US " +
-            "en_GB " +
-            "en_AU " +
-            "en_CA " +
-            "en_IE " +
-            "en_IN " +
-            "en_NZ " +
-            "en_SG " +
-            "en_ZA ";
-
     public boolean isVoiceSupported(String locale) {
         // Get the current list of supported locales and check the current locale against that
         // list. We cache this value so as not to check it every time the user starts a voice
         // input. Because this method is called by onStartInputView, this should mean that as
         // long as the locale doesn't change while the user is keeping the IME open, the
         // value should never be stale.
-        String supportedLocalesString = SettingsUtil.getSettingsString(
-                mService.getContentResolver(),
-                SettingsUtil.LATIN_IME_VOICE_INPUT_SUPPORTED_LOCALES,
-                DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES);
+        String supportedLocalesString = VoiceProxy.getSupportedLocalesString(
+                mService.getContentResolver());
         List<String> voiceInputSupportedLocales = Arrays.asList(
                 supportedLocalesString.split("\\s+"));
         return voiceInputSupportedLocales.contains(locale);
     }
 
-    public void loadSettings() {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            mLanguageSwitcher.loadLocales(mPrefs);
-        }
+    private void changeToNextSubtype() {
+        final InputMethodSubtypeCompatWrapper subtype =
+                mLanguageBarInfo.getNextKeyboardSubtype();
+        switchToTargetIME(mInputMethodId, subtype);
     }
 
-    public void toggleLanguage(boolean reset, boolean next) {
-        if (mConfigUseSpacebarLanguageSwitcher) {
-            if (reset) {
-                mLanguageSwitcher.reset();
-            } else {
-                if (next) {
-                    mLanguageSwitcher.next();
-                } else {
-                    mLanguageSwitcher.prev();
-                }
-            }
-            mLanguageSwitcher.persist(mPrefs);
-        }
+    private void changeToPreviousSubtype() {
+        final InputMethodSubtypeCompatWrapper subtype =
+                mLanguageBarInfo.getPreviousKeyboardSubtype();
+        switchToTargetIME(mInputMethodId, subtype);
     }
 
-    private void initLanguageSwitcher(LatinIME service) {
-        final Configuration conf = service.getResources().getConfiguration();
-        mLanguageSwitcher = new LanguageSwitcher(service);
-        mLanguageSwitcher.loadLocales(mPrefs);
-        mLanguageSwitcher.setSystemLocale(conf.locale);
+    public void toggleLanguage(boolean next) {
+        if (next) {
+            changeToNextSubtype();
+        } else {
+            changeToPreviousSubtype();
+        }
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 0de474e..eb5ed5a 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -27,6 +27,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
@@ -47,7 +48,7 @@
 
     /**
      * Words that appear in both bigram and unigram data gets multiplier ranging from
-     * BIGRAM_MULTIPLIER_MIN to BIGRAM_MULTIPLIER_MAX depending on the frequency score from
+     * BIGRAM_MULTIPLIER_MIN to BIGRAM_MULTIPLIER_MAX depending on the score from
      * bigram data.
      */
     public static final double BIGRAM_MULTIPLIER_MIN = 1.2;
@@ -55,7 +56,7 @@
 
     /**
      * Maximum possible bigram frequency. Will depend on how many bits are being used in data
-     * structure. Maximum bigram freqeuncy will get the BIGRAM_MULTIPLIER_MAX as the multiplier.
+     * structure. Maximum bigram frequency will get the BIGRAM_MULTIPLIER_MAX as the multiplier.
      */
     public static final int MAXIMUM_BIGRAM_FREQUENCY = 127;
 
@@ -74,31 +75,29 @@
     public static final String DICT_KEY_USER_BIGRAM = "user_bigram";
     public static final String DICT_KEY_WHITELIST ="whitelist";
 
-    static final int LARGE_DICTIONARY_THRESHOLD = 200 * 1000;
-
     private static final boolean DBG = LatinImeLogger.sDBG;
 
     private AutoCorrection mAutoCorrection;
 
-    private BinaryDictionary mMainDict;
+    private Dictionary mMainDict;
     private WhitelistDictionary mWhiteListDictionary;
     private final Map<String, Dictionary> mUnigramDictionaries = new HashMap<String, Dictionary>();
     private final Map<String, Dictionary> mBigramDictionaries = new HashMap<String, Dictionary>();
 
-    private int mPrefMaxSuggestions = 12;
+    private int mPrefMaxSuggestions = 18;
 
     private static final int PREF_MAX_BIGRAMS = 60;
 
     private boolean mQuickFixesEnabled;
 
     private double mAutoCorrectionThreshold;
-    private int[] mPriorities = new int[mPrefMaxSuggestions];
-    private int[] mBigramPriorities = new int[PREF_MAX_BIGRAMS];
+    private int[] mScores = new int[mPrefMaxSuggestions];
+    private int[] mBigramScores = new int[PREF_MAX_BIGRAMS];
 
     private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
     ArrayList<CharSequence> mBigramSuggestions  = new ArrayList<CharSequence>();
     private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
-    private String mLowerOriginalWord;
+    private CharSequence mTypedWord;
 
     // TODO: Remove these member variables by passing more context to addWord() callback method
     private boolean mIsFirstCharCapitalized;
@@ -106,28 +105,45 @@
 
     private int mCorrectionMode = CORRECTION_BASIC;
 
-    public Suggest(Context context, int dictionaryResId) {
-        init(context, BinaryDictionary.initDictionary(context, dictionaryResId, DIC_MAIN));
+    public Suggest(Context context, int dictionaryResId, Locale locale) {
+        init(context, DictionaryFactory.createDictionaryFromManager(context, locale,
+                dictionaryResId));
     }
 
-    /* package for test */ Suggest(File dictionary, long startOffset, long length) {
-        init(null, BinaryDictionary.initDictionary(dictionary, startOffset, length, DIC_MAIN));
+    /* package for test */ Suggest(Context context, File dictionary, long startOffset, long length,
+            Flag[] flagArray) {
+        init(null, DictionaryFactory.createDictionaryForTest(context, dictionary, startOffset,
+                length, flagArray));
     }
 
-    private void init(Context context, BinaryDictionary mainDict) {
-        if (mainDict != null) {
-            mMainDict = mainDict;
-            mUnigramDictionaries.put(DICT_KEY_MAIN, mainDict);
-            mBigramDictionaries.put(DICT_KEY_MAIN, mainDict);
-        }
+    private void init(Context context, Dictionary mainDict) {
+        mMainDict = mainDict;
+        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict);
+        addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict);
         mWhiteListDictionary = WhitelistDictionary.init(context);
-        if (mWhiteListDictionary != null) {
-            mUnigramDictionaries.put(DICT_KEY_WHITELIST, mWhiteListDictionary);
-        }
+        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
         mAutoCorrection = new AutoCorrection();
         initPool();
     }
 
+    private void addOrReplaceDictionary(Map<String, Dictionary> dictionaries, String key,
+            Dictionary dict) {
+        final Dictionary oldDict = (dict == null)
+                ? dictionaries.remove(key)
+                : dictionaries.put(key, dict);
+        if (oldDict != null && dict != oldDict) {
+            oldDict.close();
+        }
+    }
+
+    public void resetMainDict(Context context, int dictionaryResId, Locale locale) {
+        final Dictionary newMainDict = DictionaryFactory.createDictionaryFromManager(
+                context, locale, dictionaryResId);
+        mMainDict = newMainDict;
+        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict);
+        addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict);
+    }
+
     private void initPool() {
         for (int i = 0; i < mPrefMaxSuggestions; i++) {
             StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
@@ -148,7 +164,7 @@
     }
 
     public boolean hasMainDictionary() {
-        return mMainDict != null && mMainDict.getSize() > LARGE_DICTIONARY_THRESHOLD;
+        return mMainDict != null;
     }
 
     public Map<String, Dictionary> getUnigramDictionaries() {
@@ -164,28 +180,25 @@
      * before the main dictionary, if set.
      */
     public void setUserDictionary(Dictionary userDictionary) {
-        if (userDictionary != null)
-            mUnigramDictionaries.put(DICT_KEY_USER, userDictionary);
+        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER, userDictionary);
     }
 
     /**
-     * Sets an optional contacts dictionary resource to be loaded.
+     * Sets an optional contacts dictionary resource to be loaded. It is also possible to remove
+     * the contacts dictionary by passing null to this method. In this case no contacts dictionary
+     * won't be used.
      */
     public void setContactsDictionary(Dictionary contactsDictionary) {
-        if (contactsDictionary != null) {
-            mUnigramDictionaries.put(DICT_KEY_CONTACTS, contactsDictionary);
-            mBigramDictionaries.put(DICT_KEY_CONTACTS, contactsDictionary);
-        }
+        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
+        addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
     }
 
     public void setAutoDictionary(Dictionary autoDictionary) {
-        if (autoDictionary != null)
-            mUnigramDictionaries.put(DICT_KEY_AUTO, autoDictionary);
+        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_AUTO, autoDictionary);
     }
 
     public void setUserBigramDictionary(Dictionary userBigramDictionary) {
-        if (userBigramDictionary != null)
-            mBigramDictionaries.put(DICT_KEY_USER_BIGRAM, userBigramDictionary);
+        addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_USER_BIGRAM, userBigramDictionary);
     }
 
     public void setAutoCorrectionThreshold(double threshold) {
@@ -207,8 +220,8 @@
             throw new IllegalArgumentException("maxSuggestions must be between 1 and 100");
         }
         mPrefMaxSuggestions = maxSuggestions;
-        mPriorities = new int[mPrefMaxSuggestions];
-        mBigramPriorities = new int[PREF_MAX_BIGRAMS];
+        mScores = new int[mPrefMaxSuggestions];
+        mBigramScores = new int[PREF_MAX_BIGRAMS];
         collectGarbage(mSuggestions, mPrefMaxSuggestions);
         while (mStringPool.size() < mPrefMaxSuggestions) {
             StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
@@ -237,6 +250,7 @@
                 poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
                         : new StringBuilder(getApproxMaxWordLength());
         sb.setLength(0);
+        // TODO: Must pay attention to locale when changing case.
         if (all) {
             sb.append(word.toString().toUpperCase());
         } else if (first) {
@@ -248,6 +262,16 @@
         return sb;
     }
 
+    protected void addBigramToSuggestions(CharSequence bigram) {
+        final int poolSize = mStringPool.size();
+        final StringBuilder sb = poolSize > 0 ?
+                (StringBuilder) mStringPool.remove(poolSize - 1)
+                        : new StringBuilder(getApproxMaxWordLength());
+        sb.setLength(0);
+        sb.append(bigram);
+        mSuggestions.add(sb);
+    }
+
     // TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder
     public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer,
             CharSequence prevWordForBigram) {
@@ -256,25 +280,23 @@
         mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
         mIsAllUpperCase = wordComposer.isAllUpperCase();
         collectGarbage(mSuggestions, mPrefMaxSuggestions);
-        Arrays.fill(mPriorities, 0);
+        Arrays.fill(mScores, 0);
 
         // Save a lowercase version of the original word
         CharSequence typedWord = wordComposer.getTypedWord();
         if (typedWord != null) {
             final String typedWordString = typedWord.toString();
             typedWord = typedWordString;
-            mLowerOriginalWord = typedWordString.toLowerCase();
             // Treating USER_TYPED as UNIGRAM suggestion for logging now.
             LatinImeLogger.onAddSuggestedWord(typedWordString, Suggest.DIC_USER_TYPED,
                     Dictionary.DataType.UNIGRAM);
-        } else {
-            mLowerOriginalWord = "";
         }
+        mTypedWord = typedWord;
 
-        if (wordComposer.size() == 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM
+        if (wordComposer.size() <= 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM
                 || mCorrectionMode == CORRECTION_BASIC)) {
             // At first character typed, search only the bigrams
-            Arrays.fill(mBigramPriorities, 0);
+            Arrays.fill(mBigramScores, 0);
             collectGarbage(mBigramSuggestions, PREF_MAX_BIGRAMS);
 
             if (!TextUtils.isEmpty(prevWordForBigram)) {
@@ -285,21 +307,27 @@
                 for (final Dictionary dictionary : mBigramDictionaries.values()) {
                     dictionary.getBigrams(wordComposer, prevWordForBigram, this);
                 }
-                char currentChar = wordComposer.getTypedWord().charAt(0);
-                char currentCharUpper = Character.toUpperCase(currentChar);
-                int count = 0;
-                int bigramSuggestionSize = mBigramSuggestions.size();
-                for (int i = 0; i < bigramSuggestionSize; i++) {
-                    if (mBigramSuggestions.get(i).charAt(0) == currentChar
-                            || mBigramSuggestions.get(i).charAt(0) == currentCharUpper) {
-                        int poolSize = mStringPool.size();
-                        StringBuilder sb = poolSize > 0 ?
-                                (StringBuilder) mStringPool.remove(poolSize - 1)
-                                : new StringBuilder(getApproxMaxWordLength());
-                        sb.setLength(0);
-                        sb.append(mBigramSuggestions.get(i));
-                        mSuggestions.add(count++, sb);
-                        if (count > mPrefMaxSuggestions) break;
+                if (TextUtils.isEmpty(typedWord)) {
+                    // Nothing entered: return all bigrams for the previous word
+                    int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions);
+                    for (int i = 0; i < insertCount; ++i) {
+                        addBigramToSuggestions(mBigramSuggestions.get(i));
+                    }
+                } else {
+                    // Word entered: return only bigrams that match the first char of the typed word
+                    final char currentChar = typedWord.charAt(0);
+                    // TODO: Must pay attention to locale when changing case.
+                    final char currentCharUpper = Character.toUpperCase(currentChar);
+                    int count = 0;
+                    final int bigramSuggestionSize = mBigramSuggestions.size();
+                    for (int i = 0; i < bigramSuggestionSize; i++) {
+                        final CharSequence bigramSuggestion = mBigramSuggestions.get(i);
+                        final char bigramSuggestionFirstChar = bigramSuggestion.charAt(0);
+                        if (bigramSuggestionFirstChar == currentChar
+                                || bigramSuggestionFirstChar == currentCharUpper) {
+                            addBigramToSuggestions(bigramSuggestion);
+                            if (++count > mPrefMaxSuggestions) break;
+                        }
                     }
                 }
             }
@@ -346,7 +374,7 @@
                 mWhiteListDictionary.getWhiteListedWord(typedWordString));
 
         mAutoCorrection.updateAutoCorrectionStatus(mUnigramDictionaries, wordComposer,
-                mSuggestions, mPriorities, typedWord, mAutoCorrectionThreshold, mCorrectionMode,
+                mSuggestions, mScores, typedWord, mAutoCorrectionThreshold, mCorrectionMode,
                 autoText, whitelistedWord);
 
         if (autoText != null) {
@@ -364,26 +392,25 @@
 
         if (DBG) {
             double normalizedScore = mAutoCorrection.getNormalizedScore();
-            ArrayList<SuggestedWords.SuggestedWordInfo> frequencyInfoList =
+            ArrayList<SuggestedWords.SuggestedWordInfo> scoreInfoList =
                     new ArrayList<SuggestedWords.SuggestedWordInfo>();
-            frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false));
-            final int priorityLength = mPriorities.length;
-            for (int i = 0; i < priorityLength; ++i) {
+            scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false));
+            for (int i = 0; i < mScores.length; ++i) {
                 if (normalizedScore > 0) {
-                    final String priorityThreshold = Integer.toString(mPriorities[i]) + " (" +
-                            normalizedScore + ")";
-                    frequencyInfoList.add(
-                            new SuggestedWords.SuggestedWordInfo(priorityThreshold, false));
+                    final String scoreThreshold = String.format("%d (%4.2f)", mScores[i],
+                            normalizedScore);
+                    scoreInfoList.add(
+                            new SuggestedWords.SuggestedWordInfo(scoreThreshold, false));
                     normalizedScore = 0.0;
                 } else {
-                    final String priority = Integer.toString(mPriorities[i]);
-                    frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo(priority, false));
+                    final String score = Integer.toString(mScores[i]);
+                    scoreInfoList.add(new SuggestedWords.SuggestedWordInfo(score, false));
                 }
             }
-            for (int i = priorityLength; i < mSuggestions.size(); ++i) {
-                frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false));
+            for (int i = mScores.length; i < mSuggestions.size(); ++i) {
+                scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false));
             }
-            return new SuggestedWords.Builder().addWords(mSuggestions, frequencyInfoList);
+            return new SuggestedWords.Builder().addWords(mSuggestions, scoreInfoList);
         }
         return new SuggestedWords.Builder().addWords(mSuggestions, null);
     }
@@ -419,52 +446,37 @@
         return mAutoCorrection.hasAutoCorrection();
     }
 
-    private static boolean compareCaseInsensitive(final String lowerOriginalWord,
-            final char[] word, final int offset, final int length) {
-        final int originalLength = lowerOriginalWord.length();
-        if (originalLength == length && Character.isUpperCase(word[offset])) {
-            for (int i = 0; i < originalLength; i++) {
-                if (lowerOriginalWord.charAt(i) != Character.toLowerCase(word[offset+i])) {
-                    return false;
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-
     @Override
-    public boolean addWord(final char[] word, final int offset, final int length, int freq,
+    public boolean addWord(final char[] word, final int offset, final int length, int score,
             final int dicTypeId, final Dictionary.DataType dataType) {
         Dictionary.DataType dataTypeForLog = dataType;
-        ArrayList<CharSequence> suggestions;
-        int[] priorities;
-        int prefMaxSuggestions;
+        final ArrayList<CharSequence> suggestions;
+        final int[] sortedScores;
+        final int prefMaxSuggestions;
         if(dataType == Dictionary.DataType.BIGRAM) {
             suggestions = mBigramSuggestions;
-            priorities = mBigramPriorities;
+            sortedScores = mBigramScores;
             prefMaxSuggestions = PREF_MAX_BIGRAMS;
         } else {
             suggestions = mSuggestions;
-            priorities = mPriorities;
+            sortedScores = mScores;
             prefMaxSuggestions = mPrefMaxSuggestions;
         }
 
         int pos = 0;
 
         // Check if it's the same word, only caps are different
-        if (compareCaseInsensitive(mLowerOriginalWord, word, offset, length)) {
+        if (Utils.equalsIgnoreCase(mTypedWord, word, offset, length)) {
             // TODO: remove this surrounding if clause and move this logic to
             // getSuggestedWordBuilder.
             if (suggestions.size() > 0) {
-                final String currentHighestWordLowerCase =
-                        suggestions.get(0).toString().toLowerCase();
+                final String currentHighestWord = suggestions.get(0).toString();
                 // If the current highest word is also equal to typed word, we need to compare
                 // frequency to determine the insertion position. This does not ensure strictly
                 // correct ordering, but ensures the top score is on top which is enough for
                 // removing duplicates correctly.
-                if (compareCaseInsensitive(currentHighestWordLowerCase, word, offset, length)
-                        && freq <= priorities[0]) {
+                if (Utils.equalsIgnoreCase(currentHighestWord, word, offset, length)
+                        && score <= sortedScores[0]) {
                     pos = 1;
                 }
             }
@@ -475,24 +487,24 @@
                 if(bigramSuggestion >= 0) {
                     dataTypeForLog = Dictionary.DataType.BIGRAM;
                     // turn freq from bigram into multiplier specified above
-                    double multiplier = (((double) mBigramPriorities[bigramSuggestion])
+                    double multiplier = (((double) mBigramScores[bigramSuggestion])
                             / MAXIMUM_BIGRAM_FREQUENCY)
                             * (BIGRAM_MULTIPLIER_MAX - BIGRAM_MULTIPLIER_MIN)
                             + BIGRAM_MULTIPLIER_MIN;
                     /* Log.d(TAG,"bigram num: " + bigramSuggestion
                             + "  wordB: " + mBigramSuggestions.get(bigramSuggestion).toString()
-                            + "  currentPriority: " + freq + "  bigramPriority: "
-                            + mBigramPriorities[bigramSuggestion]
+                            + "  currentScore: " + score + "  bigramScore: "
+                            + mBigramScores[bigramSuggestion]
                             + "  multiplier: " + multiplier); */
-                    freq = (int)Math.round((freq * multiplier));
+                    score = (int)Math.round((score * multiplier));
                 }
             }
 
-            // Check the last one's priority and bail
-            if (priorities[prefMaxSuggestions - 1] >= freq) return true;
+            // Check the last one's score and bail
+            if (sortedScores[prefMaxSuggestions - 1] >= score) return true;
             while (pos < prefMaxSuggestions) {
-                if (priorities[pos] < freq
-                        || (priorities[pos] == freq && length < suggestions.get(pos).length())) {
+                if (sortedScores[pos] < score
+                        || (sortedScores[pos] == score && length < suggestions.get(pos).length())) {
                     break;
                 }
                 pos++;
@@ -502,12 +514,13 @@
             return true;
         }
 
-        System.arraycopy(priorities, pos, priorities, pos + 1, prefMaxSuggestions - pos - 1);
-        priorities[pos] = freq;
+        System.arraycopy(sortedScores, pos, sortedScores, pos + 1, prefMaxSuggestions - pos - 1);
+        sortedScores[pos] = score;
         int poolSize = mStringPool.size();
         StringBuilder sb = poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
                 : new StringBuilder(getApproxMaxWordLength());
         sb.setLength(0);
+        // TODO: Must pay attention to locale when changing case.
         if (mIsAllUpperCase) {
             sb.append(new String(word, offset, length).toUpperCase());
         } else if (mIsFirstCharCapitalized) {
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index fe7aac7..a8cdfc0 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -32,14 +32,14 @@
     public final List<SuggestedWordInfo> mSuggestedWordInfoList;
 
     private SuggestedWords(List<CharSequence> words, boolean typedWordValid,
-            boolean hasMinamlSuggestion, List<SuggestedWordInfo> suggestedWordInfoList) {
+            boolean hasMinimalSuggestion, List<SuggestedWordInfo> suggestedWordInfoList) {
         if (words != null) {
             mWords = words;
         } else {
             mWords = Collections.emptyList();
         }
         mTypedWordValid = typedWordValid;
-        mHasMinimalSuggestion = hasMinamlSuggestion;
+        mHasMinimalSuggestion = hasMinimalSuggestion;
         mSuggestedWordInfoList = suggestedWordInfoList;
     }
 
@@ -113,8 +113,8 @@
             return this;
         }
 
-        public Builder setHasMinimalSuggestion(boolean hasMinamlSuggestion) {
-            mHasMinimalSuggestion = hasMinamlSuggestion;
+        public Builder setHasMinimalSuggestion(boolean hasMinimalSuggestion) {
+            mHasMinimalSuggestion = hasMinimalSuggestion;
             return this;
         }
 
diff --git a/java/src/com/android/inputmethod/latin/SuggestionSpanPickedNotificationReceiver.java b/java/src/com/android/inputmethod/latin/SuggestionSpanPickedNotificationReceiver.java
new file mode 100644
index 0000000..4a3f42d
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/SuggestionSpanPickedNotificationReceiver.java
@@ -0,0 +1,43 @@
+/*
+ * 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 com.android.inputmethod.compat.SuggestionSpanUtils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class SuggestionSpanPickedNotificationReceiver extends BroadcastReceiver {
+    private static final boolean DBG = LatinImeLogger.sDBG;
+    private static final String TAG =
+            SuggestionSpanPickedNotificationReceiver.class.getSimpleName();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (SuggestionSpanUtils.ACTION_SUGGESTION_PICKED.equals(intent.getAction())) {
+            if (DBG) {
+                final String before = intent.getStringExtra(
+                        SuggestionSpanUtils.SUGGESTION_SPAN_PICKED_BEFORE);
+                final String after = intent.getStringExtra(
+                        SuggestionSpanUtils.SUGGESTION_SPAN_PICKED_AFTER);
+                Log.d(TAG, "Received notification picked: " + before + "," + after);
+            }
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/TextEntryState.java b/java/src/com/android/inputmethod/latin/TextEntryState.java
index 6319643..de13f3a 100644
--- a/java/src/com/android/inputmethod/latin/TextEntryState.java
+++ b/java/src/com/android/inputmethod/latin/TextEntryState.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.latin.Utils.RingCharBuffer;
+
 import android.util.Log;
 
 public class TextEntryState {
@@ -43,10 +45,12 @@
         sState = newState;
     }
 
-    public static void acceptedDefault(CharSequence typedWord, CharSequence actualWord) {
+    public static void acceptedDefault(CharSequence typedWord, CharSequence actualWord,
+            int separatorCode) {
         if (typedWord == null) return;
         setState(ACCEPTED_DEFAULT);
-        LatinImeLogger.logOnAutoSuggestion(typedWord.toString(), actualWord.toString());
+        LatinImeLogger.logOnAutoCorrection(
+                typedWord.toString(), actualWord.toString(), separatorCode);
         if (DEBUG)
             displayState("acceptedDefault", "typedWord", typedWord, "actualWord", actualWord);
     }
@@ -95,7 +99,7 @@
         if (DEBUG) displayState("onAbortRecorrection");
     }
 
-    public static void typedCharacter(char c, boolean isSeparator) {
+    public static void typedCharacter(char c, boolean isSeparator, int x, int y) {
         final boolean isSpace = (c == ' ');
         switch (sState) {
         case IN_WORD:
@@ -149,13 +153,19 @@
             setState(START);
             break;
         }
+        RingCharBuffer.getInstance().push(c, x, y);
+        if (isSeparator) {
+            LatinImeLogger.logOnInputSeparator();
+        } else {
+            LatinImeLogger.logOnInputChar();
+        }
         if (DEBUG) displayState("typedCharacter", "char", c, "isSeparator", isSeparator);
     }
     
     public static void backspace() {
         if (sState == ACCEPTED_DEFAULT) {
             setState(UNDO_COMMIT);
-            LatinImeLogger.logOnAutoSuggestionCanceled();
+            LatinImeLogger.logOnAutoCorrectionCancelled();
         } else if (sState == UNDO_COMMIT) {
             setState(IN_WORD);
         }
diff --git a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java
index 656e6f8..5b615ca 100644
--- a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java
@@ -44,12 +44,6 @@
     /** Maximum frequency for all pairs */
     private static final int FREQUENCY_MAX = 127;
 
-    /**
-     * If this pair is typed 6 times, it would be suggested.
-     * Should be smaller than ContactsDictionary.FREQUENCY_FOR_CONTACTS_BIGRAM
-     */
-    protected static final int SUGGEST_THRESHOLD = 6 * FREQUENCY_FOR_TYPED;
-
     /** Maximum number of pairs. Pruning will start when databases goes above this number. */
     private static int sMaxUserBigrams = 10000;
 
@@ -164,10 +158,14 @@
      * Pair will be added to the userbigram database.
      */
     public int addBigrams(String word1, String word2) {
-        // remove caps
+        // remove caps if second word is autocapitalized
         if (mIme != null && mIme.getCurrentWord().isAutoCapitalized()) {
             word2 = Character.toLowerCase(word2.charAt(0)) + word2.substring(1);
         }
+        // Do not insert a word as a bigram of itself
+        if (word1.equals(word2)) {
+            return 0;
+        }
 
         int freq = super.addBigram(word1, word2, FREQUENCY_FOR_TYPED);
         if (freq > FREQUENCY_MAX) freq = FREQUENCY_MAX;
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 1cbc434..6bdc0a8 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -16,6 +16,15 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.compat.InputMethodInfoCompatWrapper;
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
+import com.android.inputmethod.compat.InputTypeCompatUtils;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+
+import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.inputmethodservice.InputMethodService;
 import android.os.AsyncTask;
@@ -26,10 +35,6 @@
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.inputmethod.keyboard.KeyboardId;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -39,12 +44,17 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
 
 public class Utils {
     private static final String TAG = Utils.class.getSimpleName();
     private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4;
     private static boolean DBG = LatinImeLogger.sDBG;
+    private static boolean DBG_EDIT_DISTANCE = false;
 
     private Utils() {
         // Intentional empty constructor for utility class.
@@ -101,17 +111,49 @@
         }
     }
 
-    public static boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm) {
-        return imm.getEnabledInputMethodList().size() > 1
+    public static boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManagerCompatWrapper imm) {
+        final List<InputMethodInfoCompatWrapper> enabledImis = imm.getEnabledInputMethodList();
+
+        // Filters out IMEs that have auxiliary subtypes only (including either implicitly or
+        // explicitly enabled ones).
+        final ArrayList<InputMethodInfoCompatWrapper> filteredImis =
+                new ArrayList<InputMethodInfoCompatWrapper>();
+
+        outerloop:
+        for (InputMethodInfoCompatWrapper imi : enabledImis) {
+            // We can return true immediately after we find two or more filtered IMEs.
+            if (filteredImis.size() > 1) return true;
+            final List<InputMethodSubtypeCompatWrapper> subtypes =
+                    imm.getEnabledInputMethodSubtypeList(imi, true);
+            // IMEs that have no subtypes should be included.
+            if (subtypes.isEmpty()) {
+                filteredImis.add(imi);
+                continue;
+            }
+            // IMEs that have one or more non-auxiliary subtypes should be included.
+            for (InputMethodSubtypeCompatWrapper subtype : subtypes) {
+                if (!subtype.isAuxiliary()) {
+                    filteredImis.add(imi);
+                    continue outerloop;
+                }
+            }
+        }
+
+        return filteredImis.size() > 1
         // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
         // input method subtype (The current IME should be LatinIME.)
                 || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
     }
 
-    public static String getInputMethodId(InputMethodManager imm, String packageName) {
-        for (final InputMethodInfo imi : imm.getEnabledInputMethodList()) {
+    public static String getInputMethodId(InputMethodManagerCompatWrapper imm, String packageName) {
+        return getInputMethodInfo(imm, packageName).getId();
+    }
+
+    public static InputMethodInfoCompatWrapper getInputMethodInfo(
+            InputMethodManagerCompatWrapper imm, String packageName) {
+        for (final InputMethodInfoCompatWrapper imi : imm.getEnabledInputMethodList()) {
             if (imi.getPackageName().equals(packageName))
-                return imi.getId();
+                return imi;
         }
         throw new RuntimeException("Can not find input method id for " + packageName);
     }
@@ -202,11 +244,11 @@
                 return mCharBuf[mEnd];
             }
         }
-        public char getLastChar() {
-            if (mLength < 1) {
+        public char getBackwardNthChar(int n) {
+            if (mLength <= n || n < 0) {
                 return PLACEHOLDER_DELIMITER_CHAR;
             } else {
-                return mCharBuf[normalize(mEnd - 1)];
+                return mCharBuf[normalize(mEnd - n - 1)];
             }
         }
         public int getPreviousX(char c, int back) {
@@ -227,9 +269,16 @@
                 return mYBuf[index];
             }
         }
-        public String getLastString() {
+        public String getLastWord(int ignoreCharCount) {
             StringBuilder sb = new StringBuilder();
-            for (int i = 0; i < mLength; ++i) {
+            int i = ignoreCharCount;
+            for (; i < mLength; ++i) {
+                char c = mCharBuf[normalize(mEnd - 1 - i)];
+                if (!((LatinIME)mContext).isWordSeparator(c)) {
+                    break;
+                }
+            }
+            for (; i < mLength; ++i) {
                 char c = mCharBuf[normalize(mEnd - 1 - i)];
                 if (!((LatinIME)mContext).isWordSeparator(c)) {
                     sb.append(c);
@@ -244,6 +293,8 @@
         }
     }
 
+
+    /* Damerau-Levenshtein distance */
     public static int editDistance(CharSequence s, CharSequence t) {
         if (s == null || t == null) {
             throw new IllegalArgumentException("editDistance: Arguments should not be null.");
@@ -259,14 +310,29 @@
         }
         for (int i = 0; i < sl; ++i) {
             for (int j = 0; j < tl; ++j) {
-                if (Character.toLowerCase(s.charAt(i)) == Character.toLowerCase(t.charAt(j))) {
-                    dp[i + 1][j + 1] = dp[i][j];
-                } else {
-                    dp[i + 1][j + 1] = 1 + Math.min(dp[i][j],
-                            Math.min(dp[i + 1][j], dp[i][j + 1]));
+                final char sc = Character.toLowerCase(s.charAt(i));
+                final char tc = Character.toLowerCase(t.charAt(j));
+                final int cost = sc == tc ? 0 : 1;
+                dp[i + 1][j + 1] = Math.min(
+                        dp[i][j + 1] + 1, Math.min(dp[i + 1][j] + 1, dp[i][j] + cost));
+                // Overwrite for transposition cases
+                if (i > 0 && j > 0
+                        && sc == Character.toLowerCase(t.charAt(j - 1))
+                        && tc == Character.toLowerCase(s.charAt(i - 1))) {
+                    dp[i + 1][j + 1] = Math.min(dp[i + 1][j + 1], dp[i - 1][j - 1] + cost);
                 }
             }
         }
+        if (DBG_EDIT_DISTANCE) {
+            Log.d(TAG, "editDistance:" + s + "," + t);
+            for (int i = 0; i < dp.length; ++i) {
+                StringBuffer sb = new StringBuffer();
+                for (int j = 0; j < dp[i].length; ++j) {
+                    sb.append(dp[i][j]).append(',');
+                }
+                Log.d(TAG, i + ":" + sb.toString());
+            }
+        }
         return dp[sl][tl];
     }
 
@@ -285,7 +351,7 @@
 
     // In dictionary.cpp, getSuggestion() method,
     // suggestion scores are computed using the below formula.
-    // original score (called 'frequency')
+    // original score
     //  := pow(mTypedLetterMultiplier (this is defined 2),
     //         (the number of matched characters between typed word and suggested word))
     //     * (individual word's score which defined in the unigram dictionary,
@@ -295,7 +361,7 @@
     //       (full match up to min(before.length(), after.length())
     //       => Then multiply by FULL_MATCHED_WORDS_PROMOTION_RATE (this is defined 1.2)
     //     - If the word is a true full match except for differences in accents or
-    //       capitalization, then treat it as if the frequency was 255.
+    //       capitalization, then treat it as if the score was 255.
     //     - If before.length() == after.length()
     //       => multiply by mFullWordMultiplier (this is defined 2))
     // So, maximum original score is pow(2, min(before.length(), after.length())) * 255 * 2 * 1.2
@@ -306,6 +372,7 @@
     private static final int MAX_INITIAL_SCORE = 255;
     private static final int TYPED_LETTER_MULTIPLIER = 2;
     private static final int FULL_WORD_MULTIPLIER = 2;
+    private static final int S_INT_MAX = 2147483647;
     public static double calcNormalizedScore(CharSequence before, CharSequence after, int score) {
         final int beforeLength = before.length();
         final int afterLength = after.length();
@@ -313,8 +380,16 @@
         final int distance = editDistance(before, after);
         // If afterLength < beforeLength, the algorithm is suggesting a word by excessive character
         // correction.
-        final double maximumScore = MAX_INITIAL_SCORE
-                * Math.pow(TYPED_LETTER_MULTIPLIER, Math.min(beforeLength, afterLength))
+        int spaceCount = 0;
+        for (int i = 0; i < afterLength; ++i) {
+            if (after.charAt(i) == Keyboard.CODE_SPACE) {
+                ++spaceCount;
+            }
+        }
+        if (spaceCount == afterLength) return 0;
+        final double maximumScore = score == S_INT_MAX ? S_INT_MAX : MAX_INITIAL_SCORE
+                * Math.pow(
+                        TYPED_LETTER_MULTIPLIER, Math.min(beforeLength, afterLength - spaceCount))
                 * FULL_WORD_MULTIPLIER;
         // add a weight based on edit distance.
         // distance <= max(afterLength, beforeLength) == afterLength,
@@ -485,7 +560,7 @@
         case InputType.TYPE_CLASS_PHONE:
             return KeyboardId.MODE_PHONE;
         case InputType.TYPE_CLASS_TEXT:
-            if (Utils.isEmailVariation(variation)) {
+            if (InputTypeCompatUtils.isEmailVariation(variation)) {
                 return KeyboardId.MODE_EMAIL;
             } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) {
                 return KeyboardId.MODE_URL;
@@ -501,42 +576,6 @@
         }
     }
 
-    public static boolean isEmailVariation(int variation) {
-        return variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
-                || variation == InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
-    }
-
-    public static boolean isWebInputType(int inputType) {
-        final int variation =
-            inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION);
-        return (variation
-            == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT))
-            || (variation
-            == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD))
-            || (variation
-            == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS));
-    }
-
-    // Please refer to TextView.isPasswordInputType
-    public static boolean isPasswordInputType(int inputType) {
-        final int variation =
-                inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION);
-        return (variation
-                == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD))
-                || (variation
-                == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD))
-                || (variation
-                == (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
-    }
-
-    // Please refer to TextView.isVisiblePasswordInputType
-    public static boolean isVisiblePasswordInputType(int inputType) {
-        final int variation =
-                inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION);
-        return variation
-                == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
-    }
-
     public static boolean containsInCsv(String key, String csv) {
         if (csv == null)
             return false;
@@ -560,7 +599,9 @@
      * @return main dictionary resource id
      */
     public static int getMainDictionaryResourceId(Resources res) {
-        return res.getIdentifier("main", "raw", LatinIME.class.getPackage().getName());
+        final String MAIN_DIC_NAME = "main";
+        String packageName = LatinIME.class.getPackage().getName();
+        return res.getIdentifier(MAIN_DIC_NAME, "raw", packageName);
     }
 
     public static void loadNativeLibrary() {
@@ -570,4 +611,108 @@
             Log.e(TAG, "Could not load native library jni_latinime");
         }
     }
+
+    /**
+     * Returns true if a and b are equal ignoring the case of the character.
+     * @param a first character to check
+     * @param b second character to check
+     * @return {@code true} if a and b are equal, {@code false} otherwise.
+     */
+    public static boolean equalsIgnoreCase(char a, char b) {
+        // Some language, such as Turkish, need testing both cases.
+        return a == b
+                || Character.toLowerCase(a) == Character.toLowerCase(b)
+                || Character.toUpperCase(a) == Character.toUpperCase(b);
+    }
+
+    /**
+     * Returns true if a and b are equal ignoring the case of the characters, including if they are
+     * both null.
+     * @param a first CharSequence to check
+     * @param b second CharSequence to check
+     * @return {@code true} if a and b are equal, {@code false} otherwise.
+     */
+    public static boolean equalsIgnoreCase(CharSequence a, CharSequence b) {
+        if (a == b)
+            return true;  // including both a and b are null.
+        if (a == null || b == null)
+            return false;
+        final int length = a.length();
+        if (length != b.length())
+            return false;
+        for (int i = 0; i < length; i++) {
+            if (!equalsIgnoreCase(a.charAt(i), b.charAt(i)))
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns true if a and b are equal ignoring the case of the characters, including if a is null
+     * and b is zero length.
+     * @param a CharSequence to check
+     * @param b character array to check
+     * @param offset start offset of array b
+     * @param length length of characters in array b
+     * @return {@code true} if a and b are equal, {@code false} otherwise.
+     * @throws IndexOutOfBoundsException
+     *   if {@code offset < 0 || length < 0 || offset + length > data.length}.
+     * @throws NullPointerException if {@code b == null}.
+     */
+    public static boolean equalsIgnoreCase(CharSequence a, char[] b, int offset, int length) {
+        if (offset < 0 || length < 0 || length > b.length - offset)
+            throw new IndexOutOfBoundsException("array.length=" + b.length + " offset=" + offset
+                    + " length=" + length);
+        if (a == null)
+            return length == 0;  // including a is null and b is zero length.
+        if (a.length() != length)
+            return false;
+        for (int i = 0; i < length; i++) {
+            if (!equalsIgnoreCase(a.charAt(i), b[offset + i]))
+                return false;
+        }
+        return true;
+    }
+
+    public static float getDipScale(Context context) {
+        final float scale = context.getResources().getDisplayMetrics().density;
+        return scale;
+    }
+
+    /** Convert pixel to DIP */
+    public static int dipToPixel(float scale, int dip) {
+        return (int) (dip * scale + 0.5);
+    }
+
+    public static Locale setSystemLocale(Resources res, Locale newLocale) {
+        final Configuration conf = res.getConfiguration();
+        final Locale saveLocale = conf.locale;
+        conf.locale = newLocale;
+        res.updateConfiguration(conf, res.getDisplayMetrics());
+        return saveLocale;
+    }
+
+    private static final HashMap<String, Locale> sLocaleCache = new HashMap<String, Locale>();
+
+    public static Locale constructLocaleFromString(String localeStr) {
+        if (localeStr == null)
+            return null;
+        synchronized (sLocaleCache) {
+            if (sLocaleCache.containsKey(localeStr))
+                return sLocaleCache.get(localeStr);
+            Locale retval = null;
+            String[] localeParams = localeStr.split("_", 3);
+            if (localeParams.length == 1) {
+                retval = new Locale(localeParams[0]);
+            } else if (localeParams.length == 2) {
+                retval = new Locale(localeParams[0], localeParams[1]);
+            } else if (localeParams.length == 3) {
+                retval = new Locale(localeParams[0], localeParams[1], localeParams[2]);
+            }
+            if (retval != null) {
+                sLocaleCache.put(localeStr, retval);
+            }
+            return retval;
+        }
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
index 2389d4e..4377373 100644
--- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
+++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
@@ -39,6 +39,7 @@
     public static WhitelistDictionary init(Context context) {
         synchronized (sInstance) {
             if (context != null) {
+                // Wordlist is initialized by the proper language in Suggestion.java#init
                 sInstance.initWordlist(
                         context.getResources().getStringArray(R.array.wordlist_whitelist));
             } else {
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 0258389..af5e4b1 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -31,18 +31,18 @@
     /**
      * The list of unicode values for each keystroke (including surrounding keys)
      */
-    private final ArrayList<int[]> mCodes;
+    private ArrayList<int[]> mCodes;
 
     private int mTypedLength;
-    private final int[] mXCoordinates;
-    private final int[] mYCoordinates;
+    private int[] mXCoordinates;
+    private int[] mYCoordinates;
 
     /**
      * The word chosen from the candidate list, until it is committed.
      */
     private String mPreferredWord;
 
-    private final StringBuilder mTypedWord;
+    private StringBuilder mTypedWord;
 
     private int mCapsCount;
 
@@ -62,7 +62,11 @@
         mYCoordinates = new int[N];
     }
 
-    WordComposer(WordComposer source) {
+    public WordComposer(WordComposer source) {
+        init(source);
+    }
+
+    public void init(WordComposer source) {
         mCodes = new ArrayList<int[]>(source.mCodes);
         mPreferredWord = source.mPreferredWord;
         mTypedWord = new StringBuilder(source.mTypedWord);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java
new file mode 100644
index 0000000..63c6d69
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java
@@ -0,0 +1,115 @@
+/*
+ * 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.spellcheck;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import com.android.inputmethod.compat.ArraysCompatUtils;
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.Dictionary.DataType;
+import com.android.inputmethod.latin.Dictionary.WordCallback;
+import com.android.inputmethod.latin.DictionaryFactory;
+import com.android.inputmethod.latin.Utils;
+import com.android.inputmethod.latin.WordComposer;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Implements spell checking methods.
+ */
+public class SpellChecker {
+
+    public final Dictionary mDictionary;
+
+    public SpellChecker(final Context context, final Locale locale) {
+        final Resources resources = context.getResources();
+        final int fallbackResourceId = Utils.getMainDictionaryResourceId(resources);
+        mDictionary = DictionaryFactory.createDictionaryFromManager(context, locale,
+                fallbackResourceId);
+    }
+
+    // Note : this must be reentrant
+    /**
+     * Finds out whether a word is in the dictionary or not.
+     *
+     * @param text the sequence containing the word to check for.
+     * @param start the index of the first character of the word in text.
+     * @param end the index of the next-to-last character in text.
+     * @return true if the word is in the dictionary, false otherwise.
+     */
+    public boolean isCorrect(final CharSequence text, final int start, final int end) {
+        return mDictionary.isValidWord(text.subSequence(start, end));
+    }
+
+    private static class SuggestionsGatherer implements WordCallback {
+        private final int DEFAULT_SUGGESTION_LENGTH = 16;
+        private final List<String> mSuggestions = new LinkedList<String>();
+        private int[] mScores = new int[DEFAULT_SUGGESTION_LENGTH];
+        private int mLength = 0;
+
+        @Override
+        synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score,
+                int dicTypeId, DataType dataType) {
+            if (mLength >= mScores.length) {
+                final int newLength = mScores.length * 2;
+                mScores = new int[newLength];
+            }
+            final int positionIndex = ArraysCompatUtils.binarySearch(mScores, 0, mLength, score);
+            // binarySearch returns the index if the element exists, and -<insertion index> - 1
+            // if it doesn't. See documentation for binarySearch.
+            final int insertionIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1;
+            System.arraycopy(mScores, insertionIndex, mScores, insertionIndex + 1,
+                    mLength - insertionIndex);
+            mLength += 1;
+            mScores[insertionIndex] = score;
+            mSuggestions.add(insertionIndex, new String(word, wordOffset, wordLength));
+            return true;
+        }
+
+        public List<String> getGatheredSuggestions() {
+            return mSuggestions;
+        }
+    }
+
+    // Note : this must be reentrant
+    /**
+     * Gets a list of suggestions for a specific string.
+     *
+     * This returns a list of possible corrections for the text passed as an
+     * arguments. It may split or group words, and even perform grammatical
+     * analysis.
+     *
+     * @param text the sequence containing the word to check for.
+     * @param start the index of the first character of the word in text.
+     * @param end the index of the next-to-last character in text.
+     * @return a list of possible suggestions to replace the text.
+     */
+    public List<String> getSuggestions(final CharSequence text, final int start, final int end) {
+        final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer();
+        final WordComposer composer = new WordComposer();
+        for (int i = start; i < end; ++i) {
+            int character = text.charAt(i);
+            composer.add(character, new int[] { character },
+                    WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
+        }
+        mDictionary.getWords(composer, suggestionsGatherer);
+        return suggestionsGatherer.getGatheredSuggestions();
+    }
+}
diff --git a/native/Android.mk b/native/Android.mk
index c8342e3..d9f4f1d 100644
--- a/native/Android.mk
+++ b/native/Android.mk
@@ -3,10 +3,15 @@
 
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
 
+LOCAL_CFLAGS += -Werror -Wall
+
+# To suppress compiler warnings for unused variables/functions used for debug features etc.
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
+
 LOCAL_SRC_FILES := \
     jni/com_android_inputmethod_keyboard_ProximityInfo.cpp \
     jni/com_android_inputmethod_latin_BinaryDictionary.cpp \
-    jni/onload.cpp \
+    jni/jni_common.cpp \
     src/bigram_dictionary.cpp \
     src/char_utils.cpp \
     src/dictionary.cpp \
diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
index 3db89ed..f3e2a7e 100644
--- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
+++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
@@ -19,28 +19,15 @@
 
 #include "com_android_inputmethod_keyboard_ProximityInfo.h"
 #include "jni.h"
+#include "jni_common.h"
 #include "proximity_info.h"
 
 #include <assert.h>
 #include <errno.h>
 #include <stdio.h>
 
-// ----------------------------------------------------------------------------
-
 namespace latinime {
 
-//
-// helper function to throw an exception
-//
-static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) {
-    if (jclass cls = env->FindClass(ex)) {
-        char msg[1000];
-        snprintf(msg, sizeof(msg), fmt, data);
-        env->ThrowNew(cls, msg);
-        env->DeleteLocalRef(cls);
-    }
-}
-
 static jint latinime_Keyboard_setProximityInfo(JNIEnv *env, jobject object,
         jint maxProximityCharsSize, jint displayWidth, jint displayHeight, jint gridWidth,
         jint gridHeight, jintArray proximityCharsArray) {
@@ -57,34 +44,15 @@
     delete pi;
 }
 
-// ----------------------------------------------------------------------------
-
 static JNINativeMethod sKeyboardMethods[] = {
     {"setProximityInfoNative", "(IIIII[I)I", (void*)latinime_Keyboard_setProximityInfo},
     {"releaseProximityInfoNative", "(I)V", (void*)latinime_Keyboard_release}
 };
 
-static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods,
-        int numMethods) {
-    jclass clazz;
-
-    clazz = env->FindClass(className);
-    if (clazz == NULL) {
-        LOGE("Native registration unable to find class '%s'", className);
-        return JNI_FALSE;
-    }
-    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
-        LOGE("RegisterNatives failed for '%s'", className);
-        return JNI_FALSE;
-    }
-
-    return JNI_TRUE;
-}
-
 int register_ProximityInfo(JNIEnv *env) {
     const char* const kClassPathName = "com/android/inputmethod/keyboard/ProximityInfo";
     return registerNativeMethods(env, kClassPathName, sKeyboardMethods,
             sizeof(sKeyboardMethods) / sizeof(sKeyboardMethods[0]));
 }
 
-}; // namespace latinime
+} // namespace latinime
diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.h b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.h
index bdeeb8f..4a1e83b 100644
--- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.h
+++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.h
@@ -21,7 +21,9 @@
 #include "jni.h"
 
 namespace latinime {
+
 int register_ProximityInfo(JNIEnv *env);
+
 }
 
 #endif // _COM_ANDROID_INPUTMETHOD_KEYBOARD_PROXIMITYINFO_H
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 555a522..ce874d8 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -20,6 +20,7 @@
 #include "com_android_inputmethod_latin_BinaryDictionary.h"
 #include "dictionary.h"
 #include "jni.h"
+#include "jni_common.h"
 #include "proximity_info.h"
 
 #include <assert.h>
@@ -35,22 +36,8 @@
 #include <stdlib.h>
 #endif // USE_MMAP_FOR_DICTIONARY
 
-// ----------------------------------------------------------------------------
-
 namespace latinime {
 
-//
-// helper function to throw an exception
-//
-static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) {
-    if (jclass cls = env->FindClass(ex)) {
-        char msg[1000];
-        snprintf(msg, sizeof(msg), fmt, data);
-        env->ThrowNew(cls, msg);
-        env->DeleteLocalRef(cls);
-    }
-}
-
 static jint latinime_BinaryDictionary_open(JNIEnv *env, jobject object,
         jstring sourceDir, jlong dictOffset, jlong dictSize,
         jint typedLetterMultiplier, jint fullWordMultiplier, jint maxWordLength, jint maxWords,
@@ -131,7 +118,6 @@
     Dictionary *dictionary = (Dictionary*)dict;
     if (!dictionary) return 0;
     ProximityInfo *pInfo = (ProximityInfo*)proximityInfo;
-    if (!pInfo) return 0;
 
     int *xCoordinates = env->GetIntArrayElements(xCoordinatesArray, NULL);
     int *yCoordinates = env->GetIntArrayElements(yCoordinatesArray, NULL);
@@ -209,8 +195,6 @@
     delete dictionary;
 }
 
-// ----------------------------------------------------------------------------
-
 static JNINativeMethod sMethods[] = {
     {"openNative", "(Ljava/lang/String;JJIIIII)I", (void*)latinime_BinaryDictionary_open},
     {"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close},
@@ -219,27 +203,10 @@
     {"getBigramsNative", "(I[CI[II[C[IIII)I", (void*)latinime_BinaryDictionary_getBigrams}
 };
 
-static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods,
-        int numMethods) {
-    jclass clazz;
-
-    clazz = env->FindClass(className);
-    if (clazz == NULL) {
-        LOGE("Native registration unable to find class '%s'", className);
-        return JNI_FALSE;
-    }
-    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
-        LOGE("RegisterNatives failed for '%s'", className);
-        return JNI_FALSE;
-    }
-
-    return JNI_TRUE;
-}
-
 int register_BinaryDictionary(JNIEnv *env) {
     const char* const kClassPathName = "com/android/inputmethod/latin/BinaryDictionary";
     return registerNativeMethods(env, kClassPathName, sMethods,
             sizeof(sMethods) / sizeof(sMethods[0]));
 }
 
-}; // namespace latinime
+} // namespace latinime
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.h b/native/jni/com_android_inputmethod_latin_BinaryDictionary.h
index f7cd81f..1b1ba7f 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.h
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.h
@@ -21,7 +21,9 @@
 #include "jni.h"
 
 namespace latinime {
+
 int register_BinaryDictionary(JNIEnv *env);
+
 }
 
 #endif // _COM_ANDROID_INPUTMETHOD_LATIN_BINARYDICTIONARY_H
diff --git a/native/jni/onload.cpp b/native/jni/jni_common.cpp
similarity index 73%
rename from native/jni/onload.cpp
rename to native/jni/jni_common.cpp
index f02c9a0..8643f72 100644
--- a/native/jni/onload.cpp
+++ b/native/jni/jni_common.cpp
@@ -26,11 +26,8 @@
 #include <errno.h>
 #include <stdio.h>
 
-// ----------------------------------------------------------------------------
-
 using namespace latinime;
 
-
 /*
  * Returns the JNI version on success, -1 on failure.
  */
@@ -60,3 +57,23 @@
 bail:
     return result;
 }
+
+namespace latinime {
+
+int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* methods,
+        int numMethods) {
+    jclass clazz = env->FindClass(className);
+    if (clazz == NULL) {
+        LOGE("Native registration unable to find class '%s'", className);
+        return JNI_FALSE;
+    }
+    if (env->RegisterNatives(clazz, methods, numMethods) < 0) {
+        LOGE("RegisterNatives failed for '%s'", className);
+        env->DeleteLocalRef(clazz);
+        return JNI_FALSE;
+    }
+    env->DeleteLocalRef(clazz);
+    return JNI_TRUE;
+}
+
+} // namespace latinime
diff --git a/java/res/drawable/btn_candidate_holo.xml b/native/jni/jni_common.h
similarity index 63%
copy from java/res/drawable/btn_candidate_holo.xml
copy to native/jni/jni_common.h
index 66cd246..c502fa3 100644
--- a/java/res/drawable/btn_candidate_holo.xml
+++ b/native/jni/jni_common.h
@@ -1,8 +1,6 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** 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.
@@ -16,12 +14,17 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
--->
 
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android"
->
-    <item
-        android:state_pressed="true"
-        android:drawable="@drawable/btn_keyboard_key_popup_selected_holo" />
-</selector>
+#ifndef LATINIME_JNI_COMMON_H
+#define LATINIME_JNI_COMMON_H
+
+#include "jni.h"
+
+namespace latinime {
+
+int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* methods,
+        int numMethods);
+
+} // namespace latinime
+
+#endif // LATINIME_JNI_COMMON_H
diff --git a/native/src/bigram_dictionary.cpp b/native/src/bigram_dictionary.cpp
index 5ec310f..11e6dc2 100644
--- a/native/src/bigram_dictionary.cpp
+++ b/native/src/bigram_dictionary.cpp
@@ -30,8 +30,10 @@
     : DICT(dict), MAX_WORD_LENGTH(maxWordLength),
     MAX_ALTERNATIVES(maxAlternatives), IS_LATEST_DICT_VERSION(isLatestDictVersion),
     HAS_BIGRAM(hasBigram), mParentDictionary(parentDictionary) {
-    if (DEBUG_DICT) LOGI("BigramDictionary - constructor");
-    if (DEBUG_DICT) LOGI("Has Bigram : %d", hasBigram);
+    if (DEBUG_DICT) {
+        LOGI("BigramDictionary - constructor");
+        LOGI("Has Bigram : %d", hasBigram);
+    }
 }
 
 BigramDictionary::~BigramDictionary() {
@@ -54,7 +56,9 @@
         }
         insertAt++;
     }
-    if (DEBUG_DICT) LOGI("Bigram: InsertAt -> %d maxBigrams: %d", insertAt, mMaxBigrams);
+    if (DEBUG_DICT) {
+        LOGI("Bigram: InsertAt -> %d maxBigrams: %d", insertAt, mMaxBigrams);
+    }
     if (insertAt < mMaxBigrams) {
         memmove((char*) mBigramFreq + (insertAt + 1) * sizeof(mBigramFreq[0]),
                (char*) mBigramFreq + insertAt * sizeof(mBigramFreq[0]),
@@ -68,7 +72,9 @@
             *dest++ = *word++;
         }
         *dest = 0; // NULL terminate
-        if (DEBUG_DICT) LOGI("Bigram: Added word at %d", insertAt);
+        if (DEBUG_DICT) {
+            LOGI("Bigram: Added word at %d", insertAt);
+        }
         return true;
     }
     return false;
@@ -105,9 +111,10 @@
     mMaxBigrams = maxBigrams;
 
     if (HAS_BIGRAM && IS_LATEST_DICT_VERSION) {
-        int pos = mParentDictionary->isValidWordRec(
-                DICTIONARY_HEADER_SIZE, prevWord, 0, prevWordLength);
-        if (DEBUG_DICT) LOGI("Pos -> %d", pos);
+        int pos = mParentDictionary->getBigramPosition(prevWord, prevWordLength);
+        if (DEBUG_DICT) {
+            LOGI("Pos -> %d", pos);
+        }
         if (pos < 0) {
             return 0;
         }
@@ -151,7 +158,9 @@
         }
         pos = followDownBranchAddress; // pos start at count
         int count = DICT[pos] & 0xFF;
-        if (DEBUG_DICT) LOGI("count - %d",count);
+        if (DEBUG_DICT) {
+            LOGI("count - %d",count);
+        }
         pos++;
         for (int i = 0; i < count; i++) {
             // pos at data
@@ -225,7 +234,9 @@
         }
         depth++;
         if (followDownBranchAddress == 0) {
-            if (DEBUG_DICT) LOGI("ERROR!!! Cannot find bigram!!");
+            if (DEBUG_DICT) {
+                LOGI("ERROR!!! Cannot find bigram!!");
+            }
             break;
         }
     }
diff --git a/native/src/bigram_dictionary.h b/native/src/bigram_dictionary.h
index d658b93..c07458a 100644
--- a/native/src/bigram_dictionary.h
+++ b/native/src/bigram_dictionary.h
@@ -50,6 +50,7 @@
     int *mInputCodes;
     int mInputLength;
 };
-// ----------------------------------------------------------------------------
-}; // namespace latinime
+
+} // namespace latinime
+
 #endif // LATINIME_BIGRAM_DICTIONARY_H
diff --git a/native/src/char_utils.h b/native/src/char_utils.h
index 921ecb4..a69a35e 100644
--- a/native/src/char_utils.h
+++ b/native/src/char_utils.h
@@ -21,6 +21,6 @@
 
 unsigned short latin_tolower(unsigned short c);
 
-}; // namespace latinime
+} // namespace latinime
 
 #endif // LATINIME_CHAR_UTILS_H
diff --git a/native/src/debug.h b/native/src/debug.h
index ae629b2..38b2f10 100644
--- a/native/src/debug.h
+++ b/native/src/debug.h
@@ -28,6 +28,7 @@
     output[i] = 0;
     return output;
 }
+
 static inline unsigned char* convertToUnibyteStringAndReplaceLastChar(unsigned short* input,
         unsigned char* output, const unsigned int length, unsigned char c) {
     int i = 0;
@@ -37,6 +38,7 @@
     output[i] = 0;
     return output;
 }
+
 static inline void LOGI_S16(unsigned short* string, const unsigned int length) {
     unsigned char tmp_buffer[length];
     convertToUnibyteString(string, tmp_buffer, length);
@@ -46,6 +48,7 @@
     // TODO : refactor this in a blocking log or something.
     // usleep(10);
 }
+
 static inline void LOGI_S16_PLUS(unsigned short* string, const unsigned int length,
         unsigned char c) {
     unsigned char tmp_buffer[length+1];
diff --git a/native/src/defines.h b/native/src/defines.h
index 00cbb6c..0a32405 100644
--- a/native/src/defines.h
+++ b/native/src/defines.h
@@ -77,8 +77,8 @@
 }
 
 #else // FLAG_DBG
-#define LOGE
-#define LOGI
+#define LOGE(fmt, ...)
+#define LOGI(fmt, ...)
 #define DEBUG_DICT false
 #define DEBUG_DICT_FULL false
 #define DEBUG_SHOW_FOUND_WORD false
@@ -138,12 +138,14 @@
 #define SUGGEST_WORDS_WITH_SPACE_PROXIMITY true
 
 // The following "rate"s are used as a multiplier before dividing by 100, so they are in percent.
-#define WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE 70
-#define WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE 80
+#define WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE 80
+#define WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X 12
+#define WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE 67
 #define WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE 75
 #define WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE 75
 #define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 60
 #define FULL_MATCHED_WORDS_PROMOTION_RATE 120
+#define WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE 90
 
 // This should be greater than or equal to MAX_WORD_LENGTH defined in BinaryDictionary.java
 // This is only used for the size of array. Not to be used in c functions.
diff --git a/native/src/dictionary.cpp b/native/src/dictionary.cpp
index d69cb2a..9e32ee8 100644
--- a/native/src/dictionary.cpp
+++ b/native/src/dictionary.cpp
@@ -53,45 +53,16 @@
     return ((mDict[1] & 0xFF) == 1);
 }
 
-// TODO: use uint16_t instead of unsigned short
 bool Dictionary::isValidWord(unsigned short *word, int length) {
+    return mUnigramDictionary->isValidWord(word, length);
+}
+
+int Dictionary::getBigramPosition(unsigned short *word, int length) {
     if (IS_LATEST_DICT_VERSION) {
-        return (isValidWordRec(DICTIONARY_HEADER_SIZE, word, 0, length) != NOT_VALID_WORD);
+        return mUnigramDictionary->getBigramPosition(DICTIONARY_HEADER_SIZE, word, 0, length);
     } else {
-        return (isValidWordRec(0, word, 0, length) != NOT_VALID_WORD);
+        return mUnigramDictionary->getBigramPosition(0, word, 0, length);
     }
 }
 
-int Dictionary::isValidWordRec(int pos, unsigned short *word, int offset, int length) {
-    // returns address of bigram data of that word
-    // return -99 if not found
-
-    int count = Dictionary::getCount(mDict, &pos);
-    unsigned short currentChar = (unsigned short) word[offset];
-    for (int j = 0; j < count; j++) {
-        unsigned short c = Dictionary::getChar(mDict, &pos);
-        int terminal = Dictionary::getTerminal(mDict, &pos);
-        int childPos = Dictionary::getAddress(mDict, &pos);
-        if (c == currentChar) {
-            if (offset == length - 1) {
-                if (terminal) {
-                    return (pos+1);
-                }
-            } else {
-                if (childPos != 0) {
-                    int t = isValidWordRec(childPos, word, offset + 1, length);
-                    if (t > 0) {
-                        return t;
-                    }
-                }
-            }
-        }
-        if (terminal) {
-            Dictionary::getFreq(mDict, IS_LATEST_DICT_VERSION, &pos);
-        }
-        // There could be two instances of each alphabet - upper and lower case. So continue
-        // looking ...
-    }
-    return NOT_VALID_WORD;
-}
 } // namespace latinime
diff --git a/native/src/dictionary.h b/native/src/dictionary.h
index 13b2a28..3dc577a 100644
--- a/native/src/dictionary.h
+++ b/native/src/dictionary.h
@@ -43,7 +43,6 @@
     }
 
     bool isValidWord(unsigned short *word, int length);
-    int isValidWordRec(int pos, unsigned short *word, int offset, int length);
     void *getDict() { return (void *)mDict; }
     int getDictSize() { return mDictSize; }
     int getMmapFd() { return mMmapFd; }
@@ -63,6 +62,9 @@
             const int pos, unsigned short *c, int *childrenPosition,
             bool *terminal, int *freq);
 
+    // TODO: delete this
+    int getBigramPosition(unsigned short *word, int length);
+
 private:
     bool hasBigram();
 
@@ -79,7 +81,6 @@
     BigramDictionary *mBigramDictionary;
 };
 
-// ----------------------------------------------------------------------------
 // public static utility methods
 // static inline methods should be defined in the header file
 inline unsigned short Dictionary::getChar(const unsigned char *dict, int *pos) {
@@ -132,7 +133,6 @@
     return freq;
 }
 
-
 inline int Dictionary::wideStrLen(unsigned short *str) {
     if (!str) return 0;
     unsigned short *end = str;
@@ -156,5 +156,6 @@
     return position;
 }
 
-}; // namespace latinime
+} // namespace latinime
+
 #endif // LATINIME_DICTIONARY_H
diff --git a/native/src/proximity_info.cpp b/native/src/proximity_info.cpp
index 102123c..209c31e 100644
--- a/native/src/proximity_info.cpp
+++ b/native/src/proximity_info.cpp
@@ -22,6 +22,7 @@
 #include "proximity_info.h"
 
 namespace latinime {
+
 ProximityInfo::ProximityInfo(const int maxProximityCharsSize, const int keyboardWidth,
         const int keyboardHeight, const int gridWidth, const int gridHeight,
         const uint32_t *proximityCharsArray)
@@ -61,4 +62,5 @@
     }
     return false;
 }
-}  // namespace latinime
+
+} // namespace latinime
diff --git a/native/src/proximity_info.h b/native/src/proximity_info.h
index 0f12018..327cd09 100644
--- a/native/src/proximity_info.h
+++ b/native/src/proximity_info.h
@@ -32,14 +32,16 @@
     bool hasSpaceProximity(const int x, const int y) const;
 private:
     int getStartIndexFromCoordinates(const int x, const int y) const;
-    const int CELL_WIDTH;
-    const int CELL_HEIGHT;
+    const int MAX_PROXIMITY_CHARS_SIZE;
     const int KEYBOARD_WIDTH;
     const int KEYBOARD_HEIGHT;
     const int GRID_WIDTH;
     const int GRID_HEIGHT;
-    const int MAX_PROXIMITY_CHARS_SIZE;
+    const int CELL_WIDTH;
+    const int CELL_HEIGHT;
     uint32_t *mProximityCharsArray;
 };
-}; // namespace latinime
+
+} // namespace latinime
+
 #endif // LATINIME_PROXIMITY_INFO_H
diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp
index 30fbaea..e3296f1 100644
--- a/native/src/unigram_dictionary.cpp
+++ b/native/src/unigram_dictionary.cpp
@@ -16,8 +16,6 @@
 */
 
 #include <assert.h>
-#include <fcntl.h>
-#include <stdio.h>
 #include <string.h>
 
 #define LOG_TAG "LatinIME: unigram_dictionary.cpp"
@@ -34,16 +32,20 @@
         { 'o', 'e' },
         { 'u', 'e' } };
 
-UnigramDictionary::UnigramDictionary(const unsigned char *dict, int typedLetterMultiplier,
+// TODO: check the header
+UnigramDictionary::UnigramDictionary(const uint8_t* const streamStart, int typedLetterMultiplier,
         int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars,
         const bool isLatestDictVersion)
-    : DICT(dict), MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords),
+    : DICT_ROOT(streamStart),
+    MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords),
     MAX_PROXIMITY_CHARS(maxProximityChars), IS_LATEST_DICT_VERSION(isLatestDictVersion),
     TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier),
     ROOT_POS(isLatestDictVersion ? DICTIONARY_HEADER_SIZE : 0),
     BYTES_IN_ONE_CHAR(MAX_PROXIMITY_CHARS * sizeof(*mInputCodes)),
     MAX_UMLAUT_SEARCH_DEPTH(DEFAULT_MAX_UMLAUT_SEARCH_DEPTH) {
-    if (DEBUG_DICT) LOGI("UnigramDictionary - constructor");
+    if (DEBUG_DICT) {
+        LOGI("UnigramDictionary - constructor");
+    }
 }
 
 UnigramDictionary::~UnigramDictionary() {}
@@ -183,7 +185,9 @@
     // Suggestion with missing character
     if (SUGGEST_WORDS_WITH_MISSING_CHARACTER) {
         for (int i = 0; i < codesSize; ++i) {
-            if (DEBUG_DICT) LOGI("--- Suggest missing characters %d", i);
+            if (DEBUG_DICT) {
+                LOGI("--- Suggest missing characters %d", i);
+            }
             getSuggestionCandidates(i, -1, -1, NULL, 0, MAX_DEPTH);
         }
     }
@@ -194,7 +198,9 @@
     if (SUGGEST_WORDS_WITH_EXCESSIVE_CHARACTER
             && mInputLength >= MIN_USER_TYPED_LENGTH_FOR_EXCESSIVE_CHARACTER_SUGGESTION) {
         for (int i = 0; i < codesSize; ++i) {
-            if (DEBUG_DICT) LOGI("--- Suggest excessive characters %d", i);
+            if (DEBUG_DICT) {
+                LOGI("--- Suggest excessive characters %d", i);
+            }
             getSuggestionCandidates(-1, i, -1, NULL, 0, MAX_DEPTH);
         }
     }
@@ -205,7 +211,9 @@
     // Only suggest words that length is mInputLength
     if (SUGGEST_WORDS_WITH_TRANSPOSED_CHARACTERS) {
         for (int i = 0; i < codesSize; ++i) {
-            if (DEBUG_DICT) LOGI("--- Suggest transposed characters %d", i);
+            if (DEBUG_DICT) {
+                LOGI("--- Suggest transposed characters %d", i);
+            }
             getSuggestionCandidates(-1, -1, i, NULL, 0, mInputLength - 1);
         }
     }
@@ -216,22 +224,27 @@
     if (SUGGEST_WORDS_WITH_MISSING_SPACE_CHARACTER
             && mInputLength >= MIN_USER_TYPED_LENGTH_FOR_MISSING_SPACE_SUGGESTION) {
         for (int i = 1; i < codesSize; ++i) {
-            if (DEBUG_DICT) LOGI("--- Suggest missing space characters %d", i);
+            if (DEBUG_DICT) {
+                LOGI("--- Suggest missing space characters %d", i);
+            }
             getMissingSpaceWords(mInputLength, i);
         }
     }
     PROF_END(5);
 
     PROF_START(6);
-    if (SUGGEST_WORDS_WITH_SPACE_PROXIMITY) {
+    if (SUGGEST_WORDS_WITH_SPACE_PROXIMITY && proximityInfo) {
         // The first and last "mistyped spaces" are taken care of by excessive character handling
         for (int i = 1; i < codesSize - 1; ++i) {
-            if (DEBUG_DICT) LOGI("--- Suggest words with proximity space %d", i);
+            if (DEBUG_DICT) {
+                LOGI("--- Suggest words with proximity space %d", i);
+            }
             const int x = xcoordinates[i];
             const int y = ycoordinates[i];
-            if (DEBUG_PROXIMITY_INFO)
+            if (DEBUG_PROXIMITY_INFO) {
                 LOGI("Input[%d] x = %d, y = %d, has space proximity = %d",
                         i, x, y, proximityInfo->hasSpaceProximity(x, y));
+            }
             if (proximityInfo->hasSpaceProximity(x, y)) {
                 getMistypedSpaceWords(mInputLength, i);
             }
@@ -242,7 +255,9 @@
 
 void UnigramDictionary::initSuggestions(const int *codes, const int codesSize,
         unsigned short *outWords, int *frequencies) {
-    if (DEBUG_DICT) LOGI("initSuggest");
+    if (DEBUG_DICT) {
+        LOGI("initSuggest");
+    }
     mFrequencies = frequencies;
     mOutputChars = outWords;
     mInputCodes = codes;
@@ -250,14 +265,14 @@
     mMaxEditDistance = mInputLength < 5 ? 2 : mInputLength / 2;
 }
 
-void UnigramDictionary::registerNextLetter(
-        unsigned short c, int *nextLetters, int nextLettersSize) {
+static inline void registerNextLetter(unsigned short c, int *nextLetters, int nextLettersSize) {
     if (c < nextLettersSize) {
         nextLetters[c]++;
     }
 }
 
 // TODO: We need to optimize addWord by using STL or something
+// TODO: This needs to take an const unsigned short* and not tinker with its contents
 bool UnigramDictionary::addWord(unsigned short *word, int length, int frequency) {
     word[length] = 0;
     if (DEBUG_DICT && DEBUG_SHOW_FOUND_WORD) {
@@ -266,15 +281,17 @@
         LOGI("Found word = %s, freq = %d", s, frequency);
     }
     if (length > MAX_WORD_LENGTH) {
-        if (DEBUG_DICT) LOGI("Exceeded max word length.");
+        if (DEBUG_DICT) {
+            LOGI("Exceeded max word length.");
+        }
         return false;
     }
 
     // Find the right insertion point
     int insertAt = 0;
     while (insertAt < MAX_WORDS) {
-        if (frequency > mFrequencies[insertAt] || (mFrequencies[insertAt] == frequency
-                && length < Dictionary::wideStrLen(mOutputChars + insertAt * MAX_WORD_LENGTH))) {
+        // TODO: How should we sort words with the same frequency?
+        if (frequency > mFrequencies[insertAt]) {
             break;
         }
         insertAt++;
@@ -283,7 +300,7 @@
         if (DEBUG_DICT) {
             char s[length + 1];
             for (int i = 0; i <= length; i++) s[i] = word[i];
-            LOGI("Added word = %s, freq = %d", s, frequency);
+            LOGI("Added word = %s, freq = %d, %d", s, frequency, S_INT_MAX);
         }
         memmove((char*) mFrequencies + (insertAt + 1) * sizeof(mFrequencies[0]),
                (char*) mFrequencies + insertAt * sizeof(mFrequencies[0]),
@@ -297,13 +314,25 @@
             *dest++ = *word++;
         }
         *dest = 0; // NULL terminate
-        if (DEBUG_DICT) LOGI("Added word at %d", insertAt);
+        if (DEBUG_DICT) {
+            LOGI("Added word at %d", insertAt);
+        }
         return true;
     }
     return false;
 }
 
-unsigned short UnigramDictionary::toBaseLowerCase(unsigned short c) {
+inline void UnigramDictionary::addWordAlternatesSpellings(const uint8_t* const root, int pos,
+        int depth, int finalFreq) {
+    // TODO: actually add alternates when the format supports it.
+}
+
+static inline bool hasAlternateSpellings(uint8_t flags) {
+    // TODO: when the format supports it, return the actual value.
+    return false;
+}
+
+static inline unsigned short toBaseLowerCase(unsigned short c) {
     if (c < sizeof(BASE_CHARS) / sizeof(BASE_CHARS[0])) {
         c = BASE_CHARS[c];
     }
@@ -315,7 +344,7 @@
     return c;
 }
 
-bool UnigramDictionary::sameAsTyped(unsigned short *word, int length) {
+bool UnigramDictionary::sameAsTyped(const unsigned short *word, int length) const {
     if (length != mInputLength) {
         return false;
     }
@@ -344,7 +373,7 @@
     }
     int rootPosition = ROOT_POS;
     // Get the number of child of root, then increment the position
-    int childCount = Dictionary::getCount(DICT, &rootPosition);
+    int childCount = Dictionary::getCount(DICT_ROOT, &rootPosition);
     int depth = 0;
 
     mStackChildCount[0] = childCount;
@@ -353,6 +382,7 @@
     mStackInputIndex[0] = 0;
     mStackDiffs[0] = 0;
     mStackSiblingPos[0] = rootPosition;
+    mStackOutputIndex[0] = 0;
 
     // Depth first search
     while (depth >= 0) {
@@ -363,14 +393,15 @@
             int inputIndex = mStackInputIndex[depth];
             int diffs = mStackDiffs[depth];
             int siblingPos = mStackSiblingPos[depth];
+            int outputIndex = mStackOutputIndex[depth];
             int firstChildPos;
             // depth will never be greater than maxDepth because in that case,
             // needsToTraverseChildrenNodes should be false
-            const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos, depth,
+            const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos, outputIndex,
                     maxDepth, traverseAllNodes, matchWeight, inputIndex, diffs, skipPos,
                     excessivePos, transposedPos, nextLetters, nextLettersSize, &childCount,
                     &firstChildPos, &traverseAllNodes, &matchWeight, &inputIndex, &diffs,
-                    &siblingPos);
+                    &siblingPos, &outputIndex);
             // Update next sibling pos
             mStackSiblingPos[depth] = siblingPos;
             if (needsToTraverseChildrenNodes) {
@@ -382,6 +413,7 @@
                 mStackInputIndex[depth] = inputIndex;
                 mStackDiffs[depth] = diffs;
                 mStackSiblingPos[depth] = firstChildPos;
+                mStackOutputIndex[depth] = outputIndex;
             }
         } else {
             // Goes to parent sibling node
@@ -390,114 +422,128 @@
     }
 }
 
-inline static void multiplyRate(const int rate, int *freq) {
-    if (rate > 1000000) {
-        *freq = (*freq / 100) * rate;
-    } else {
-        *freq = *freq * rate / 100;
+static const int TWO_31ST_DIV_255 = S_INT_MAX / 255;
+static inline int capped255MultForFullMatchAccentsOrCapitalizationDifference(const int num) {
+    return (num < TWO_31ST_DIV_255 ? 255 * num : S_INT_MAX);
+}
+
+static const int TWO_31ST_DIV_2 = S_INT_MAX / 2;
+inline static void multiplyIntCapped(const int multiplier, int *base) {
+    const int temp = *base;
+    if (temp != S_INT_MAX) {
+        // Branch if multiplier == 2 for the optimization
+        if (multiplier == 2) {
+            *base = TWO_31ST_DIV_2 >= temp ? temp << 1 : S_INT_MAX;
+        } else {
+            const int tempRetval = temp * multiplier;
+            *base = tempRetval >= temp ? tempRetval : S_INT_MAX;
+        }
     }
 }
 
-bool UnigramDictionary::getSplitTwoWordsSuggestion(const int inputLength,
-        const int firstWordStartPos, const int firstWordLength, const int secondWordStartPos,
-        const int secondWordLength) {
-    if (inputLength >= MAX_WORD_LENGTH) return false;
-    if (0 >= firstWordLength || 0 >= secondWordLength || firstWordStartPos >= secondWordStartPos
-            || firstWordStartPos < 0 || secondWordStartPos + secondWordLength > inputLength)
-        return false;
-    const int newWordLength = firstWordLength + secondWordLength + 1;
-    // Allocating variable length array on stack
-    unsigned short word[newWordLength];
-    const int firstFreq = getBestWordFreq(firstWordStartPos, firstWordLength, mWord);
-    if (DEBUG_DICT) LOGI("First freq: %d", firstFreq);
-    if (firstFreq <= 0) return false;
+inline static int powerIntCapped(const int base, const int n) {
+    if (base == 2) {
+        return n < 31 ? 1 << n : S_INT_MAX;
+    } else {
+        int ret = base;
+        for (int i = 1; i < n; ++i) multiplyIntCapped(base, &ret);
+        return ret;
+    }
+}
 
-    for (int i = 0; i < firstWordLength; ++i) {
-        word[i] = mWord[i];
+inline static void multiplyRate(const int rate, int *freq) {
+    if (*freq != S_INT_MAX) {
+        if (*freq > 1000000) {
+            *freq /= 100;
+            multiplyIntCapped(rate, freq);
+        } else {
+            multiplyIntCapped(rate, freq);
+            *freq /= 100;
+        }
+    }
+}
+
+inline static int calcFreqForSplitTwoWords(
+        const int typedLetterMultiplier, const int firstWordLength, const int secondWordLength,
+        const int firstFreq, const int secondFreq, const bool isSpaceProximity) {
+    if (firstWordLength == 0 || secondWordLength == 0) {
+        return 0;
+    }
+    const int firstDemotionRate = 100 - 100 / (firstWordLength + 1);
+    int tempFirstFreq = firstFreq;
+    multiplyRate(firstDemotionRate, &tempFirstFreq);
+
+    const int secondDemotionRate = 100 - 100 / (secondWordLength + 1);
+    int tempSecondFreq = secondFreq;
+    multiplyRate(secondDemotionRate, &tempSecondFreq);
+
+    const int totalLength = firstWordLength + secondWordLength;
+
+    // Promote pairFreq with multiplying by 2, because the word length is the same as the typed
+    // length.
+    int totalFreq = tempFirstFreq + tempSecondFreq;
+
+    // This is a workaround to try offsetting the not-enough-demotion which will be done in
+    // calcNormalizedScore in Utils.java.
+    // In calcNormalizedScore the score will be demoted by (1 - 1 / length)
+    // but we demoted only (1 - 1 / (length + 1)) so we will additionally adjust freq by
+    // (1 - 1 / length) / (1 - 1 / (length + 1)) = (1 - 1 / (length * length))
+    const int normalizedScoreNotEnoughDemotionAdjustment = 100 - 100 / (totalLength * totalLength);
+    multiplyRate(normalizedScoreNotEnoughDemotionAdjustment, &totalFreq);
+
+    // At this moment, totalFreq is calculated by the following formula:
+    // (firstFreq * (1 - 1 / (firstWordLength + 1)) + secondFreq * (1 - 1 / (secondWordLength + 1)))
+    //        * (1 - 1 / totalLength) / (1 - 1 / (totalLength + 1))
+
+    multiplyIntCapped(powerIntCapped(typedLetterMultiplier, totalLength), &totalFreq);
+
+    // This is another workaround to offset the demotion which will be done in
+    // calcNormalizedScore in Utils.java.
+    // In calcNormalizedScore the score will be demoted by (1 - 1 / length) so we have to promote
+    // the same amount because we already have adjusted the synthetic freq of this "missing or
+    // mistyped space" suggestion candidate above in this method.
+    const int normalizedScoreDemotionRateOffset = (100 + 100 / totalLength);
+    multiplyRate(normalizedScoreDemotionRateOffset, &totalFreq);
+
+    if (isSpaceProximity) {
+        // A word pair with one space proximity correction
+        if (DEBUG_DICT) {
+            LOGI("Found a word pair with space proximity correction.");
+        }
+        multiplyIntCapped(typedLetterMultiplier, &totalFreq);
+        multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &totalFreq);
     }
 
-    const int secondFreq = getBestWordFreq(secondWordStartPos, secondWordLength, mWord);
-    if (DEBUG_DICT) LOGI("Second  freq:  %d", secondFreq);
-    if (secondFreq <= 0) return false;
-
-    word[firstWordLength] = SPACE;
-    for (int i = (firstWordLength + 1); i < newWordLength; ++i) {
-        word[i] = mWord[i - firstWordLength - 1];
-    }
-
-    int pairFreq = ((firstFreq + secondFreq) / 2);
-    for (int i = 0; i < inputLength; ++i) pairFreq *= TYPED_LETTER_MULTIPLIER;
-    multiplyRate(WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE, &pairFreq);
-    addWord(word, newWordLength, pairFreq);
-    return true;
+    multiplyRate(WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE, &totalFreq);
+    return totalFreq;
 }
 
 bool UnigramDictionary::getMissingSpaceWords(const int inputLength, const int missingSpacePos) {
     return getSplitTwoWordsSuggestion(
-            inputLength, 0, missingSpacePos, missingSpacePos, inputLength - missingSpacePos);
+            inputLength, 0, missingSpacePos, missingSpacePos, inputLength - missingSpacePos, false);
 }
 
 bool UnigramDictionary::getMistypedSpaceWords(const int inputLength, const int spaceProximityPos) {
     return getSplitTwoWordsSuggestion(
             inputLength, 0, spaceProximityPos, spaceProximityPos + 1,
-            inputLength - spaceProximityPos - 1);
+            inputLength - spaceProximityPos - 1, true);
 }
 
-// Keep this for comparing spec to new getWords
-void UnigramDictionary::getWordsOld(const int initialPos, const int inputLength, const int skipPos,
-        const int excessivePos, const int transposedPos,int *nextLetters,
-        const int nextLettersSize) {
-    int initialPosition = initialPos;
-    const int count = Dictionary::getCount(DICT, &initialPosition);
-    getWordsRec(count, initialPosition, 0,
-            min(inputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH),
-            mInputLength <= 0, 1, 0, 0, skipPos, excessivePos, transposedPos, nextLetters,
-            nextLettersSize);
-}
-
-void UnigramDictionary::getWordsRec(const int childrenCount, const int pos, const int depth,
-        const int maxDepth, const bool traverseAllNodes, const int matchWeight,
-        const int inputIndex, const int diffs, const int skipPos, const int excessivePos,
-        const int transposedPos, int *nextLetters, const int nextLettersSize) {
-    int siblingPos = pos;
-    for (int i = 0; i < childrenCount; ++i) {
-        int newCount;
-        int newChildPosition;
-        const int newDepth = depth + 1;
-        bool newTraverseAllNodes;
-        int newMatchRate;
-        int newInputIndex;
-        int newDiffs;
-        int newSiblingPos;
-        const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos, depth, maxDepth,
-                traverseAllNodes, matchWeight, inputIndex, diffs,
-                skipPos, excessivePos, transposedPos,
-                nextLetters, nextLettersSize,
-                &newCount, &newChildPosition, &newTraverseAllNodes, &newMatchRate,
-                &newInputIndex, &newDiffs, &newSiblingPos);
-        siblingPos = newSiblingPos;
-
-        if (needsToTraverseChildrenNodes) {
-            getWordsRec(newCount, newChildPosition, newDepth, maxDepth, newTraverseAllNodes,
-                    newMatchRate, newInputIndex, newDiffs, skipPos, excessivePos, transposedPos,
-                    nextLetters, nextLettersSize);
-        }
-    }
-}
-
-static const int TWO_31ST_DIV_255 = S_INT_MAX / 255;
-static inline int capped255MultForFullMatchAccentsOrCapitalizationDifference(const int num) {
-    return (num < TWO_31ST_DIV_255 ? 255 * num : S_INT_MAX);
-}
 inline int UnigramDictionary::calculateFinalFreq(const int inputIndex, const int depth,
         const int matchWeight, const int skipPos, const int excessivePos, const int transposedPos,
         const int freq, const bool sameLength) const {
     // TODO: Demote by edit distance
     int finalFreq = freq * matchWeight;
     if (skipPos >= 0) {
-        if (mInputLength >= 3) {
-            multiplyRate(WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE *
-                    (mInputLength - 2) / (mInputLength - 1), &finalFreq);
+        if (mInputLength >= 2) {
+            const int demotionRate = WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE
+                    * (10 * mInputLength - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X)
+                    / (10 * mInputLength
+                            - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X + 10);
+            if (DEBUG_DICT_FULL) {
+                LOGI("Demotion rate for missing character is %d.", demotionRate);
+            }
+            multiplyRate(demotionRate, &finalFreq);
         } else {
             finalFreq = 0;
         }
@@ -511,42 +557,33 @@
         }
     }
     int lengthFreq = TYPED_LETTER_MULTIPLIER;
-    for (int i = 0; i < depth; ++i) lengthFreq *= TYPED_LETTER_MULTIPLIER;
+    multiplyIntCapped(powerIntCapped(TYPED_LETTER_MULTIPLIER, depth), &lengthFreq);
     if (lengthFreq == matchWeight) {
+        // Full exact match
         if (depth > 1) {
-            if (DEBUG_DICT) LOGI("Found full matched word.");
+            if (DEBUG_DICT) {
+                LOGI("Found full matched word.");
+            }
             multiplyRate(FULL_MATCHED_WORDS_PROMOTION_RATE, &finalFreq);
         }
         if (sameLength && transposedPos < 0 && skipPos < 0 && excessivePos < 0) {
             finalFreq = capped255MultForFullMatchAccentsOrCapitalizationDifference(finalFreq);
         }
+    } else if (sameLength && transposedPos < 0 && skipPos < 0 && excessivePos < 0 && depth > 0) {
+        // A word with proximity corrections
+        if (DEBUG_DICT) {
+            LOGI("Found one proximity correction.");
+        }
+        multiplyIntCapped(TYPED_LETTER_MULTIPLIER, &finalFreq);
+        multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
     }
-    if (sameLength) finalFreq *= FULL_WORD_MULTIPLIER;
+    if (DEBUG_DICT) {
+        LOGI("calc: %d, %d", depth, sameLength);
+    }
+    if (sameLength) multiplyIntCapped(FULL_WORD_MULTIPLIER, &finalFreq);
     return finalFreq;
 }
 
-inline void UnigramDictionary::onTerminalWhenUserTypedLengthIsGreaterThanInputLength(
-        unsigned short *word, const int inputIndex, const int depth, const int matchWeight,
-        int *nextLetters, const int nextLettersSize, const int skipPos, const int excessivePos,
-        const int transposedPos, const int freq) {
-    const int finalFreq = calculateFinalFreq(inputIndex, depth, matchWeight, skipPos, excessivePos,
-            transposedPos, freq, false);
-    if (depth >= MIN_SUGGEST_DEPTH) addWord(word, depth + 1, finalFreq);
-    if (depth >= mInputLength && skipPos < 0) {
-        registerNextLetter(mWord[mInputLength], nextLetters, nextLettersSize);
-    }
-}
-
-inline void UnigramDictionary::onTerminalWhenUserTypedLengthIsSameAsInputLength(
-        unsigned short *word, const int inputIndex, const int depth, const int matchWeight,
-        const int skipPos, const int excessivePos, const int transposedPos, const int freq) {
-    if (sameAsTyped(word, depth + 1)) return;
-    const int finalFreq = calculateFinalFreq(inputIndex, depth, matchWeight, skipPos,
-            excessivePos, transposedPos, freq, true);
-    // Proximity collection will promote a word of the same length as what user typed.
-    if (depth >= MIN_SUGGEST_DEPTH) addWord(word, depth + 1, finalFreq);
-}
-
 inline bool UnigramDictionary::needsToSkipCurrentNode(const unsigned short c,
         const int inputIndex, const int skipPos, const int depth) {
     const unsigned short userTypedChar = getInputCharsAt(inputIndex)[0];
@@ -577,7 +614,6 @@
     return false;
 }
 
-
 // In the following function, c is the current character of the dictionary word
 // currently examined.
 // currentChars is an array containing the keys close to the character the
@@ -620,96 +656,79 @@
     return UNRELATED_CHAR;
 }
 
-inline bool UnigramDictionary::processCurrentNode(const int pos, const int depth,
-        const int maxDepth, const bool traverseAllNodes, int matchWeight, int inputIndex,
-        const int diffs, const int skipPos, const int excessivePos, const int transposedPos,
-        int *nextLetters, const int nextLettersSize, int *newCount, int *newChildPosition,
-        bool *newTraverseAllNodes, int *newMatchRate, int *newInputIndex, int *newDiffs,
-        int *nextSiblingPosition) {
-    if (DEBUG_DICT) {
-        int inputCount = 0;
-        if (skipPos >= 0) ++inputCount;
-        if (excessivePos >= 0) ++inputCount;
-        if (transposedPos >= 0) ++inputCount;
-        assert(inputCount <= 1);
-    }
-    unsigned short c;
-    int childPosition;
-    bool terminal;
-    int freq;
-    bool isSameAsUserTypedLength = false;
+inline void UnigramDictionary::onTerminal(unsigned short int* word, const int depth,
+        const uint8_t* const root, const uint8_t flags, int pos,
+        const int inputIndex, const int matchWeight, const int skipPos,
+        const int excessivePos, const int transposedPos, const int freq, const bool sameLength,
+        int* nextLetters, const int nextLettersSize) {
 
-    if (excessivePos == depth && inputIndex < mInputLength - 1) ++inputIndex;
+    const bool isSameAsTyped = sameLength ? sameAsTyped(word, depth + 1) : false;
+    const bool hasAlternates = hasAlternateSpellings(flags);
+    if (isSameAsTyped && !hasAlternates) return;
 
-    *nextSiblingPosition = Dictionary::setDictionaryValues(DICT, IS_LATEST_DICT_VERSION, pos, &c,
-            &childPosition, &terminal, &freq);
-
-    const bool needsToTraverseChildrenNodes = childPosition != 0;
-
-    // If we are only doing traverseAllNodes, no need to look at the typed characters.
-    if (traverseAllNodes || needsToSkipCurrentNode(c, inputIndex, skipPos, depth)) {
-        mWord[depth] = c;
-        if (traverseAllNodes && terminal) {
-            onTerminalWhenUserTypedLengthIsGreaterThanInputLength(mWord, inputIndex, depth,
-                    matchWeight, nextLetters, nextLettersSize, skipPos, excessivePos, transposedPos,
-                    freq);
-        }
-        if (!needsToTraverseChildrenNodes) return false;
-        *newTraverseAllNodes = traverseAllNodes;
-        *newMatchRate = matchWeight;
-        *newDiffs = diffs;
-        *newInputIndex = inputIndex;
-    } else {
-        const int *currentChars = getInputCharsAt(inputIndex);
-
-        if (transposedPos >= 0) {
-            if (inputIndex == transposedPos) currentChars += MAX_PROXIMITY_CHARS;
-            if (inputIndex == (transposedPos + 1)) currentChars -= MAX_PROXIMITY_CHARS;
-        }
-
-        int matchedProximityCharId = getMatchedProximityId(currentChars, c, skipPos, excessivePos,
-                transposedPos);
-        if (UNRELATED_CHAR == matchedProximityCharId) return false;
-        mWord[depth] = c;
-        // If inputIndex is greater than mInputLength, that means there is no
-        // proximity chars. So, we don't need to check proximity.
-        if (SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR == matchedProximityCharId) {
-            matchWeight = matchWeight * TYPED_LETTER_MULTIPLIER;
-        }
-        bool isSameAsUserTypedLength = mInputLength == inputIndex + 1
-                || (excessivePos == mInputLength - 1 && inputIndex == mInputLength - 2);
-        if (isSameAsUserTypedLength && terminal) {
-            onTerminalWhenUserTypedLengthIsSameAsInputLength(mWord, inputIndex, depth, matchWeight,
-                    skipPos, excessivePos, transposedPos, freq);
-        }
-        if (!needsToTraverseChildrenNodes) return false;
-        // Start traversing all nodes after the index exceeds the user typed length
-        *newTraverseAllNodes = isSameAsUserTypedLength;
-        *newMatchRate = matchWeight;
-        *newDiffs = diffs + ((NEAR_PROXIMITY_CHAR == matchedProximityCharId) ? 1 : 0);
-        *newInputIndex = inputIndex + 1;
-    }
-    // Optimization: Prune out words that are too long compared to how much was typed.
-    if (depth >= maxDepth || *newDiffs > mMaxEditDistance) {
-        return false;
+    if (depth >= MIN_SUGGEST_DEPTH) {
+        const int finalFreq = calculateFinalFreq(inputIndex, depth, matchWeight, skipPos,
+                excessivePos, transposedPos, freq, sameLength);
+        if (!isSameAsTyped)
+            addWord(word, depth + 1, finalFreq);
+        if (hasAlternates)
+            addWordAlternatesSpellings(DICT_ROOT, pos, flags, finalFreq);
     }
 
-    // If inputIndex is greater than mInputLength, that means there are no proximity chars.
-    // TODO: Check if this can be isSameAsUserTypedLength only.
-    if (isSameAsUserTypedLength || mInputLength <= *newInputIndex) {
-        *newTraverseAllNodes = true;
+    if (sameLength && depth >= mInputLength && skipPos < 0) {
+        registerNextLetter(word[mInputLength], nextLetters, nextLettersSize);
     }
-    // get the count of nodes and increment childAddress.
-    *newCount = Dictionary::getCount(DICT, &childPosition);
-    *newChildPosition = childPosition;
-    if (DEBUG_DICT) assert(needsToTraverseChildrenNodes);
-    return needsToTraverseChildrenNodes;
+}
+
+#ifndef NEW_DICTIONARY_FORMAT
+// TODO: Don't forget to bring inline functions back to over where they are used.
+
+// The following functions will be entirely replaced with new implementations.
+void UnigramDictionary::getWordsOld(const int initialPos, const int inputLength, const int skipPos,
+        const int excessivePos, const int transposedPos,int *nextLetters,
+        const int nextLettersSize) {
+    int initialPosition = initialPos;
+    const int count = Dictionary::getCount(DICT_ROOT, &initialPosition);
+    getWordsRec(count, initialPosition, 0,
+            min(inputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH),
+            mInputLength <= 0, 1, 0, 0, skipPos, excessivePos, transposedPos, nextLetters,
+            nextLettersSize);
+}
+
+void UnigramDictionary::getWordsRec(const int childrenCount, const int pos, const int depth,
+        const int maxDepth, const bool traverseAllNodes, const int matchWeight,
+        const int inputIndex, const int diffs, const int skipPos, const int excessivePos,
+        const int transposedPos, int *nextLetters, const int nextLettersSize) {
+    int siblingPos = pos;
+    for (int i = 0; i < childrenCount; ++i) {
+        int newCount;
+        int newChildPosition;
+        bool newTraverseAllNodes;
+        int newMatchRate;
+        int newInputIndex;
+        int newDiffs;
+        int newSiblingPos;
+        int newOutputIndex;
+        const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos, depth, maxDepth,
+                traverseAllNodes, matchWeight, inputIndex, diffs,
+                skipPos, excessivePos, transposedPos,
+                nextLetters, nextLettersSize,
+                &newCount, &newChildPosition, &newTraverseAllNodes, &newMatchRate,
+                &newInputIndex, &newDiffs, &newSiblingPos, &newOutputIndex);
+        siblingPos = newSiblingPos;
+
+        if (needsToTraverseChildrenNodes) {
+            getWordsRec(newCount, newChildPosition, newOutputIndex, maxDepth, newTraverseAllNodes,
+                    newMatchRate, newInputIndex, newDiffs, skipPos, excessivePos, transposedPos,
+                    nextLetters, nextLettersSize);
+        }
+    }
 }
 
 inline int UnigramDictionary::getBestWordFreq(const int startInputIndex, const int inputLength,
         unsigned short *word) {
     int pos = ROOT_POS;
-    int count = Dictionary::getCount(DICT, &pos);
+    int count = Dictionary::getCount(DICT_ROOT, &pos);
     int maxFreq = 0;
     int depth = 0;
     unsigned short newWord[MAX_WORD_LENGTH_INTERNAL];
@@ -765,10 +784,12 @@
     const int inputIndex = startInputIndex + depth;
     const int *currentChars = getInputCharsAt(inputIndex);
     unsigned short c;
-    *siblingPos = Dictionary::setDictionaryValues(DICT, IS_LATEST_DICT_VERSION, firstChildPos, &c,
-            newChildPosition, newTerminal, newFreq);
+    *siblingPos = Dictionary::setDictionaryValues(DICT_ROOT, IS_LATEST_DICT_VERSION, firstChildPos,
+            &c, newChildPosition, newTerminal, newFreq);
     const unsigned int inputC = currentChars[0];
-    if (DEBUG_DICT) assert(inputC <= U_SHORT_MAX);
+    if (DEBUG_DICT) {
+        assert(inputC <= U_SHORT_MAX);
+    }
     const unsigned short baseLowerC = toBaseLowerCase(c);
     const bool matched = (inputC == baseLowerC || inputC == c);
     const bool hasChild = *newChildPosition != 0;
@@ -776,10 +797,12 @@
         word[depth] = c;
         if (DEBUG_DICT && DEBUG_NODE) {
             LOGI("Node(%c, %c)<%d>, %d, %d", inputC, c, matched, hasChild, *newFreq);
-            if (*newTerminal) LOGI("Terminal %d", *newFreq);
+            if (*newTerminal) {
+                LOGI("Terminal %d", *newFreq);
+            }
         }
         if (hasChild) {
-            *newCount = Dictionary::getCount(DICT, newChildPosition);
+            *newCount = Dictionary::getCount(DICT_ROOT, newChildPosition);
             return true;
         } else {
             return false;
@@ -791,4 +814,314 @@
         return false;
     }
 }
+
+// TODO: use uint32_t instead of unsigned short
+bool UnigramDictionary::isValidWord(unsigned short *word, int length) {
+    if (IS_LATEST_DICT_VERSION) {
+        return (getBigramPosition(DICTIONARY_HEADER_SIZE, word, 0, length) != NOT_VALID_WORD);
+    } else {
+        return (getBigramPosition(0, word, 0, length) != NOT_VALID_WORD);
+    }
+}
+
+
+// Require strict exact match.
+int UnigramDictionary::getBigramPosition(int pos, unsigned short *word, int offset,
+        int length) const {
+    // returns address of bigram data of that word
+    // return -99 if not found
+
+    int count = Dictionary::getCount(DICT_ROOT, &pos);
+    unsigned short currentChar = (unsigned short) word[offset];
+    for (int j = 0; j < count; j++) {
+        unsigned short c = Dictionary::getChar(DICT_ROOT, &pos);
+        int terminal = Dictionary::getTerminal(DICT_ROOT, &pos);
+        int childPos = Dictionary::getAddress(DICT_ROOT, &pos);
+        if (c == currentChar) {
+            if (offset == length - 1) {
+                if (terminal) {
+                    return (pos+1);
+                }
+            } else {
+                if (childPos != 0) {
+                    int t = getBigramPosition(childPos, word, offset + 1, length);
+                    if (t > 0) {
+                        return t;
+                    }
+                }
+            }
+        }
+        if (terminal) {
+            Dictionary::getFreq(DICT_ROOT, IS_LATEST_DICT_VERSION, &pos);
+        }
+        // There could be two instances of each alphabet - upper and lower case. So continue
+        // looking ...
+    }
+    return NOT_VALID_WORD;
+}
+
+
+// The following functions will be modified.
+bool UnigramDictionary::getSplitTwoWordsSuggestion(const int inputLength,
+        const int firstWordStartPos, const int firstWordLength, const int secondWordStartPos,
+        const int secondWordLength, const bool isSpaceProximity) {
+    if (inputLength >= MAX_WORD_LENGTH) return false;
+    if (0 >= firstWordLength || 0 >= secondWordLength || firstWordStartPos >= secondWordStartPos
+            || firstWordStartPos < 0 || secondWordStartPos + secondWordLength > inputLength)
+        return false;
+    const int newWordLength = firstWordLength + secondWordLength + 1;
+    // Allocating variable length array on stack
+    unsigned short word[newWordLength];
+    const int firstFreq = getBestWordFreq(firstWordStartPos, firstWordLength, mWord);
+    if (DEBUG_DICT) {
+        LOGI("First freq: %d", firstFreq);
+    }
+    if (firstFreq <= 0) return false;
+
+    for (int i = 0; i < firstWordLength; ++i) {
+        word[i] = mWord[i];
+    }
+
+    const int secondFreq = getBestWordFreq(secondWordStartPos, secondWordLength, mWord);
+    if (DEBUG_DICT) {
+        LOGI("Second  freq:  %d", secondFreq);
+    }
+    if (secondFreq <= 0) return false;
+
+    word[firstWordLength] = SPACE;
+    for (int i = (firstWordLength + 1); i < newWordLength; ++i) {
+        word[i] = mWord[i - firstWordLength - 1];
+    }
+
+    int pairFreq = calcFreqForSplitTwoWords(TYPED_LETTER_MULTIPLIER, firstWordLength,
+            secondWordLength, firstFreq, secondFreq, isSpaceProximity);
+    if (DEBUG_DICT) {
+        LOGI("Split two words:  %d, %d, %d, %d, %d", firstFreq, secondFreq, pairFreq, inputLength,
+                TYPED_LETTER_MULTIPLIER);
+    }
+    addWord(word, newWordLength, pairFreq);
+    return true;
+}
+
+inline bool UnigramDictionary::processCurrentNode(const int pos, const int depth,
+        const int maxDepth, const bool traverseAllNodes, int matchWeight, int inputIndex,
+        const int diffs, const int skipPos, const int excessivePos, const int transposedPos,
+        int *nextLetters, const int nextLettersSize, int *newCount, int *newChildPosition,
+        bool *newTraverseAllNodes, int *newMatchRate, int *newInputIndex, int *newDiffs,
+        int *nextSiblingPosition, int *nextOutputIndex) {
+    if (DEBUG_DICT) {
+        int inputCount = 0;
+        if (skipPos >= 0) ++inputCount;
+        if (excessivePos >= 0) ++inputCount;
+        if (transposedPos >= 0) ++inputCount;
+        assert(inputCount <= 1);
+    }
+    unsigned short c;
+    int childPosition;
+    bool terminal;
+    int freq;
+    bool isSameAsUserTypedLength = false;
+
+    const uint8_t flags = 0; // No flags for now
+
+    if (excessivePos == depth && inputIndex < mInputLength - 1) ++inputIndex;
+
+    *nextSiblingPosition = Dictionary::setDictionaryValues(DICT_ROOT, IS_LATEST_DICT_VERSION, pos,
+            &c, &childPosition, &terminal, &freq);
+    *nextOutputIndex = depth + 1;
+
+    const bool needsToTraverseChildrenNodes = childPosition != 0;
+
+    // If we are only doing traverseAllNodes, no need to look at the typed characters.
+    if (traverseAllNodes || needsToSkipCurrentNode(c, inputIndex, skipPos, depth)) {
+        mWord[depth] = c;
+        if (traverseAllNodes && terminal) {
+            onTerminal(mWord, depth, DICT_ROOT, flags, pos, inputIndex, matchWeight, skipPos,
+                       excessivePos, transposedPos, freq, false, nextLetters, nextLettersSize);
+        }
+        if (!needsToTraverseChildrenNodes) return false;
+        *newTraverseAllNodes = traverseAllNodes;
+        *newMatchRate = matchWeight;
+        *newDiffs = diffs;
+        *newInputIndex = inputIndex;
+    } else {
+        const int *currentChars = getInputCharsAt(inputIndex);
+
+        if (transposedPos >= 0) {
+            if (inputIndex == transposedPos) currentChars += MAX_PROXIMITY_CHARS;
+            if (inputIndex == (transposedPos + 1)) currentChars -= MAX_PROXIMITY_CHARS;
+        }
+
+        int matchedProximityCharId = getMatchedProximityId(currentChars, c, skipPos, excessivePos,
+                transposedPos);
+        if (UNRELATED_CHAR == matchedProximityCharId) return false;
+        mWord[depth] = c;
+        // If inputIndex is greater than mInputLength, that means there is no
+        // proximity chars. So, we don't need to check proximity.
+        if (SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR == matchedProximityCharId) {
+            multiplyIntCapped(TYPED_LETTER_MULTIPLIER, &matchWeight);
+        }
+        bool isSameAsUserTypedLength = mInputLength == inputIndex + 1
+                || (excessivePos == mInputLength - 1 && inputIndex == mInputLength - 2);
+        if (isSameAsUserTypedLength && terminal) {
+            onTerminal(mWord, depth, DICT_ROOT, flags, pos, inputIndex, matchWeight, skipPos,
+                    excessivePos, transposedPos, freq, true, nextLetters, nextLettersSize);
+        }
+        if (!needsToTraverseChildrenNodes) return false;
+        // Start traversing all nodes after the index exceeds the user typed length
+        *newTraverseAllNodes = isSameAsUserTypedLength;
+        *newMatchRate = matchWeight;
+        *newDiffs = diffs + ((NEAR_PROXIMITY_CHAR == matchedProximityCharId) ? 1 : 0);
+        *newInputIndex = inputIndex + 1;
+    }
+    // Optimization: Prune out words that are too long compared to how much was typed.
+    if (depth >= maxDepth || *newDiffs > mMaxEditDistance) {
+        return false;
+    }
+
+    // If inputIndex is greater than mInputLength, that means there are no proximity chars.
+    // TODO: Check if this can be isSameAsUserTypedLength only.
+    if (isSameAsUserTypedLength || mInputLength <= *newInputIndex) {
+        *newTraverseAllNodes = true;
+    }
+    // get the count of nodes and increment childAddress.
+    *newCount = Dictionary::getCount(DICT_ROOT, &childPosition);
+    *newChildPosition = childPosition;
+    if (DEBUG_DICT) assert(needsToTraverseChildrenNodes);
+    return needsToTraverseChildrenNodes;
+}
+
+#else // NEW_DICTIONARY_FORMAT
+
+bool UnigramDictionary::getSplitTwoWordsSuggestion(const int inputLength,
+        const int firstWordStartPos, const int firstWordLength, const int secondWordStartPos,
+        const int secondWordLength, const bool isSpaceProximity) {
+    if (inputLength >= MAX_WORD_LENGTH) return false;
+    if (0 >= firstWordLength || 0 >= secondWordLength || firstWordStartPos >= secondWordStartPos
+            || firstWordStartPos < 0 || secondWordStartPos + secondWordLength > inputLength)
+        return false;
+    const int newWordLength = firstWordLength + secondWordLength + 1;
+    // Allocating variable length array on stack
+    unsigned short word[newWordLength];
+    const int firstFreq = getBestWordFreq(firstWordStartPos, firstWordLength, mWord);
+    if (DEBUG_DICT) {
+        LOGI("First freq: %d", firstFreq);
+    }
+    if (firstFreq <= 0) return false;
+
+    for (int i = 0; i < firstWordLength; ++i) {
+        word[i] = mWord[i];
+    }
+
+    const int secondFreq = getBestWordFreq(secondWordStartPos, secondWordLength, mWord);
+    if (DEBUG_DICT) {
+        LOGI("Second  freq:  %d", secondFreq);
+    }
+    if (secondFreq <= 0) return false;
+
+    word[firstWordLength] = SPACE;
+    for (int i = (firstWordLength + 1); i < newWordLength; ++i) {
+        word[i] = mWord[i - firstWordLength - 1];
+    }
+
+    int pairFreq = calcFreqForSplitTwoWords(TYPED_LETTER_MULTIPLIER, firstWordLength,
+            secondWordLength, firstFreq, secondFreq, isSpaceProximity);
+    if (DEBUG_DICT) {
+        LOGI("Split two words:  %d, %d, %d, %d, %d", firstFreq, secondFreq, pairFreq, inputLength,
+                TYPED_LETTER_MULTIPLIER);
+    }
+    addWord(word, newWordLength, pairFreq);
+    return true;
+}
+
+inline bool UnigramDictionary::processCurrentNode(const int pos, const int depth,
+        const int maxDepth, const bool traverseAllNodes, int matchWeight, int inputIndex,
+        const int diffs, const int skipPos, const int excessivePos, const int transposedPos,
+        int *nextLetters, const int nextLettersSize, int *newCount, int *newChildPosition,
+        bool *newTraverseAllNodes, int *newMatchRate, int *newInputIndex, int *newDiffs,
+        int *nextSiblingPosition, int *nextOutputIndex) {
+    if (DEBUG_DICT) {
+        int inputCount = 0;
+        if (skipPos >= 0) ++inputCount;
+        if (excessivePos >= 0) ++inputCount;
+        if (transposedPos >= 0) ++inputCount;
+        assert(inputCount <= 1);
+    }
+    unsigned short c;
+    int childPosition;
+    bool terminal;
+    int freq;
+    bool isSameAsUserTypedLength = false;
+
+    const uint8_t flags = 0; // No flags for now
+
+    if (excessivePos == depth && inputIndex < mInputLength - 1) ++inputIndex;
+
+    *nextSiblingPosition = Dictionary::setDictionaryValues(DICT_ROOT, IS_LATEST_DICT_VERSION, pos,
+            &c, &childPosition, &terminal, &freq);
+    *nextOutputIndex = depth + 1;
+
+    const bool needsToTraverseChildrenNodes = childPosition != 0;
+
+    // If we are only doing traverseAllNodes, no need to look at the typed characters.
+    if (traverseAllNodes || needsToSkipCurrentNode(c, inputIndex, skipPos, depth)) {
+        mWord[depth] = c;
+        if (traverseAllNodes && terminal) {
+            onTerminal(mWord, depth, DICT_ROOT, flags, pos, inputIndex, matchWeight, skipPos,
+                       excessivePos, transposedPos, freq, false, nextLetters, nextLettersSize);
+        }
+        if (!needsToTraverseChildrenNodes) return false;
+        *newTraverseAllNodes = traverseAllNodes;
+        *newMatchRate = matchWeight;
+        *newDiffs = diffs;
+        *newInputIndex = inputIndex;
+    } else {
+        const int *currentChars = getInputCharsAt(inputIndex);
+
+        if (transposedPos >= 0) {
+            if (inputIndex == transposedPos) currentChars += MAX_PROXIMITY_CHARS;
+            if (inputIndex == (transposedPos + 1)) currentChars -= MAX_PROXIMITY_CHARS;
+        }
+
+        int matchedProximityCharId = getMatchedProximityId(currentChars, c, skipPos, excessivePos,
+                transposedPos);
+        if (UNRELATED_CHAR == matchedProximityCharId) return false;
+        mWord[depth] = c;
+        // If inputIndex is greater than mInputLength, that means there is no
+        // proximity chars. So, we don't need to check proximity.
+        if (SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR == matchedProximityCharId) {
+            multiplyIntCapped(TYPED_LETTER_MULTIPLIER, &matchWeight);
+        }
+        bool isSameAsUserTypedLength = mInputLength == inputIndex + 1
+                || (excessivePos == mInputLength - 1 && inputIndex == mInputLength - 2);
+        if (isSameAsUserTypedLength && terminal) {
+            onTerminal(mWord, depth, DICT_ROOT, flags, pos, inputIndex, matchWeight, skipPos,
+                    excessivePos, transposedPos, freq, true, nextLetters, nextLettersSize);
+        }
+        if (!needsToTraverseChildrenNodes) return false;
+        // Start traversing all nodes after the index exceeds the user typed length
+        *newTraverseAllNodes = isSameAsUserTypedLength;
+        *newMatchRate = matchWeight;
+        *newDiffs = diffs + ((NEAR_PROXIMITY_CHAR == matchedProximityCharId) ? 1 : 0);
+        *newInputIndex = inputIndex + 1;
+    }
+    // Optimization: Prune out words that are too long compared to how much was typed.
+    if (depth >= maxDepth || *newDiffs > mMaxEditDistance) {
+        return false;
+    }
+
+    // If inputIndex is greater than mInputLength, that means there are no proximity chars.
+    // TODO: Check if this can be isSameAsUserTypedLength only.
+    if (isSameAsUserTypedLength || mInputLength <= *newInputIndex) {
+        *newTraverseAllNodes = true;
+    }
+    // get the count of nodes and increment childAddress.
+    *newCount = Dictionary::getCount(DICT_ROOT, &childPosition);
+    *newChildPosition = childPosition;
+    if (DEBUG_DICT) assert(needsToTraverseChildrenNodes);
+    return needsToTraverseChildrenNodes;
+}
+
+#endif // NEW_DICTIONARY_FORMAT
+
 } // namespace latinime
diff --git a/native/src/unigram_dictionary.h b/native/src/unigram_dictionary.h
index 3d3007c..154ac9b 100644
--- a/native/src/unigram_dictionary.h
+++ b/native/src/unigram_dictionary.h
@@ -17,9 +17,14 @@
 #ifndef LATINIME_UNIGRAM_DICTIONARY_H
 #define LATINIME_UNIGRAM_DICTIONARY_H
 
+#include <stdint.h>
 #include "defines.h"
 #include "proximity_info.h"
 
+#ifndef NULL
+#define NULL 0
+#endif
+
 namespace latinime {
 
 class UnigramDictionary {
@@ -31,8 +36,11 @@
     } ProximityType;
 
 public:
-    UnigramDictionary(const unsigned char *dict, int typedLetterMultipler, int fullWordMultiplier,
-            int maxWordLength, int maxWords, int maxProximityChars, const bool isLatestDictVersion);
+    UnigramDictionary(const uint8_t* const streamStart, int typedLetterMultipler,
+            int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars,
+            const bool isLatestDictVersion);
+    bool isValidWord(unsigned short *word, int length);
+    int getBigramPosition(int pos, unsigned short *word, int offset, int length) const;
     int getSuggestions(const ProximityInfo *proximityInfo, const int *xcoordinates,
             const int *ycoordinates, const int *codes, const int codesSize, const int flags,
             unsigned short *outWords, int *frequencies);
@@ -56,34 +64,30 @@
     bool checkIfDictVersionIsLatest();
     int getAddress(int *pos);
     int getFreq(int *pos);
-    int wideStrLen(unsigned short *str);
-    bool sameAsTyped(unsigned short *word, int length);
+    bool sameAsTyped(const unsigned short *word, int length) const;
     bool addWord(unsigned short *word, int length, int frequency);
-    unsigned short toBaseLowerCase(unsigned short c);
+    void addWordAlternatesSpellings(const uint8_t* const root, int pos, int depth, int finalFreq);
     void getWordsRec(const int childrenCount, const int pos, const int depth, const int maxDepth,
             const bool traverseAllNodes, const int snr, const int inputIndex, const int diffs,
             const int skipPos, const int excessivePos, const int transposedPos, int *nextLetters,
             const int nextLettersSize);
     bool getSplitTwoWordsSuggestion(const int inputLength,
             const int firstWordStartPos, const int firstWordLength,
-            const int secondWordStartPos, const int secondWordLength);
+            const int secondWordStartPos, const int secondWordLength, const bool isSpaceProximity);
     bool getMissingSpaceWords(const int inputLength, const int missingSpacePos);
     bool getMistypedSpaceWords(const int inputLength, const int spaceProximityPos);
     // Keep getWordsOld for comparing performance between getWords and getWordsOld
     void getWordsOld(const int initialPos, const int inputLength, const int skipPos,
             const int excessivePos, const int transposedPos, int *nextLetters,
             const int nextLettersSize);
-    void registerNextLetter(unsigned short c, int *nextLetters, int nextLettersSize);
     int calculateFinalFreq(const int inputIndex, const int depth, const int snr, const int skipPos,
             const int excessivePos, const int transposedPos, const int freq,
             const bool sameLength) const;
-    void onTerminalWhenUserTypedLengthIsGreaterThanInputLength(unsigned short *word,
-            const int inputIndex, const int depth, const int snr, int *nextLetters,
-            const int nextLettersSize, const int skipPos, const int excessivePos,
-            const int transposedPos, const int freq);
-    void onTerminalWhenUserTypedLengthIsSameAsInputLength(unsigned short *word,
-            const int inputIndex, const int depth, const int snr, const int skipPos,
-            const int excessivePos, const int transposedPos, const int freq);
+    void onTerminal(unsigned short int* word, const int depth,
+            const uint8_t* const root, const uint8_t flags, int pos,
+            const int inputIndex, const int matchWeight, const int skipPos,
+            const int excessivePos, const int transposedPos, const int freq, const bool sameLength,
+            int *nextLetters, const int nextLettersSize);
     bool needsToSkipCurrentNode(const unsigned short c,
             const int inputIndex, const int skipPos, const int depth);
     ProximityType getMatchedProximityId(const int *currentChars, const unsigned short c,
@@ -94,7 +98,7 @@
             const int diffs, const int skipPos, const int excessivePos, const int transposedPos,
             int *nextLetters, const int nextLettersSize, int *newCount, int *newChildPosition,
             bool *newTraverseAllNodes, int *newSnr, int*newInputIndex, int *newDiffs,
-            int *nextSiblingPosition);
+            int *nextSiblingPosition, int *nextOutputIndex);
     int getBestWordFreq(const int startInputIndex, const int inputLength, unsigned short *word);
     // Process a node by considering missing space
     bool processCurrentNodeForExactMatch(const int firstChildPos,
@@ -104,7 +108,8 @@
     inline const int* getInputCharsAt(const int index) const {
         return mInputCodes + (index * MAX_PROXIMITY_CHARS);
     }
-    const unsigned char *DICT;
+
+    const uint8_t* const DICT_ROOT;
     const int MAX_WORD_LENGTH;
     const int MAX_WORDS;
     const int MAX_PROXIMITY_CHARS;
@@ -138,11 +143,10 @@
     int mStackInputIndex[MAX_WORD_LENGTH_INTERNAL];
     int mStackDiffs[MAX_WORD_LENGTH_INTERNAL];
     int mStackSiblingPos[MAX_WORD_LENGTH_INTERNAL];
+    int mStackOutputIndex[MAX_WORD_LENGTH_INTERNAL];
     int mNextLettersFrequency[NEXT_LETTERS_SIZE];
 };
 
-// ----------------------------------------------------------------------------
-
-}; // namespace latinime
+} // namespace latinime
 
 #endif // LATINIME_UNIGRAM_DICTIONARY_H
diff --git a/tests/src/com/android/inputmethod/compat/ArraysCompatUtilsTests.java b/tests/src/com/android/inputmethod/compat/ArraysCompatUtilsTests.java
new file mode 100644
index 0000000..93681b6
--- /dev/null
+++ b/tests/src/com/android/inputmethod/compat/ArraysCompatUtilsTests.java
@@ -0,0 +1,103 @@
+/*
+ * 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.compat;
+
+import android.test.AndroidTestCase;
+
+public class ArraysCompatUtilsTests extends AndroidTestCase {
+    // See {@link tests.api.java.util.ArraysTest}.
+    private static final int ARRAY_SIZE = 100;
+    private final int[] mIntArray = new int[ARRAY_SIZE];
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        for (int counter = 0; counter < ARRAY_SIZE; counter++) {
+            mIntArray[counter] = counter;
+        }
+    }
+
+    public void testEmptyArray() {
+        final int index = ArraysCompatUtils.binarySearch(mIntArray, 0, 0, 0);
+        assertEquals("empty", ~0, index);
+        final int compat = ArraysCompatUtils.compatBinarySearch(mIntArray, 0, 0, 0);
+        assertEquals("empty compat", ~0, compat);
+    }
+
+    public void testEmptyRangeArray() {
+        final int mid = ARRAY_SIZE / 3;
+        final int index = ArraysCompatUtils.binarySearch(mIntArray, mid, mid, 1);
+        assertEquals("empty", ~mid, index);
+        final int compat = ArraysCompatUtils.compatBinarySearch(mIntArray, mid, mid, 1);
+        assertEquals("empty compat", ~mid, compat);
+    }
+
+    public void testFind() {
+        for (int counter = 0; counter < ARRAY_SIZE; counter++) {
+            final int index = ArraysCompatUtils.binarySearch(mIntArray, 0, ARRAY_SIZE, counter);
+            assertEquals("found", counter, index);
+        }
+        for (int counter = 0; counter < ARRAY_SIZE; counter++) {
+            final int compat = ArraysCompatUtils.compatBinarySearch(
+                    mIntArray, 0, ARRAY_SIZE, counter);
+            assertEquals("found compat", counter, compat);
+        }
+    }
+
+    public void testFindNegative() {
+        final int offset = ARRAY_SIZE / 2;
+        for (int counter = 0; counter < ARRAY_SIZE; counter++) {
+            mIntArray[counter] -= offset;
+        }
+        for (int counter = 0; counter < ARRAY_SIZE; counter++) {
+            final int index = ArraysCompatUtils.binarySearch(
+                    mIntArray, 0, ARRAY_SIZE, counter - offset);
+            assertEquals("found", counter, index);
+        }
+        for (int counter = 0; counter < ARRAY_SIZE; counter++) {
+            final int compat = ArraysCompatUtils.compatBinarySearch(
+                    mIntArray, 0, ARRAY_SIZE, counter - offset);
+            assertEquals("found compat", counter, compat);
+        }
+    }
+
+    public void testNotFountAtTop() {
+        final int index = ArraysCompatUtils.binarySearch(mIntArray, 0, ARRAY_SIZE, -1);
+        assertEquals("not found top", ~0, index);
+        final int compat = ArraysCompatUtils.compatBinarySearch(
+                    mIntArray, 0, ARRAY_SIZE, -1);
+        assertEquals("not found top compat", ~0, compat);
+    }
+
+    public void testNotFountAtEnd() {
+        final int index = ArraysCompatUtils.binarySearch(mIntArray, 0, ARRAY_SIZE, ARRAY_SIZE);
+        assertEquals("not found end", ~ARRAY_SIZE, index);
+        final int compat = ArraysCompatUtils.compatBinarySearch(
+                    mIntArray, 0, ARRAY_SIZE, ARRAY_SIZE);
+        assertEquals("not found end compat", ~ARRAY_SIZE, compat);
+    }
+
+    public void testNotFountAtMid() {
+        final int mid = ARRAY_SIZE / 3;
+        mIntArray[mid] = mIntArray[mid + 1];
+        final int index = ArraysCompatUtils.binarySearch(mIntArray, 0, ARRAY_SIZE, mid);
+        assertEquals("not found mid", ~mid, index);
+        final int compat = ArraysCompatUtils.compatBinarySearch(
+                    mIntArray, 0, ARRAY_SIZE, mid);
+        assertEquals("not found mid compat", ~mid, compat);
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/PopupCharactersParserTests.java b/tests/src/com/android/inputmethod/keyboard/PopupCharactersParserTests.java
deleted file mode 100644
index ae78866..0000000
--- a/tests/src/com/android/inputmethod/keyboard/PopupCharactersParserTests.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import com.android.inputmethod.latin.R;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.test.AndroidTestCase;
-
-public class PopupCharactersParserTests extends AndroidTestCase {
-    private Resources mRes;
-
-    private static final String CODE_SETTINGS = "@integer/key_settings";
-    private static final String ICON_SETTINGS = "@drawable/sym_keyboard_settings";
-    private static final String CODE_NON_EXISTING = "@integer/non_existing";
-    private static final String ICON_NON_EXISTING = "@drawable/non_existing";
-
-    private int mCodeSettings;
-    private Drawable mIconSettings;
-
-    @Override
-    protected void setUp() {
-        Resources res = getContext().getResources();
-        mRes = res;
-
-        final String packageName = res.getResourcePackageName(R.string.english_ime_name);
-        final int codeId = res.getIdentifier(CODE_SETTINGS.substring(1), null, packageName);
-        final int iconId = res.getIdentifier(ICON_SETTINGS.substring(1), null, packageName);
-        mCodeSettings = res.getInteger(codeId);
-        mIconSettings = res.getDrawable(iconId);
-    }
-
-    private void assertParser(String message, String popupSpec, String expectedLabel,
-            String expectedOutputText, Drawable expectedIcon, int expectedCode) {
-        String actualLabel = PopupCharactersParser.getLabel(popupSpec);
-        assertEquals(message + ": label:", expectedLabel, actualLabel);
-
-        String actualOutputText = PopupCharactersParser.getOutputText(popupSpec);
-        assertEquals(message + ": ouptputText:", expectedOutputText, actualOutputText);
-
-        Drawable actualIcon = PopupCharactersParser.getIcon(mRes, popupSpec);
-        // We can not compare drawables, checking null or non-null instead.
-        if (expectedIcon == null) {
-            assertNull(message + ": icon null:", actualIcon);
-        } else {
-            assertNotNull(message + ": icon non-null:", actualIcon);
-        }
-
-        int actualCode = PopupCharactersParser.getCode(mRes, popupSpec);
-        assertEquals(message + ": codes value:", expectedCode, actualCode);
-    }
-
-    private void assertParserError(String message, String popupSpec, String expectedLabel,
-            String expectedOutputText, Drawable expectedIcon, int expectedCode) {
-        try {
-            assertParser(message, popupSpec, expectedLabel, expectedOutputText, expectedIcon,
-                    expectedCode);
-            fail(message);
-        } catch (PopupCharactersParser.PopupCharactersParserError pcpe) {
-            // success.
-        }
-    }
-
-    public void testSingleLetter() {
-        assertParser("Single letter", "a", "a", null, null, 'a');
-        assertParser("Single escaped bar", "\\|", "|", null, null, '|');
-        assertParser("Single escaped escape", "\\\\", "\\", null, null, '\\');
-        assertParser("Single comma", ",", ",", null, null, ',');
-        assertParser("Single escaped comma", "\\,", ",", null, null, ',');
-        assertParser("Single escaped letter", "\\a", "a", null, null, 'a');
-        assertParser("Single at", "@", "@", null, null, '@');
-        assertParser("Single escaped at", "\\@", "@", null, null, '@');
-        assertParser("Single letter with outputText", "a|abc", "a", "abc", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Single letter with escaped outputText", "a|a\\|c", "a", "a|c", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Single letter with comma outputText", "a|a,b", "a", "a,b", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Single letter with escaped comma outputText", "a|a\\,b", "a", "a,b", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Single letter with outputText starts with at", "a|@bc", "a", "@bc", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Single letter with outputText contains at", "a|a@c", "a", "a@c", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Single letter with escaped at outputText", "a|\\@bc", "a", "@bc", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Single escaped escape with outputText", "\\\\|\\\\", "\\", "\\", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Single escaped bar with outputText", "\\||\\|", "|", "|", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Single letter with code", "a|" + CODE_SETTINGS, "a", null, null,
-                mCodeSettings);
-    }
-
-    public void testLabel() {
-        assertParser("Simple label", "abc", "abc", "abc", null, Keyboard.CODE_DUMMY);
-        assertParser("Label with escaped bar", "a\\|c", "a|c", "a|c", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label with escaped escape", "a\\\\c", "a\\c", "a\\c", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label with comma", "a,c", "a,c", "a,c", null, Keyboard.CODE_DUMMY);
-        assertParser("Label with escaped comma", "a\\,c", "a,c", "a,c", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label starts with at", "@bc", "@bc", "@bc", null, Keyboard.CODE_DUMMY);
-        assertParser("Label contains at", "a@c", "a@c", "a@c", null, Keyboard.CODE_DUMMY);
-        assertParser("Label with escaped at", "\\@bc", "@bc", "@bc", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label with escaped letter", "\\abc", "abc", "abc", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label with outputText", "abc|def", "abc", "def", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label with comma and outputText", "a,c|def", "a,c", "def", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Escaped comma label with outputText", "a\\,c|def", "a,c", "def", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Escaped label with outputText", "a\\|c|def", "a|c", "def", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label with escaped bar outputText", "abc|d\\|f", "abc", "d|f", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Escaped escape label with outputText", "a\\\\|def", "a\\", "def", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label starts with at and outputText", "@bc|def", "@bc", "def", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label contains at label and outputText", "a@c|def", "a@c", "def", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Escaped at label with outputText", "\\@bc|def", "@bc", "def", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label with comma outputText", "abc|a,b", "abc", "a,b", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label with escaped comma outputText", "abc|a\\,b", "abc", "a,b", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label with outputText starts with at", "abc|@bc", "abc", "@bc", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label with outputText contains at", "abc|a@c", "abc", "a@c", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label with escaped at outputText", "abc|\\@bc", "abc", "@bc", null,
-                Keyboard.CODE_DUMMY);
-        assertParser("Label with escaped bar outputText", "abc|d\\|f", "abc", "d|f",
-                null, Keyboard.CODE_DUMMY);
-        assertParser("Escaped bar label with escaped bar outputText", "a\\|c|d\\|f", "a|c", "d|f",
-                null, Keyboard.CODE_DUMMY);
-        assertParser("Label with code", "abc|" + CODE_SETTINGS, "abc", null, null, mCodeSettings);
-        assertParser("Escaped label with code", "a\\|c|" + CODE_SETTINGS, "a|c", null, null,
-                mCodeSettings);
-    }
-
-    public void testIconAndCode() {
-        assertParser("Icon with outputText", ICON_SETTINGS + "|abc", null, "abc", mIconSettings,
-                Keyboard.CODE_DUMMY);
-        assertParser("Icon with outputText starts with at", ICON_SETTINGS + "|@bc", null, "@bc",
-                mIconSettings, Keyboard.CODE_DUMMY);
-        assertParser("Icon with outputText contains at", ICON_SETTINGS + "|a@c", null, "a@c",
-                mIconSettings, Keyboard.CODE_DUMMY);
-        assertParser("Icon with escaped at outputText", ICON_SETTINGS + "|\\@bc", null, "@bc",
-                mIconSettings, Keyboard.CODE_DUMMY);
-        assertParser("Label starts with at and code", "@bc|" + CODE_SETTINGS, "@bc", null, null,
-                mCodeSettings);
-        assertParser("Label contains at and code", "a@c|" + CODE_SETTINGS, "a@c", null, null,
-                mCodeSettings);
-        assertParser("Escaped at label with code", "\\@bc|" + CODE_SETTINGS, "@bc", null, null,
-                mCodeSettings);
-        assertParser("Icon with code", ICON_SETTINGS + "|" + CODE_SETTINGS, null, null,
-                mIconSettings, mCodeSettings);
-    }
-
-    public void testFormatError() {
-        assertParserError("Empty spec", "", null, null, null, Keyboard.CODE_UNSPECIFIED);
-        assertParserError("Empty label with outputText", "|a", null, "a", null,
-                Keyboard.CODE_DUMMY);
-        assertParserError("Empty label with code", "|" + CODE_SETTINGS, null, null, null,
-                mCodeSettings);
-        assertParserError("Empty outputText with label", "a|", "a", null, null,
-                Keyboard.CODE_UNSPECIFIED);
-        assertParserError("Empty outputText with icon", ICON_SETTINGS + "|", null, null,
-                mIconSettings, Keyboard.CODE_UNSPECIFIED);
-        assertParserError("Empty icon and code", "|", null, null, null, Keyboard.CODE_UNSPECIFIED);
-        assertParserError("Icon without code", ICON_SETTINGS, null, null, mIconSettings,
-                Keyboard.CODE_DUMMY);
-        assertParserError("Non existing icon", ICON_NON_EXISTING + "|abc", null, "abc", null,
-                Keyboard.CODE_DUMMY);
-        assertParserError("Non existing code", "abc|" + CODE_NON_EXISTING, "abc", null, null,
-                Keyboard.CODE_UNSPECIFIED);
-        assertParserError("Third bar at end", "a|b|", "a", null, null, Keyboard.CODE_UNSPECIFIED);
-        assertParserError("Multiple bar", "a|b|c", "a", null, null, Keyboard.CODE_UNSPECIFIED);
-        assertParserError("Multiple bar with label and code", "a|" + CODE_SETTINGS + "|c", "a",
-                null, null, mCodeSettings);
-        assertParserError("Multiple bar with icon and outputText", ICON_SETTINGS + "|b|c", null,
-                null, mIconSettings, Keyboard.CODE_UNSPECIFIED);
-        assertParserError("Multiple bar with icon and code",
-                ICON_SETTINGS + "|" + CODE_SETTINGS + "|c", null, null, mIconSettings,
-                mCodeSettings);
-    }
-}
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyStylesTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyStylesTests.java
similarity index 96%
rename from tests/src/com/android/inputmethod/keyboard/KeyStylesTests.java
rename to tests/src/com/android/inputmethod/keyboard/internal/KeyStylesTests.java
index 5dff114..4050a71 100644
--- a/tests/src/com/android/inputmethod/keyboard/KeyStylesTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyStylesTests.java
@@ -14,9 +14,9 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
+package com.android.inputmethod.keyboard.internal;
 
-import com.android.inputmethod.keyboard.KeyStyles.EmptyKeyStyle;
+import com.android.inputmethod.keyboard.internal.KeyStyles.EmptyKeyStyle;
 
 import android.test.AndroidTestCase;
 import android.text.TextUtils;
diff --git a/tests/src/com/android/inputmethod/keyboard/MiniKeyboardBuilderTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilderTests.java
similarity index 99%
rename from tests/src/com/android/inputmethod/keyboard/MiniKeyboardBuilderTests.java
rename to tests/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilderTests.java
index 600342a..0d2802d 100644
--- a/tests/src/com/android/inputmethod/keyboard/MiniKeyboardBuilderTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilderTests.java
@@ -14,9 +14,9 @@
  * the License.
  */
 
-package com.android.inputmethod.keyboard;
+package com.android.inputmethod.keyboard.internal;
 
-import com.android.inputmethod.keyboard.MiniKeyboardBuilder.MiniKeyboardLayoutParams;
+import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder.MiniKeyboardLayoutParams;
 
 import android.test.AndroidTestCase;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/PopupCharactersParserTests.java b/tests/src/com/android/inputmethod/keyboard/internal/PopupCharactersParserTests.java
new file mode 100644
index 0000000..e4a1c68
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/internal/PopupCharactersParserTests.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.R;
+
+public class PopupCharactersParserTests extends AndroidTestCase {
+    private Resources mRes;
+
+    private static final int ICON_SETTINGS_KEY = 5;
+    private static final int ICON_UNDEFINED = KeyboardIconsSet.ICON_UNDEFINED;
+
+    private static final String CODE_SETTINGS = "@integer/key_settings";
+    private static final String ICON_SETTINGS = "@icon/" + ICON_SETTINGS_KEY;
+    private static final String CODE_NON_EXISTING = "@integer/non_existing";
+    private static final String ICON_NON_EXISTING = "@icon/non_existing";
+
+    private int mCodeSettings;
+
+    @Override
+    protected void setUp() {
+        Resources res = getContext().getResources();
+        mRes = res;
+
+        final String packageName = res.getResourcePackageName(R.string.english_ime_name);
+        final int codeId = res.getIdentifier(CODE_SETTINGS.substring(1), null, packageName);
+        mCodeSettings = res.getInteger(codeId);
+    }
+
+    private void assertParser(String message, String popupSpec, String expectedLabel,
+            String expectedOutputText, int expectedIcon, int expectedCode) {
+        String actualLabel = PopupCharactersParser.getLabel(popupSpec);
+        assertEquals(message + ": label:", expectedLabel, actualLabel);
+
+        String actualOutputText = PopupCharactersParser.getOutputText(popupSpec);
+        assertEquals(message + ": ouptputText:", expectedOutputText, actualOutputText);
+
+        int actualIcon = PopupCharactersParser.getIconId(popupSpec);
+        assertEquals(message + ": icon:", expectedIcon, actualIcon);
+
+        int actualCode = PopupCharactersParser.getCode(mRes, popupSpec);
+        assertEquals(message + ": codes value:", expectedCode, actualCode);
+    }
+
+    private void assertParserError(String message, String popupSpec, String expectedLabel,
+            String expectedOutputText, int expectedIcon, int expectedCode) {
+        try {
+            assertParser(message, popupSpec, expectedLabel, expectedOutputText, expectedIcon,
+                    expectedCode);
+            fail(message);
+        } catch (PopupCharactersParser.PopupCharactersParserError pcpe) {
+            // success.
+        }
+    }
+
+    public void testSingleLetter() {
+        assertParser("Single letter", "a",
+                "a", null, ICON_UNDEFINED, 'a');
+        assertParser("Single escaped bar", "\\|",
+                "|", null, ICON_UNDEFINED, '|');
+        assertParser("Single escaped escape", "\\\\",
+                "\\", null, ICON_UNDEFINED, '\\');
+        assertParser("Single comma", ",",
+                ",", null, ICON_UNDEFINED, ',');
+        assertParser("Single escaped comma", "\\,",
+                ",", null, ICON_UNDEFINED, ',');
+        assertParser("Single escaped letter", "\\a",
+                "a", null, ICON_UNDEFINED, 'a');
+        assertParser("Single at", "@",
+                "@", null, ICON_UNDEFINED, '@');
+        assertParser("Single escaped at", "\\@",
+                "@", null, ICON_UNDEFINED, '@');
+        assertParser("Single letter with outputText", "a|abc",
+                "a", "abc", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Single letter with escaped outputText", "a|a\\|c",
+                "a", "a|c", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Single letter with comma outputText", "a|a,b",
+                "a", "a,b", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Single letter with escaped comma outputText", "a|a\\,b",
+                "a", "a,b", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Single letter with outputText starts with at", "a|@bc",
+                "a", "@bc", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Single letter with outputText contains at", "a|a@c",
+                "a", "a@c", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Single letter with escaped at outputText", "a|\\@bc",
+                "a", "@bc", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Single escaped escape with outputText", "\\\\|\\\\",
+                "\\", "\\", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Single escaped bar with outputText", "\\||\\|",
+                "|", "|", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Single letter with code", "a|" + CODE_SETTINGS,
+                "a", null, ICON_UNDEFINED, mCodeSettings);
+    }
+
+    public void testLabel() {
+        assertParser("Simple label", "abc",
+                "abc", "abc", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with escaped bar", "a\\|c",
+                "a|c", "a|c", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with escaped escape", "a\\\\c",
+                "a\\c", "a\\c", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with comma", "a,c",
+                "a,c", "a,c", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with escaped comma", "a\\,c",
+                "a,c", "a,c", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label starts with at", "@bc",
+                "@bc", "@bc", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label contains at", "a@c",
+                "a@c", "a@c", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with escaped at", "\\@bc",
+                "@bc", "@bc", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with escaped letter", "\\abc",
+                "abc", "abc", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with outputText", "abc|def",
+                "abc", "def", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with comma and outputText", "a,c|def",
+                "a,c", "def", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Escaped comma label with outputText", "a\\,c|def",
+                "a,c", "def", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Escaped label with outputText", "a\\|c|def",
+                "a|c", "def", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with escaped bar outputText", "abc|d\\|f",
+                "abc", "d|f", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Escaped escape label with outputText", "a\\\\|def",
+                "a\\", "def", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label starts with at and outputText", "@bc|def",
+                "@bc", "def", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label contains at label and outputText", "a@c|def",
+                "a@c", "def", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Escaped at label with outputText", "\\@bc|def",
+                "@bc", "def", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with comma outputText", "abc|a,b",
+                "abc", "a,b", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with escaped comma outputText", "abc|a\\,b",
+                "abc", "a,b", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with outputText starts with at", "abc|@bc",
+                "abc", "@bc", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with outputText contains at", "abc|a@c",
+                "abc", "a@c", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with escaped at outputText", "abc|\\@bc",
+                "abc", "@bc", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with escaped bar outputText", "abc|d\\|f",
+                "abc", "d|f", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Escaped bar label with escaped bar outputText", "a\\|c|d\\|f",
+                "a|c", "d|f", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParser("Label with code", "abc|" + CODE_SETTINGS,
+                "abc", null, ICON_UNDEFINED, mCodeSettings);
+        assertParser("Escaped label with code", "a\\|c|" + CODE_SETTINGS,
+                "a|c", null, ICON_UNDEFINED, mCodeSettings);
+    }
+
+    public void testIconAndCode() {
+        assertParser("Icon with outputText", ICON_SETTINGS + "|abc",
+                null, "abc", ICON_SETTINGS_KEY, Keyboard.CODE_DUMMY);
+        assertParser("Icon with outputText starts with at", ICON_SETTINGS + "|@bc",
+                null, "@bc", ICON_SETTINGS_KEY, Keyboard.CODE_DUMMY);
+        assertParser("Icon with outputText contains at", ICON_SETTINGS + "|a@c",
+                null, "a@c", ICON_SETTINGS_KEY, Keyboard.CODE_DUMMY);
+        assertParser("Icon with escaped at outputText", ICON_SETTINGS + "|\\@bc",
+                null, "@bc", ICON_SETTINGS_KEY, Keyboard.CODE_DUMMY);
+        assertParser("Label starts with at and code", "@bc|" + CODE_SETTINGS,
+                "@bc", null, ICON_UNDEFINED, mCodeSettings);
+        assertParser("Label contains at and code", "a@c|" + CODE_SETTINGS,
+                "a@c", null, ICON_UNDEFINED, mCodeSettings);
+        assertParser("Escaped at label with code", "\\@bc|" + CODE_SETTINGS,
+                "@bc", null, ICON_UNDEFINED, mCodeSettings);
+        assertParser("Icon with code", ICON_SETTINGS + "|" + CODE_SETTINGS,
+                null, null, ICON_SETTINGS_KEY, mCodeSettings);
+    }
+
+    public void testFormatError() {
+        assertParserError("Empty spec", "", null,
+                null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+        assertParserError("Empty label with outputText", "|a",
+                null, "a", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParserError("Empty label with code", "|" + CODE_SETTINGS,
+                null, null, ICON_UNDEFINED, mCodeSettings);
+        assertParserError("Empty outputText with label", "a|",
+                "a", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+        assertParserError("Empty outputText with icon", ICON_SETTINGS + "|",
+                null, null, ICON_SETTINGS_KEY, Keyboard.CODE_UNSPECIFIED);
+        assertParserError("Empty icon and code", "|",
+                null, null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+        assertParserError("Icon without code", ICON_SETTINGS,
+                null, null, ICON_SETTINGS_KEY, Keyboard.CODE_DUMMY);
+        assertParser("Non existing icon", ICON_NON_EXISTING + "|abc",
+                null, "abc", ICON_UNDEFINED, Keyboard.CODE_DUMMY);
+        assertParserError("Non existing code", "abc|" + CODE_NON_EXISTING,
+                "abc", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+        assertParserError("Third bar at end", "a|b|",
+                "a", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+        assertParserError("Multiple bar", "a|b|c",
+                "a", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+        assertParserError("Multiple bar with label and code", "a|" + CODE_SETTINGS + "|c",
+                "a", null, ICON_UNDEFINED, mCodeSettings);
+        assertParserError("Multiple bar with icon and outputText", ICON_SETTINGS + "|b|c",
+                null, null, ICON_SETTINGS_KEY, Keyboard.CODE_UNSPECIFIED);
+        assertParserError("Multiple bar with icon and code",
+                ICON_SETTINGS + "|" + CODE_SETTINGS + "|c",
+                null, null, ICON_SETTINGS_KEY, mCodeSettings);
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/EventRingBufferTests.java b/tests/src/com/android/inputmethod/latin/EventRingBufferTests.java
index 869781f..a3d2a2b 100644
--- a/tests/src/com/android/inputmethod/latin/EventRingBufferTests.java
+++ b/tests/src/com/android/inputmethod/latin/EventRingBufferTests.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.latin;
 
-import com.android.inputmethod.keyboard.SwipeTracker.EventRingBuffer;
+import com.android.inputmethod.keyboard.internal.SwipeTracker.EventRingBuffer;
 
 import android.test.AndroidTestCase;
 
diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
index d128cb3..d102aa4 100644
--- a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
+++ b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.latin.Utils;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.test.AndroidTestCase;
@@ -75,7 +77,7 @@
         int failedCount = 0;
         for (final InputMethodSubtype subtype : mKeyboardSubtypes) {
             final String localeCode = subtype.getLocale();
-            final Locale locale = new Locale(localeCode);
+            final Locale locale = Utils.constructLocaleFromString(localeCode);
             // The locale name which will be displayed on spacebar.  For example 'English (US)' or
             // 'Francais (Canada)'.  (c=\u008d)
             final String displayName = SubtypeLocale.getFullDisplayName(locale);
diff --git a/tests/src/com/android/inputmethod/latin/SuggestHelper.java b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
index 5930ea3..87ea011 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestHelper.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
@@ -20,7 +20,6 @@
 import com.android.inputmethod.keyboard.KeyDetector;
 import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.LatinKeyboard;
-import com.android.inputmethod.keyboard.ProximityKeyDetector;
 
 import android.content.Context;
 import android.text.TextUtils;
@@ -34,17 +33,19 @@
     private final KeyDetector mKeyDetector;
 
     public SuggestHelper(Context context, int dictionaryId, KeyboardId keyboardId) {
-        mSuggest = new Suggest(context, dictionaryId);
-        mKeyboard = new LatinKeyboard(context, keyboardId);
-        mKeyDetector = new ProximityKeyDetector();
+        // Use null as the locale for Suggest so as to force it to use the internal dictionary
+        // (and not try to find a dictionary provider for a specified locale)
+        mSuggest = new Suggest(context, dictionaryId, null);
+        mKeyboard = new LatinKeyboard(context, keyboardId, keyboardId.mWidth);
+        mKeyDetector = new KeyDetector();
         init();
     }
 
     protected SuggestHelper(Context context, File dictionaryPath, long startOffset, long length,
             KeyboardId keyboardId) {
-        mSuggest = new Suggest(dictionaryPath, startOffset, length);
-        mKeyboard = new LatinKeyboard(context, keyboardId);
-        mKeyDetector = new ProximityKeyDetector();
+        mSuggest = new Suggest(context, dictionaryPath, startOffset, length, null);
+        mKeyboard = new LatinKeyboard(context, keyboardId, keyboardId.mWidth);
+        mKeyDetector = new KeyDetector();
         init();
     }
 
@@ -53,7 +54,7 @@
         mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL);
         mKeyDetector.setKeyboard(mKeyboard, 0, 0);
         mKeyDetector.setProximityCorrectionEnabled(true);
-        mKeyDetector.setProximityThreshold(KeyDetector.getMostCommonKeyWidth(mKeyboard));
+        mKeyDetector.setProximityThreshold(mKeyboard.getMostCommonKeyWidth());
     }
 
     public void setCorrectionMode(int correctionMode) {
diff --git a/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java b/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java
index 48d169f..bb2a6fe 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java
@@ -16,15 +16,14 @@
 
 package com.android.inputmethod.latin;
 
-import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.KeyboardView;
-
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Configuration;
 import android.test.AndroidTestCase;
 import android.text.TextUtils;
 import android.view.inputmethod.EditorInfo;
 
+import com.android.inputmethod.keyboard.KeyboardId;
+
 import java.io.File;
 import java.io.InputStream;
 import java.util.Locale;
@@ -38,11 +37,12 @@
         mTestPackageFile = new File(getTestContext().getApplicationInfo().sourceDir);
     }
 
-    protected static KeyboardId createKeyboardId(Locale locale) {
+    protected KeyboardId createKeyboardId(Locale locale) {
+        final int displayWidth = getContext().getResources().getDisplayMetrics().widthPixels;
         return new KeyboardId(locale.toString() + " keyboard",
-                com.android.inputmethod.latin.R.xml.kbd_qwerty, KeyboardView.COLOR_SCHEME_WHITE,
-                locale, Configuration.ORIENTATION_LANDSCAPE, KeyboardId.MODE_TEXT,
-                new EditorInfo(), false, KeyboardId.F2KEY_MODE_NONE, false, false, false, false);
+                com.android.inputmethod.latin.R.xml.kbd_qwerty, locale,
+                Configuration.ORIENTATION_LANDSCAPE, displayWidth, KeyboardId.MODE_TEXT,
+                new EditorInfo(), false, false, false, false);
     }
 
     protected InputStream openTestRawResource(int resIdInTest) {
diff --git a/tests/src/com/android/inputmethod/latin/UtilsTests.java b/tests/src/com/android/inputmethod/latin/UtilsTests.java
new file mode 100644
index 0000000..5c0b03a
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/UtilsTests.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010,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.test.AndroidTestCase;
+
+import com.android.inputmethod.latin.tests.R;
+
+public class UtilsTests extends AndroidTestCase {
+
+    // The following is meant to be a reasonable default for
+    // the "word_separators" resource.
+    private static final String sSeparators = ".,:;!?-";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    /************************** Tests ************************/
+
+    /**
+     * Test for getting previous word (for bigram suggestions)
+     */
+    public void testGetPreviousWord() {
+        // If one of the following cases breaks, the bigram suggestions won't work.
+        assertEquals(EditingUtils.getPreviousWord("abc def", sSeparators), "abc");
+        assertNull(EditingUtils.getPreviousWord("abc", sSeparators));
+        assertNull(EditingUtils.getPreviousWord("abc. def", sSeparators));
+
+        // The following tests reflect the current behavior of the function
+        // EditingUtils#getPreviousWord.
+        // TODO: However at this time, the code does never go
+        // into such a path, so it should be safe to change the behavior of
+        // this function if needed - especially since it does not seem very
+        // logical. These tests are just there to catch any unintentional
+        // changes in the behavior of the EditingUtils#getPreviousWord method.
+        assertEquals(EditingUtils.getPreviousWord("abc def ", sSeparators), "abc");
+        assertEquals(EditingUtils.getPreviousWord("abc def.", sSeparators), "abc");
+        assertEquals(EditingUtils.getPreviousWord("abc def .", sSeparators), "def");
+        assertNull(EditingUtils.getPreviousWord("abc ", sSeparators));
+    }
+
+    /**
+     * Test for getting the word before the cursor (for bigram)
+     */
+    public void testGetThisWord() {
+        assertEquals(EditingUtils.getThisWord("abc def", sSeparators), "def");
+        assertEquals(EditingUtils.getThisWord("abc def ", sSeparators), "def");
+        assertNull(EditingUtils.getThisWord("abc def.", sSeparators));
+        assertNull(EditingUtils.getThisWord("abc def .", sSeparators));
+    }
+}
diff --git a/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java b/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java
index 51e2038..4a285ff 100644
--- a/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java
+++ b/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java
@@ -41,18 +41,17 @@
  *  in the data. There is no need to increase the version when only the words in the data changes.
  */
 public class MakeBinaryDictionary {
-
     private static final int VERSION_NUM = 200;
 
-    public static final int ALPHA_SIZE = 256;
-
-    public static final String TAG_WORD = "w";
-    public static final String ATTR_FREQ = "f";
+    private static final String TAG_WORD = "w";
+    private static final String ATTR_FREQ = "f";
 
     private static final int FLAG_ADDRESS_MASK  = 0x400000;
     private static final int FLAG_TERMINAL_MASK = 0x800000;
     private static final int ADDRESS_MASK = 0x3FFFFF;
 
+    private static final int INITIAL_STRING_BUILDER_CAPACITY = 48;
+
     /**
      * Unit for this variable is in bytes
      * If destination file name is main.dict and file limit causes dictionary to be separated into
@@ -61,15 +60,15 @@
     private static int sOutputFileSize;
     private static boolean sSplitOutput;
 
-    public static final CharNode EMPTY_NODE = new CharNode();
+    private static final CharNode EMPTY_NODE = new CharNode();
 
-    List<CharNode> roots;
-    Map<String, Integer> mDictionary;
-    int mWordCount;
+    private List<CharNode> mRoots;
+    private Map<String, Integer> mDictionary;
+    private int mWordCount;
 
-    BigramDictionary bigramDict;
+    private BigramDictionary mBigramDict;
 
-    static class CharNode {
+    private static class CharNode {
         char data;
         int freq;
         boolean terminal;
@@ -81,7 +80,7 @@
         }
     }
 
-    public static void usage() {
+    private static void usage() {
         System.err.println("Usage: makedict -s <src_dict.xml> [-b <src_bigram.xml>] "
                 + "-d <dest.dict> [--size filesize]");
         System.exit(-1);
@@ -118,36 +117,37 @@
         }
     }
 
-    public MakeBinaryDictionary(String srcFilename, String bigramSrcFilename, String destFilename){
+    private MakeBinaryDictionary(String srcFilename, String bigramSrcFilename,
+            String destFilename) {
         System.out.println("Generating dictionary version " + VERSION_NUM);
-        bigramDict = new BigramDictionary(bigramSrcFilename, (bigramSrcFilename != null));
+        mBigramDict = new BigramDictionary(bigramSrcFilename, (bigramSrcFilename != null));
         populateDictionary(srcFilename);
         writeToDict(destFilename);
 
         // Enable the code below to verify that the generated tree is traversable
         // and bigram data is stored correctly.
         if (false) {
-            bigramDict.reverseLookupAll(mDictionary, dict);
+            mBigramDict.reverseLookupAll(mDictionary, mDict);
             traverseDict(2, new char[32], 0);
         }
     }
 
     private void populateDictionary(String filename) {
-        roots = new ArrayList<CharNode>();
+        mRoots = new ArrayList<CharNode>();
         mDictionary = new HashMap<String, Integer>();
         try {
             SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
             parser.parse(new File(filename), new DefaultHandler() {
                 boolean inWord;
                 int freq;
-                StringBuilder wordBuilder = new StringBuilder(48);
+                StringBuilder wordBuilder = new StringBuilder(INITIAL_STRING_BUILDER_CAPACITY);
 
                 @Override
                 public void startElement(String uri, String localName,
                         String qName, Attributes attributes) {
-                    if (qName.equals("w")) {
+                    if (qName.equals(TAG_WORD)) {
                         inWord = true;
-                        freq = Integer.parseInt(attributes.getValue(0));
+                        freq = Integer.parseInt(attributes.getValue(ATTR_FREQ));
                         wordBuilder.setLength(0);
                     }
                 }
@@ -162,7 +162,7 @@
                 @Override
                 public void endElement(String uri, String localName,
                         String qName) {
-                    if (qName.equals("w")) {
+                    if (qName.equals(TAG_WORD)) {
                         if (wordBuilder.length() >= 1) {
                             addWordTop(wordBuilder.toString(), freq);
                             mWordCount++;
@@ -178,7 +178,7 @@
         System.out.println("Nodes = " + CharNode.sNodes);
     }
 
-    private int indexOf(List<CharNode> children, char c) {
+    private static int indexOf(List<CharNode> children, char c) {
         if (children == null) {
             return -1;
         }
@@ -190,27 +190,30 @@
         return -1;
     }
 
-    private void addWordTop(String word, int occur) {
-        if (occur > 255) occur = 255;
+    private void addWordTop(String word, int freq) {
+        if (freq < 0) {
+            freq = 0;
+        } else if (freq > 255) {
+            freq = 255;
+        }
         char firstChar = word.charAt(0);
-        int index = indexOf(roots, firstChar);
+        int index = indexOf(mRoots, firstChar);
         if (index == -1) {
             CharNode newNode = new CharNode();
             newNode.data = firstChar;
-            newNode.freq = occur;
-            index = roots.size();
-            roots.add(newNode);
-        } else {
-            roots.get(index).freq += occur;
+            index = mRoots.size();
+            mRoots.add(newNode);
         }
+        final CharNode node = mRoots.get(index);
         if (word.length() > 1) {
-            addWordRec(roots.get(index), word, 1, occur);
+            addWordRec(node, word, 1, freq);
         } else {
-            roots.get(index).terminal = true;
+            node.terminal = true;
+            node.freq = freq;
         }
     }
 
-    private void addWordRec(CharNode parent, String word, int charAt, int occur) {
+    private void addWordRec(CharNode parent, String word, int charAt, int freq) {
         CharNode child = null;
         char data = word.charAt(charAt);
         if (parent.children == null) {
@@ -229,89 +232,89 @@
             parent.children.add(child);
         }
         child.data = data;
-        if (child.freq == 0) child.freq = occur;
         if (word.length() > charAt + 1) {
-            addWordRec(child, word, charAt + 1, occur);
+            addWordRec(child, word, charAt + 1, freq);
         } else {
             child.terminal = true;
-            child.freq = occur;
+            child.freq = freq;
         }
     }
 
-    byte[] dict;
-    int dictSize;
-    static final int CHAR_WIDTH = 8;
-    static final int FLAGS_WIDTH = 1; // Terminal flag (word end)
-    static final int ADDR_WIDTH = 23; // Offset to children
-    static final int FREQ_WIDTH_BYTES = 1;
-    static final int COUNT_WIDTH_BYTES = 1;
+    private byte[] mDict;
+    private int mDictSize;
+    private static final int CHAR_WIDTH = 8;
+    private static final int FLAGS_WIDTH = 1; // Terminal flag (word end)
+    private static final int ADDR_WIDTH = 23; // Offset to children
+    private static final int FREQ_WIDTH_BYTES = 1;
+    private static final int COUNT_WIDTH_BYTES = 1;
 
     private void addCount(int count) {
-        dict[dictSize++] = (byte) (0xFF & count);
+        mDict[mDictSize++] = (byte) (0xFF & count);
     }
 
     private void addNode(CharNode node, String word1) {
-        if (node.terminal) { // store address of each word1
-            mDictionary.put(word1, dictSize);
+        if (node.terminal) { // store address of each word1 for bigram dic generation
+            mDictionary.put(word1, mDictSize);
         }
+
         int charData = 0xFFFF & node.data;
         if (charData > 254) {
-            dict[dictSize++] = (byte) 255;
-            dict[dictSize++] = (byte) ((node.data >> 8) & 0xFF);
-            dict[dictSize++] = (byte) (node.data & 0xFF);
+            mDict[mDictSize++] = (byte) 255;
+            mDict[mDictSize++] = (byte) ((node.data >> 8) & 0xFF);
+            mDict[mDictSize++] = (byte) (node.data & 0xFF);
         } else {
-            dict[dictSize++] = (byte) (0xFF & node.data);
+            mDict[mDictSize++] = (byte) (0xFF & node.data);
         }
         if (node.children != null) {
-            dictSize += 3; // Space for children address
+            mDictSize += 3; // Space for children address
         } else {
-            dictSize += 1; // Space for just the terminal/address flags
+            mDictSize += 1; // Space for just the terminal/address flags
         }
         if ((0xFFFFFF & node.freq) > 255) {
             node.freq = 255;
         }
         if (node.terminal) {
             byte freq = (byte) (0xFF & node.freq);
-            dict[dictSize++] = freq;
+            mDict[mDictSize++] = freq;
             // bigram
-            if (bigramDict.mBi.containsKey(word1)) {
-                int count = bigramDict.mBi.get(word1).count;
-                bigramDict.mBigramToFill.add(word1);
-                bigramDict.mBigramToFillAddress.add(dictSize);
-                dictSize += (4 * count);
+            if (mBigramDict.mBi.containsKey(word1)) {
+                int count = mBigramDict.mBi.get(word1).count;
+                mBigramDict.mBigramToFill.add(word1);
+                mBigramDict.mBigramToFillAddress.add(mDictSize);
+                mDictSize += (4 * count);
             } else {
-                dict[dictSize++] = (byte) (0x00);
+                mDict[mDictSize++] = (byte) (0x00);
             }
         }
     }
 
-    int nullChildrenCount = 0;
-    int notTerminalCount = 0;
+    private int mNullChildrenCount = 0;
+    private int mNotTerminalCount = 0;
 
     private void updateNodeAddress(int nodeAddress, CharNode node,
             int childrenAddress) {
-        if ((dict[nodeAddress] & 0xFF) == 0xFF) { // 3 byte character
+        if ((mDict[nodeAddress] & 0xFF) == 0xFF) { // 3 byte character
             nodeAddress += 2;
         }
         childrenAddress = ADDRESS_MASK & childrenAddress;
         if (childrenAddress == 0) {
-            nullChildrenCount++;
+            mNullChildrenCount++;
         } else {
             childrenAddress |= FLAG_ADDRESS_MASK;
         }
         if (node.terminal) {
             childrenAddress |= FLAG_TERMINAL_MASK;
         } else {
-            notTerminalCount++;
+            mNotTerminalCount++;
         }
-        dict[nodeAddress + 1] = (byte) (childrenAddress >> 16);
+        mDict[nodeAddress + 1] = (byte) (childrenAddress >> 16);
         if ((childrenAddress & FLAG_ADDRESS_MASK) != 0) {
-            dict[nodeAddress + 2] = (byte) ((childrenAddress & 0xFF00) >> 8);
-            dict[nodeAddress + 3] = (byte) ((childrenAddress & 0xFF));
+            mDict[nodeAddress + 2] = (byte) ((childrenAddress & 0xFF00) >> 8);
+            mDict[nodeAddress + 3] = (byte) ((childrenAddress & 0xFF));
         }
     }
 
-    void writeWordsRec(List<CharNode> children, StringBuilder word) {
+    private void writeWordsRec(List<CharNode> children, StringBuilder word) {
         if (children == null || children.size() == 0) {
             return;
         }
@@ -319,60 +322,59 @@
         addCount(childCount);
         int[] childrenAddresses = new int[childCount];
         for (int j = 0; j < childCount; j++) {
-            CharNode node = children.get(j);
-            childrenAddresses[j] = dictSize;
-            word.append(children.get(j).data);
-            addNode(node, word.toString());
-            word.deleteCharAt(word.length()-1);
+            CharNode child = children.get(j);
+            childrenAddresses[j] = mDictSize;
+            word.append(child.data);
+            addNode(child, word.toString());
+            word.setLength(word.length() - 1);
         }
         for (int j = 0; j < childCount; j++) {
-            CharNode node = children.get(j);
+            CharNode child = children.get(j);
             int nodeAddress = childrenAddresses[j];
-            int cacheDictSize = dictSize;
-            word.append(children.get(j).data);
-            writeWordsRec(node.children, word);
-            word.deleteCharAt(word.length()-1);
-            updateNodeAddress(nodeAddress, node, node.children != null
-                    ? cacheDictSize : 0);
+            int cacheDictSize = mDictSize;
+            word.append(child.data);
+            writeWordsRec(child.children, word);
+            word.setLength(word.length() - 1);
+            updateNodeAddress(nodeAddress, child, child.children != null ? cacheDictSize : 0);
         }
     }
 
-    void writeToDict(String dictFilename) {
+    private void writeToDict(String dictFilename) {
         // 4MB max, 22-bit offsets
-        dict = new byte[4 * 1024 * 1024]; // 4MB upper limit. Actual is probably
-                                          // < 1MB in most cases, as there is a limit in the
-                                          // resource size in apks.
-        dictSize = 0;
+        mDict = new byte[4 * 1024 * 1024]; // 4MB upper limit. Actual is probably
+                                           // < 1MB in most cases, as there is a limit in the
+                                           // resource size in apks.
+        mDictSize = 0;
 
-        dict[dictSize++] = (byte) (0xFF & VERSION_NUM); // version info
-        dict[dictSize++] = (byte) (0xFF & (bigramDict.mHasBigram ? 1 : 0));
+        mDict[mDictSize++] = (byte) (0xFF & VERSION_NUM); // version info
+        mDict[mDictSize++] = (byte) (0xFF & (mBigramDict.mHasBigram ? 1 : 0));
 
-        StringBuilder word = new StringBuilder(48);
-        writeWordsRec(roots, word);
-        dict = bigramDict.writeBigrams(dict, mDictionary);
-        System.out.println("Dict Size = " + dictSize);
+        final StringBuilder word = new StringBuilder(INITIAL_STRING_BUILDER_CAPACITY);
+        writeWordsRec(mRoots, word);
+        mDict = mBigramDict.writeBigrams(mDict, mDictionary);
+        System.out.println("Dict Size = " + mDictSize);
         if (!sSplitOutput) {
-            sOutputFileSize = dictSize;
+            sOutputFileSize = mDictSize;
         }
         try {
             int currentLoc = 0;
             int i = 0;
             int extension = dictFilename.indexOf(".dict");
             String filename = dictFilename.substring(0, extension);
-            while (dictSize > 0) {
+            while (mDictSize > 0) {
                 FileOutputStream fos;
                 if (sSplitOutput) {
                     fos = new FileOutputStream(filename + i + ".dict");
                 } else {
                     fos = new FileOutputStream(filename + ".dict");
                 }
-                if (dictSize > sOutputFileSize) {
-                    fos.write(dict, currentLoc, sOutputFileSize);
-                    dictSize -= sOutputFileSize;
+                if (mDictSize > sOutputFileSize) {
+                    fos.write(mDict, currentLoc, sOutputFileSize);
+                    mDictSize -= sOutputFileSize;
                     currentLoc += sOutputFileSize;
                 } else {
-                    fos.write(dict, currentLoc, dictSize);
-                    dictSize = 0;
+                    fos.write(mDict, currentLoc, mDictSize);
+                    mDictSize = 0;
                 }
                 fos.close();
                 i++;
@@ -382,36 +384,36 @@
         }
     }
 
-    void traverseDict(int pos, char[] word, int depth) {
-        int count = dict[pos++] & 0xFF;
+    private void traverseDict(int pos, char[] word, int depth) {
+        int count = mDict[pos++] & 0xFF;
         for (int i = 0; i < count; i++) {
-            char c = (char) (dict[pos++] & 0xFF);
+            char c = (char) (mDict[pos++] & 0xFF);
             if (c == 0xFF) { // two byte character
-                c = (char) (((dict[pos] & 0xFF) << 8) | (dict[pos+1] & 0xFF));
+                c = (char) (((mDict[pos] & 0xFF) << 8) | (mDict[pos+1] & 0xFF));
                 pos += 2;
             }
             word[depth] = c;
-            boolean terminal = getFirstBitOfByte(pos, dict);
+            boolean terminal = getFirstBitOfByte(pos, mDict);
             int address = 0;
-            if ((dict[pos] & (FLAG_ADDRESS_MASK >> 16)) > 0) { // address check
-                address = get22BitAddress(pos, dict);
+            if ((mDict[pos] & (FLAG_ADDRESS_MASK >> 16)) > 0) { // address check
+                address = get22BitAddress(pos, mDict);
                 pos += 3;
             } else {
                 pos += 1;
             }
             if (terminal) {
-                showWord(word, depth + 1, dict[pos] & 0xFF);
+                showWord(word, depth + 1, mDict[pos] & 0xFF);
                 pos++;
 
-                int bigramExist = (dict[pos] & bigramDict.FLAG_BIGRAM_READ);
+                int bigramExist = (mDict[pos] & mBigramDict.FLAG_BIGRAM_READ);
                 if (bigramExist > 0) {
                     int nextBigramExist = 1;
                     while (nextBigramExist > 0) {
-                        int bigramAddress = get22BitAddress(pos, dict);
+                        int bigramAddress = get22BitAddress(pos, mDict);
                         pos += 3;
-                        int frequency = (bigramDict.FLAG_BIGRAM_FREQ & dict[pos]);
-                        bigramDict.searchForTerminalNode(bigramAddress, frequency, dict);
-                        nextBigramExist = (dict[pos++] & bigramDict.FLAG_BIGRAM_CONTINUED);
+                        int frequency = (mBigramDict.FLAG_BIGRAM_FREQ & mDict[pos]);
+                        mBigramDict.searchForTerminalNode(bigramAddress, frequency, mDict);
+                        nextBigramExist = (mDict[pos++] & mBigramDict.FLAG_BIGRAM_CONTINUED);
                     }
                 } else {
                     pos++;
@@ -423,21 +425,21 @@
         }
     }
 
-    void showWord(char[] word, int size, int freq) {
+    private static void showWord(char[] word, int size, int freq) {
         System.out.print(new String(word, 0, size) + " " + freq + "\n");
     }
 
-    static int get22BitAddress(int pos, byte[] dict) {
+    /* package */ static int get22BitAddress(int pos, byte[] dict) {
         return ((dict[pos + 0] & 0x3F) << 16)
                 | ((dict[pos + 1] & 0xFF) << 8)
                 | ((dict[pos + 2] & 0xFF));
     }
 
-    static boolean getFirstBitOfByte(int pos, byte[] dict) {
+    /* package */ static boolean getFirstBitOfByte(int pos, byte[] dict) {
         return (dict[pos] & 0x80) > 0;
     }
 
-    static boolean getSecondBitOfByte(int pos, byte[] dict) {
+    /* package */ static boolean getSecondBitOfByte(int pos, byte[] dict) {
         return (dict[pos] & 0x40) > 0;
     }
 }