Merge "Add the new dictionary maker to the build."
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index cb0a9a2..d680f18 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -21,6 +21,14 @@
             <meta-data android:name="android.view.im" android:resource="@xml/method" />
         </service>
 
+        <service android:name=".spellcheck.AndroidSpellCheckerService"
+                 android:label="@string/spell_checker_service_name"
+                 android:permission="android.permission.BIND_TEXT_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.textservice.SpellCheckerService" />
+            </intent-filter>
+            <meta-data android:name="android.view.textservice.scs" android:resource="@xml/spellchecker" />
+        </service>
         <activity android:name="Settings" android:label="@string/english_ime_settings">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
diff --git a/java/proguard.flags b/java/proguard.flags
index 7ce6f41..3959500 100644
--- a/java/proguard.flags
+++ b/java/proguard.flags
@@ -30,3 +30,7 @@
 -keep class com.android.inputmethod.latin.SettingsActivity {
   *;
 }
+
+-keep class com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder$MiniKeyboardParams {
+  <init>(...);
+}
diff --git a/java/res/drawable-hdpi/keyboard_background_holo.9.png b/java/res/drawable-hdpi/keyboard_background_holo.9.png
index 39746ff..714db43 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_suggest_strip_holo.9.png b/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png
index 85b6360..e173beb 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/more_suggestions_hint.png b/java/res/drawable-hdpi/more_suggestions_hint.png
new file mode 100644
index 0000000..4515f44
--- /dev/null
+++ b/java/res/drawable-hdpi/more_suggestions_hint.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
deleted file mode 100644
index 3e4eff6..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_delete_rtl.png b/java/res/drawable-hdpi/sym_bkeyboard_delete_rtl.png
deleted file mode 100644
index 102eac7..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_delete_rtl.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_label_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_label_mic.png
new file mode 100644
index 0000000..25702cf
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_label_mic.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
deleted file mode 100644
index 6f0aec2..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_123_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_123_mic_holo.png b/java/res/drawable-hdpi/sym_keyboard_123_mic_holo.png
deleted file mode 100644
index ed66f75..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_123_mic_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_delete_rtl.png b/java/res/drawable-hdpi/sym_keyboard_delete_rtl.png
deleted file mode 100644
index a508452..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_delete_rtl.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_delete_rtl_holo.png b/java/res/drawable-hdpi/sym_keyboard_delete_rtl_holo.png
deleted file mode 100644
index a77e4a0..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_delete_rtl_holo.png
+++ /dev/null
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
deleted file mode 100644
index 67311fc..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_feedback_space.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_label_mic.png b/java/res/drawable-hdpi/sym_keyboard_label_mic.png
new file mode 100644
index 0000000..4e0a8ed
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_label_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-hdpi/sym_keyboard_label_mic_holo.png
new file mode 100644
index 0000000..2280243
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_label_mic_holo.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
index 7215743..59ad534 100644
--- a/java/res/drawable-hdpi/sym_keyboard_tab_holo.png
+++ b/java/res/drawable-hdpi/sym_keyboard_tab_holo.png
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 f1d55c6..2776621 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_suggest_strip_holo.9.png b/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png
index e488323..b1c18b4 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/more_suggestions_hint.png b/java/res/drawable-mdpi/more_suggestions_hint.png
new file mode 100644
index 0000000..6168de3
--- /dev/null
+++ b/java/res/drawable-mdpi/more_suggestions_hint.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png b/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png
deleted file mode 100644
index 0749b5f..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_delete_rtl.png b/java/res/drawable-mdpi/sym_bkeyboard_delete_rtl.png
deleted file mode 100644
index 32253ea..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_delete_rtl.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_label_mic.png b/java/res/drawable-mdpi/sym_bkeyboard_label_mic.png
new file mode 100644
index 0000000..7f0b135
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_label_mic.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_123_mic.png b/java/res/drawable-mdpi/sym_keyboard_123_mic.png
deleted file mode 100644
index 35afe08..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_123_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_123_mic_holo.png b/java/res/drawable-mdpi/sym_keyboard_123_mic_holo.png
deleted file mode 100644
index 4814e51..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_123_mic_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_delete_rtl.png b/java/res/drawable-mdpi/sym_keyboard_delete_rtl.png
deleted file mode 100644
index d436c23..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_delete_rtl.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_delete_rtl_holo.png b/java/res/drawable-mdpi/sym_keyboard_delete_rtl_holo.png
deleted file mode 100644
index 2e75d85..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_delete_rtl_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_space.png b/java/res/drawable-mdpi/sym_keyboard_feedback_space.png
deleted file mode 100644
index 36eb60c..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_feedback_space.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_label_mic.png b/java/res/drawable-mdpi/sym_keyboard_label_mic.png
new file mode 100644
index 0000000..a354d53
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_label_mic.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-mdpi/sym_keyboard_label_mic_holo.png
new file mode 100644
index 0000000..d51adbe
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_label_mic_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
index e424321..d5edd32 100644
--- a/java/res/drawable-mdpi/sym_keyboard_tab_holo.png
+++ b/java/res/drawable-mdpi/sym_keyboard_tab_holo.png
Binary files differ
diff --git a/java/res/drawable-sw600dp-hdpi/sym_keyboard_delete_rtl_holo.png b/java/res/drawable-sw600dp-hdpi/sym_keyboard_delete_rtl_holo.png
deleted file mode 100644
index 5c19f32..0000000
--- a/java/res/drawable-sw600dp-hdpi/sym_keyboard_delete_rtl_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-mdpi/sym_keyboard_delete_rtl_holo.png b/java/res/drawable-sw600dp-mdpi/sym_keyboard_delete_rtl_holo.png
deleted file mode 100644
index 9948541..0000000
--- a/java/res/drawable-sw600dp-mdpi/sym_keyboard_delete_rtl_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-sw600dp-xhdpi/sym_keyboard_delete_rtl_holo.png b/java/res/drawable-sw600dp-xhdpi/sym_keyboard_delete_rtl_holo.png
deleted file mode 100644
index f3310a1..0000000
--- a/java/res/drawable-sw600dp-xhdpi/sym_keyboard_delete_rtl_holo.png
+++ /dev/null
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
index 58d1514..a0aa4ba 100644
--- a/java/res/drawable-xhdpi/keyboard_background_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_background_holo.9.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
index b40f766..4c27072 100644
--- a/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/more_suggestions_hint.png b/java/res/drawable-xhdpi/more_suggestions_hint.png
new file mode 100644
index 0000000..f577a36
--- /dev/null
+++ b/java/res/drawable-xhdpi/more_suggestions_hint.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
deleted file mode 100644
index 0aefaa1..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_123_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_delete_rtl.png b/java/res/drawable-xhdpi/sym_bkeyboard_delete_rtl.png
deleted file mode 100644
index 23aee2b..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_delete_rtl.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_label_mic.png b/java/res/drawable-xhdpi/sym_bkeyboard_label_mic.png
new file mode 100644
index 0000000..9bd1d65
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_bkeyboard_label_mic.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
deleted file mode 100644
index bcb0979..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_123_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_123_mic_holo.png b/java/res/drawable-xhdpi/sym_keyboard_123_mic_holo.png
deleted file mode 100644
index 0c75058..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_123_mic_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete_rtl.png b/java/res/drawable-xhdpi/sym_keyboard_delete_rtl.png
deleted file mode 100644
index 3f9d3dd..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_delete_rtl.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete_rtl_holo.png b/java/res/drawable-xhdpi/sym_keyboard_delete_rtl_holo.png
deleted file mode 100644
index 1f4890c..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_delete_rtl_holo.png
+++ /dev/null
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
deleted file mode 100644
index cee1056..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_feedback_space.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_label_mic.png b/java/res/drawable-xhdpi/sym_keyboard_label_mic.png
new file mode 100644
index 0000000..49810a0
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_label_mic.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-xhdpi/sym_keyboard_label_mic_holo.png
new file mode 100644
index 0000000..a7d3eaa
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_label_mic_holo.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
index 1666434..c32f8bb 100644
--- a/java/res/drawable-xhdpi/sym_keyboard_tab_holo.png
+++ b/java/res/drawable-xhdpi/sym_keyboard_tab_holo.png
Binary files differ
diff --git a/java/res/layout/candidates_strip.xml b/java/res/layout/candidates_strip.xml
index 269bc1b..bcc1322 100644
--- a/java/res/layout/candidates_strip.xml
+++ b/java/res/layout/candidates_strip.xml
@@ -29,31 +29,6 @@
         android:layout_width="0dp"
         android:layout_height="match_parent" />
     <LinearLayout
-        android:id="@+id/candidates_pane_control"
-        android:orientation="horizontal"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-    >
-        <TextView
-            android:id="@+id/expand_candidates_pane"
-            android:text="@string/label_expand_candidates_pane"
-            android:gravity="center"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:minWidth="28dp"
-            android:textSize="@dimen/candidate_text_size"
-            android:visibility="visible" />
-        <TextView
-            android:id="@+id/close_candidates_pane"
-            android:text="@string/label_close_candidates_pane"
-            android:gravity="center"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:minWidth="28dp"
-            android:textSize="@dimen/candidate_text_size"
-            android:visibility="gone" />
-    </LinearLayout>
-    <LinearLayout
         android:id="@+id/touch_to_save"
         android:orientation="horizontal"
         android:layout_width="match_parent"
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index 3a9567f..cbe6406 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <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>
@@ -51,20 +53,13 @@
     <string name="auto_cap" msgid="1719746674854628252">"Outohoofletters"</string>
     <!-- no translation found for configure_dictionaries_title (3758288002414557345) -->
     <skip />
-    <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>
@@ -84,6 +79,10 @@
     <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>
+    <!-- no translation found for label_to_symbol_key (8516904117128967293) -->
+    <skip />
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -187,6 +186,8 @@
     <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>
+    <!-- no translation found for configure_input_method (373356270290742459) -->
+    <skip />
     <string name="language_selection_title" msgid="1651299598555326750">"Invoertale"</string>
     <!-- no translation found for select_language (2573265881207142437) -->
     <skip />
@@ -197,62 +198,11 @@
     <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>
     <!-- outdated translation 437433231038683666 -->     <string name="keyboard_layout" msgid="8451164783510487501">"Sleutelbordtema"</string>
-    <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Tsjeggiese sleutelbord"</string>
-    <!-- no translation found for subtype_mode_ar_keyboard (2655338636329774995) -->
+    <!-- no translation found for subtype_de_qwerty (3358900499589259491) -->
     <skip />
-    <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"Deense sleutelbord"</string>
-    <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"Duitse sleutelbord"</string>
-    <!-- no translation found for subtype_mode_de_qwerty_keyboard (54890770769303956) -->
+    <!-- no translation found for subtype_en_GB (88170601942311355) -->
     <skip />
-    <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_tr_keyboard (3155981874829226370) -->
-    <skip />
-    <!-- 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) -->
+    <!-- no translation found for subtype_en_US (6160452336634534239) -->
     <skip />
     <!-- outdated translation 6937813623647419810 -->     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Bruikbaarheidsmodus"</string>
 </resources>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index 307fdc9..4d8ace5 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"በቁልፍመጫንጊዜ አንዝር"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"በቁልፍ መጫን ላይ የሚወጣ ድምፅ"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"ቁልፍ ጫን ላይ ብቅ ባይ"</string>
@@ -51,20 +53,13 @@
     <string name="auto_cap" msgid="1719746674854628252">"ራስ-ሰር አቢይ ማድረግ"</string>
     <!-- no translation found for configure_dictionaries_title (3758288002414557345) -->
     <skip />
-    <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>
@@ -84,6 +79,10 @@
     <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>
+    <!-- no translation found for label_to_symbol_key (8516904117128967293) -->
+    <skip />
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"ተጨማሪ"</string>
     <string name="label_pause_key" msgid="181098308428035340">"ላፍታ አቁም"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"ቆይ"</string>
@@ -187,6 +186,8 @@
     <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>
+    <!-- no translation found for configure_input_method (373356270290742459) -->
+    <skip />
     <string name="language_selection_title" msgid="1651299598555326750">"ቋንቋዎች አግቤት"</string>
     <!-- no translation found for select_language (2573265881207142437) -->
     <skip />
@@ -197,62 +198,11 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"ቃላትን ለማስተካከል ንካ"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"የገቡ ቃላትን ለማስተካከል ንካ፣ አማራጮች ሲታዩብቻ"</string>
     <!-- outdated translation 437433231038683666 -->     <string name="keyboard_layout" msgid="8451164783510487501">"የቁልፍ ሰሌዳ ገጽታ"</string>
-    <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"የቼክ ቁልፍሰሌዳ"</string>
-    <!-- no translation found for subtype_mode_ar_keyboard (2655338636329774995) -->
+    <!-- no translation found for subtype_de_qwerty (3358900499589259491) -->
     <skip />
-    <string name="subtype_mode_da_keyboard" msgid="1243570804427922104">"የዳኒሽኛ ቁልፍሰሌዳ"</string>
-    <string name="subtype_mode_de_keyboard" msgid="1990979135959462145">"የጀርመንኛ ቁልፍሰሌዳ"</string>
-    <!-- no translation found for subtype_mode_de_qwerty_keyboard (54890770769303956) -->
+    <!-- no translation found for subtype_en_GB (88170601942311355) -->
     <skip />
-    <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_tr_keyboard (3155981874829226370) -->
-    <skip />
-    <!-- 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) -->
+    <!-- no translation found for subtype_en_US (6160452336634534239) -->
     <skip />
     <!-- outdated translation 6937813623647419810 -->     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"የተገልጋይነት ጥናት ሁነታ"</string>
 </resources>
diff --git a/java/res/values-ar/donottranslate-altchars.xml b/java/res/values-ar/donottranslate-altchars.xml
index b4c103d..0e4f265 100644
--- a/java/res/values-ar/donottranslate-altchars.xml
+++ b/java/res/values-ar/donottranslate-altchars.xml
@@ -31,9 +31,7 @@
          \u064b: ARABIC FATHATAN
          \u064d: ARABIC KASRATAN -->
     <string name="alternates_for_punctuation">"\u060c,\u061b,\u061f,!,:,-,/,\',\",\u0651,\u0652,\u064c,\u0640,\u064f,\u064e,\u0650,\u064b,\u064d"</string>
-    <string name="alternates_for_web_tab_punctuation">".,\u060c,\u061b,\u061f,!,:,-,/,\',\",\u0651,\u0652,\u064c,\u0640,\u064f,\u064e,\u0650,\u064b,\u064d"</string>
     <integer name="popup_keyboard_column_for_punctuation">9</integer>
-    <integer name="popup_keyboard_column_for_web_punctuation">10</integer>
     <string name="keyhintlabel_for_punctuation">\u064b</string>
     <string name="keylabel_for_symbols_1">"١"</string>
     <string name="keylabel_for_symbols_2">"٢"</string>
@@ -55,12 +53,21 @@
     <string name="alternates_for_symbols_8">8</string>
     <string name="alternates_for_symbols_9">9</string>
     <string name="alternates_for_symbols_0">0,ⁿ,∅</string>
-    <string name="keylabel_for_symbols_comma">\u060c</string>
+    <string name="keylabel_for_comma">\u060c</string>
+    <string name="keylabel_for_f1">\u060c</string>
     <string name="keylabel_for_symbols_question">\u061f</string>
     <string name="keylabel_for_symbols_semicolon">\u061b</string>
-    <string name="alternates_for_symbols_comma">,</string>
+    <!-- \u066a: ARABIC PERCENT SIGN -->
+    <string name="keylabel_for_symbols_percent">\u066a</string>
+    <string name="alternates_for_comma">,</string>
+    <string name="alternates_for_f1">,</string>
+    <!-- @icon/3 is iconSettingsKey -->
+    <string name="alternates_for_f1_settings">\\,,\@icon/3|\@integer/key_settings</string>
+    <!-- @icon/7 is iconTabKey -->
+    <string name="alternates_for_f1_navigate">\\,,\@icon/7|\@integer/key_tab</string>
     <string name="alternates_for_symbols_question">\?,¿</string>
     <string name="alternates_for_symbols_semicolon">;</string>
+    <string name="alternates_for_symbols_percent">%,‰</string>
     <string name="keylabel_for_apostrophe">"،"</string>
     <string name="keylabel_for_dash">"."</string>
     <string name="keyhintlabel_for_apostrophe">"؟"</string>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index f996543..4330813 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"اهتزاز عند الضغط على مفتاح"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"صوت عند الضغط على مفتاح"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"انبثاق عند الضغط على المفاتيح"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"استخدام الأسماء من جهات الاتصال للاقتراحات والتصحيحات"</string>
     <string name="auto_cap" msgid="1719746674854628252">"استخدام الأحرف الكبيرة تلقائيًا"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"تهيئة القواميس"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"إصلاحات سريعة"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"تصحيح الأخطاء المكتوبة الشائعة"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"عرض اقتراحات التصحيح"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"عرض الكلمات المقترحة أثناء الكتابة"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"عرض دومًا"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"إخفاء دومًا"</string>
     <string name="auto_correction" msgid="4979925752001319458">"التصحيح التلقائي"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"تؤدي المسافة والترقيم إلى تصحيح الكلمات المكتوبة بشكل غير صحيح"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"إيقاف"</string>
@@ -66,7 +62,10 @@
     <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">"أ ب ج د"</string>
+    <string name="label_to_alpha_key" msgid="4793983863798817523">"أ ب ج"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"المزيد"</string>
     <string name="label_pause_key" msgid="181098308428035340">"توقف مؤقت"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"انتظار"</string>
@@ -134,9 +133,9 @@
     <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="configure_input_method" msgid="373356270290742459">"تهيئة طرق الإدخال"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"لغات الإدخال"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"تحديد لغات الإدخال"</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>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"المس لتصحيح الكلمات"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"المس الكلمات التي تم إدخالها لتصحيحها، وذلك فقط عندما تكون الاقتراحات مرئية."</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"مظهر لوحة المفاتيح"</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_de_qwerty_keyboard" msgid="54890770769303956">"لوحة مفاتيح QWERTY ألمانية"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"لوحة المفاتيح الفنلندية"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"لوحة المفاتيح الكرواتية"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"لوحة المفاتيح المجرية"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"لوحة المفاتيح التركية"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"صوت أفريقاني"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"صوت تشيكي"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"صوت ألماني"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"صوت إنجليزي"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"صوت إسباني"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"صوت فرنسي"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"صوت إيطالي"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"صوت ياباني"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"صوت كوري"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"صوت هولندي"</string>
-    <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>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"صوت صيني، يوي"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"صوت صيني، الماندارين"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"صوت زولوي"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"الألمانية (QWERTY)"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"الإنجليزية (المملكة المتحدة)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"الإنجليزية (الولايات المتحدة)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"وضع دراسة سهولة الاستخدام"</string>
 </resources>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index e925563..cc958af 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Да вибрира при натискане на клавиш"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук при натискане на клавиш"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Изскачащ прозорец при натискане на клавиш"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Използване на имена от „Контакти“ за предложения и поправки"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Автоматично поставяне на главни букви"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Конфигуриране на речници"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Бързи корекции"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Коригира най-честите грешки при въвеждане"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Показване на предложения за поправка"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Показване на предложения, докато пишете"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Винаги да се показва"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Да се скрива винаги"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Автоко"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Клавишът за интервал и пунктуация авт. поправя сгрешени думи"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Изкл."</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Готово"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Изпращане"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"АБВ"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"Още"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Пауза"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Чака"</string>
@@ -134,9 +133,9 @@
     <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="configure_input_method" msgid="373356270290742459">"Конфигуриране на въвеждането"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Входни езици"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Избор на езици за въвеждане"</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>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Докоснете, за да поправите думите"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Докоснете въведените думи, за да ги поправите – само когато предложенията са видими"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Тема на клавиатурата"</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_de_qwerty_keyboard" msgid="54890770769303956">"Немска клавиатура „QWERTY“"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Финландска клавиатура"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Хърватска клавиатура"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Унгарска клавиатура"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Турска клавиатура"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"африкаанс, гласово"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"чешки, гласово"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"немски, гласово"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"английски, гласово"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"испански, гласово"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"френски, гласово"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"италиански, гласово"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"японски, гласово"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"корейски, гласово"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"нидерландски, гласово"</string>
-    <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>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"китайски, кантонски, гласово"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"китайски, мандарин, гласово"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"зулуски, гласово"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Немска „QWERTY“"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"английски (Великобритания)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"английски (САЩ)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим за изучаване на използваемостта"</string>
 </resources>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index c038f19..2c9fbfb 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Teclat Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Configuració del teclat d\'Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opcions d\'entrada"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibra en prémer tecles"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilitza els noms de Contactes per a suggeriments i correccions"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Majúscules automàtiques"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Configura diccionaris"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostra els suggeriments de correcció"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Mostra paraules suggerides mentre s\'escriu"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Amaga sempre"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Correcció automàtica"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Barra espaiadora i punt. correg. autom. paraules mal escrites"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactiva"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Fet"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Envia"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micro en tecl. símb."</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entr. veu desactiv."</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Selecciona el mètode d\'entrada"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Configuració de mètodes d\'entrada"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Idiomes d\'entrada"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Selecciona l\'idioma d\'entrada"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Torna a tocar per desar"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Diccionari disponible"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Activa els comentaris de l\'usuari"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Toca per corregir paraules"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Teclat alemany QWERTY"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Teclat finès"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Teclat croat"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Teclat hongarès"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Teclat turc"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Veu en afrikaans"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Veu txeca"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Veu alemanya"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Veu anglesa"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Veu espanyola"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Veu francesa"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Veu italiana"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Veu japonesa"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Veu coreana"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Veu holandesa"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Veu polonesa"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Veu portuguesa"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Veu russa"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Veu turca"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Veu xinesa (Yue)"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Veu xinesa (mandarí)"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Veu en zulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY alemany"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Anglès (Regne Unit)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Anglès (EUA)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode d\'estudi d\'usabilitat"</string>
 </resources>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 5807773..38417a0 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Klávesnice Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Nastavení klávesnice Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávání textu a dat"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Při stisku klávesy vibrovat"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Použít jména ze seznamu kontaktů k návrhům a opravám"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Velká písmena automaticky"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Konfigurovat slovníky"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Zobrazit návrhy oprav"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Zobrazovat navržená slova během psaní"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vždy zobrazovat"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Vždy skrývat"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Automatické opravy"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Stisknutím mezerníku a interpunkce se automaticky opravují chybně napsaná slova"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Vypnuto"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Hotovo"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Odeslat"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikr. na kláv. se symb."</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hlasový vstup vypnut"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Výběr metody zadávání dat"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Nakonfigurovat metody vstupu"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Vstupní jazyky"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Výběr jazyků vstupu"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Dalším dotykem slovo uložíte"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"K dispozici je slovník"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Aktivovat zasílání statistik užívání a zpráv o selhání"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Dotykem aktivovat opravy"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Německá klávesnice QWERTY"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Finská klávesnice"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Chorvatská klávesnice"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Maďarská klávesnice"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Turecká klávesnice"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Voice – afrikánština"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Voice – čeština"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Voice – němčina"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Voice – angličtina"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Voice – španělština"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Voice – francouzština"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Hlasový vstup – italština"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Voice – japonština"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Voice – korejština"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Hlasový vstup – holandština"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Voice – polština"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Voice – portugalština"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Voice – ruština"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Voice – turečtina"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Voice – čínština, kantonština"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Voice – čínština, mandarínština"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Voice – zulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"němčina (QWERTY)"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"angličtina (VB)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"angličtina (USA)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Režim studie použitelnosti"</string>
 </resources>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index c0dc9b7..97dc8a0 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Android-tastatur"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Indstillinger for Android-tastatur"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Indstillinger for input"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibration ved tastetryk"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Lyd ved tastetryk"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Popup ved tastetryk"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Brug navne fra Kontaktpersoner til forslag og rettelser"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Skriv aut. med stort"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Konfigurer ordbøger"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Hurtige løsninger"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Retter almindelige stavefejl"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Vis rettelsesforslag"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Vis ordforslag under indtastning"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vis altid"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Skjul altid"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Automatisk retning"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Mellemrumstast og tegnsætning retter automatisk forkerte ord"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Fra"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Udfør"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Send"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik. på symboltastatur"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Stemmeinput deaktiveret"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Vælg inputmetode"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Konfigurer inputmetoder"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Inputsprog"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Vælg inputsprog"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Tryk igen for at gemme"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Ordbog er tilgængelig"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Aktiver brugerfeedback"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tryk for at rette ord"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Tysk QWERTY-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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Finsk tastatur"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Kroatisk tastatur"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Ungarsk tastatur"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Tyrkisk tastatur"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Afrikaans stemme"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Tjekkisk stemme"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Tysk stemme"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Engelsk stemme"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Spansk stemme"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Fransk stemme"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Italiensk stemme"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Japansk stemme"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Koreansk stemme"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Hollandsk stemme"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Polsk stemme"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Portugisisk stemme"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Russisk stemme"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Tyrkisk stemme"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Kinesisk, Yue stemme"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Kinesisk, mandarin stemme"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"isiZulu stemme"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Tysk QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Engelsk (Storbritannien)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Engelsk (USA)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Tilstand for brugsstudie"</string>
 </resources>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 1076132..fdce3bb 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Android-Tastatur"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Android-Tastatureinstellungen"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Eingabeoptionen"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrieren b. Tastendruck"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Ton bei Tastendruck"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up bei Tastendruck"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen aus \"Kontakte\" als Vorschläge und Korrekturmöglichkeiten anzeigen"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschr."</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Wörterbücher konfigurieren"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Quick Fixes"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Korrigiert gängige Tippfehler"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Änderungsvorschläge anzeigen"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Vorgeschlagene Wörter während des Tippens anzeigen"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Immer anzeigen"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Immer ausblenden"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Autokorrektur"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Korrektur fehlerhafter Wörter durch Leertaste und Satzzeichen"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Aus"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Fertig"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Senden"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikro auf Symboltastatur"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Spracheingabe deaktiviert"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Eingabemethode auswählen"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Eingabemethoden konfigurieren"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Eingabesprachen"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Eingabesprachen auswählen"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Zum Speichern erneut berühren"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Wörterbuch verfügbar"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Nutzer-Feedback aktivieren"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Wortkorrektur"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Deutsche QWERTZ-Tastaturbelegung"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Finnische Tastatur"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Kroatische Tastatur"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Ungarische Tastatur"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Türkische Tastatur"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Stimme in Afrikaans"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Tschechische Sprache"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Deutsche Sprache"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Englische Stimme"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Spanische Sprache"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Französische Sprache"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Italienisch"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Japanische Sprache"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Koreanische Sprache"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Niederländisch"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Polnische Sprache"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Portugiesische Sprache"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Russische Sprache"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Türkische Sprache"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Chinesische Stimme (Yue)"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Chinesische Stimme (Mandarin)"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Stimme in isiZulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Deutsche QWERTZ-Tastaturbelegung"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Englisch (GB)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Englisch (USA)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modus der Studie zur Benutzerfreundlichkeit"</string>
 </resources>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 544a834..2629c24 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Δόνηση κατά το πάτημα πλήκτρων"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Ήχος κατά το πάτημα πλήκτρων"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Εμφάνιση με το πάτημα πλήκτρου"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Χρησιμοποιήστε ονόματα από τις Επαφές για προτάσεις και διορθ."</string>
     <string name="auto_cap" msgid="1719746674854628252">"Αυτόματη χρήση κεφαλαίων"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Διαμόρφωση λεξικών"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Γρήγορες διορθώσεις"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Διορθώνει συνηθισμένα λάθη πληκτρολόγησης"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Εμφάνιση προτάσεων διόρθωσης"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Προβολή προτεινόμενων λέξεων κατά την πληκτρολόγηση"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Να εμφανίζεται πάντα"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Πάντα απόκρυψη"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Αυτόματη διόρθωση"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Τα πλήκτρα διαστήματος και στίξης διορθ. αυτόμ. λάθος λέξεις"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Απενεργοποίηση"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Τέλος"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Αποστολή"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ΑΒΓ"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">";123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"Περισσότερα"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Παύση"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Αναμ."</string>
@@ -134,9 +133,9 @@
     <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="configure_input_method" msgid="373356270290742459">"Διαμόρφωση μεθόδων εισαγωγής"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Γλώσσες εισόδου"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Επιλογή γλωσσών εισαγωγής"</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>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Αγγίξτε για διόρθωση λέξεων"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Αγγίξτε τις λέξες για να τις διορθώσετε, μόνο όταν οι προτάσεις είναι ορατές"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Θέμα πληκτρολογίου"</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_de_qwerty_keyboard" msgid="54890770769303956">"Γερμανικό πληκτρολόγιο QWERTY"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Πληκτρ. με φινλανδικούς χαρακτήρες"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Πληκτρολ. με κροατικούς χαρακτήρες"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Πληκτρολ. με ουγγρικούς χαρακτήρες"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Πληκτρολόγιο με τουρκικούς χαρακτήρες"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Αφρικάανς"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Τσεχικά"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Γερμανικά"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Αγγλικά"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Ισπανικά"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Γαλλικά"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Φωνητικές εντολές στα Ιταλικά"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Ιαπωνικά"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Κορεατικά"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Φωνητικές εντολές στα Ολλανδικά"</string>
-    <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>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Κινεζικά, Γιούε"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Κινεζικά, Μανδαρινικά"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Ζουλού"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Γερμανικά QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Αγγλικά (ΗΒ)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Αγγλικά (ΗΠΑ)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Λειτουργία μελέτης χρηστικότητας"</string>
 </resources>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index bb3588b..7a0449c 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Android keyboard"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Android keyboard settings"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Input options"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrate on key-press"</string>
     <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>
@@ -39,18 +41,12 @@
     <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="configure_dictionaries_title" msgid="3758288002414557345">"Configure dictionaries"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Quick fixes"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Corrects commonly typed mistakes"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Show correction suggestions"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Display suggested words while typing"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Always hide"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Auto-correction"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Spacebar and punctuation correct mistyped words automatically"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Off"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Done"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Send"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,6 +133,7 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic on symbols keyboard"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Voice input is disabled"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Select input method"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Configure input methods"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Input languages"</string>
     <string name="select_language" msgid="2573265881207142437">"Select input languages"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Touch again to save"</string>
@@ -143,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Touch to correct words"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"German QWERTY 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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Turkish keyboard"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Afrikaans Voice"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Czech Voice"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"German Voice"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"English Voice"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Spanish Voice"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"French Voice"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Italian Voice"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Japanese Voice"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Korean Voice"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Dutch Voice"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Polish Voice"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Portuguese Voice"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Russian Voice"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Turkish Voice"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Chinese, Yue Voice"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Chinese, Mandarin Voice"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"isiZulu Voice"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"German QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"English (UK)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"English (US)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Usability study mode"</string>
 </resources>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index f5311b8..4eacee9 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Configuración de teclado de Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opciones de entrada"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar teclas"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Usar nombres de los contactos para sugerencias y correcciones"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Configurar diccionarios"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Arreglos rápidos"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Corrige errores de escritura comunes"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostrar sugerencias de correcciones"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Mostrar palabras sugeridas al escribir"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar siempre"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Ocultar siempre"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Corrección automática"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"La barra espaciadora y puntuación insertan automáticamente las palabras corregidas"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactivado"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Hecho"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Enviar"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micrófono en el teclado de símbolos"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"La entrada por voz está inhabilitada"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Seleccionar método de entrada"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de entrada"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Seleccionar idiomas de entrada"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Tocar de nuevo para guardar"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Diccionario disponible"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Habilitar los comentarios del usuario"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tocar para corregir palabras"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Teclado QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Teclado finlandés"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Teclado croata"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Teclado húngaro"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Teclado turco"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Voz en Afrikaans"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Voz en checo"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Voz en alemán"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Voz en inglés"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Voz en español"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Voz en francés"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Voz italiana"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Voz en japonés"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Voz en coreano"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Voz holandesa"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Voz en polaco"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Voz en portugués"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Voz en ruso"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Voz en turco"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Voz en Yue, chino"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Voz en mandarín, chino"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Voz en isiZulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY alemán"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Inglés (Reino Unido)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Inglés (EE.UU.)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudio de usabilidad"</string>
 </resources>
diff --git a/java/res/values-es/donottranslate-altchars.xml b/java/res/values-es/donottranslate-altchars.xml
index 16111ec..e1f849f 100644
--- a/java/res/values-es/donottranslate-altchars.xml
+++ b/java/res/values-es/donottranslate-altchars.xml
@@ -26,5 +26,4 @@
     <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 2298e55..aae38ca 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Ajustes del teclado de Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opciones introducción texto"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar tecla"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Sonido al pulsar tecla"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Popup al pulsar tecla"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizar nombres de contactos para sugerencias y correcciones"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Configurar diccionarios"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostrar sugerencias de correcciones"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Muestra las palabras sugeridas mientras se escribe."</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar siempre"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Ocultar siempre"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Autocorrección"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Espacio y punt para corregir errores"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactivada"</string>
@@ -64,9 +60,12 @@
     <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>
-    <string name="label_done_key" msgid="2441578748772529288">"Listo"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Ok"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Enviar"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,6 +133,7 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micro en teclado de símbolos"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entrada de voz inhabilitada"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Seleccionar método de introducción de texto"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de introducción"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Idiomas"</string>
     <string name="select_language" msgid="2573265881207142437">"Seleccionar idiomas de entrada"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Volver a tocar para guardar"</string>
@@ -143,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tocar para corregir palabras"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Teclado QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Teclado finlandés"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Teclado croata"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Teclado húngaro"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Teclado turco"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Voz afrikáans"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Google Voice en checo"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Google Voice en alemán"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Voz inglesa"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Google Voice en español"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Google Voice en francés"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Voz italiana"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Google Voice en japonés"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Google Voice en coreano"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Voz holandesa"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Google Voice en polaco"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Google Voice en portugués"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Google Voice en ruso"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Google Voice en turco"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Voz china (cantonés)"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Voz china (mandarín)"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Voz zulú"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY alemán"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"inglés (Reino Unido)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"inglés (EE.UU.)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudio de usabilidad"</string>
 </resources>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index 3dccfb4..bacc180 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"لرزش با فشار کلید"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"صدا با فشار کلید"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"بازشو با فشار کلید"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"برای پیشنهاد و تصحیح از نام مخاطبین استفاده شود"</string>
     <string name="auto_cap" msgid="1719746674854628252">"نوشتن با حروف بزرگ خودکار"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"پیکربندی لغت نامه ها"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"راه حل های سریع"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"تصحیح خطاهای تایپی رایج"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"نمایش پیشنهادات تصحیح"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"نمایش واژه های پیشنهادی در حین تایپ"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"همیشه نمایش داده شود"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"همیشه پنهان"</string>
     <string name="auto_correction" msgid="4979925752001319458">"تصحیح خودکار"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"کلید فاصله و علائم نگارشی به صورت خودکار کلماتی را که غلط تایپ شده اند تصحیح می کنند"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"خاموش"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"انجام شد"</string>
     <string name="label_send_key" msgid="2815056534433717444">"ارسال"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"بیشتر"</string>
     <string name="label_pause_key" msgid="181098308428035340">"توقف موقت"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"منتظر بمانید"</string>
@@ -134,9 +133,9 @@
     <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="configure_input_method" msgid="373356270290742459">"پیکربندی روش های ورودی"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"زبان های ورودی"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"انتخاب زبان های ورودی"</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>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"برای تصحیح کلمات لمس کنید"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"فقط هنگامی که پیشنهادات قابل مشاهده هستند، برای تصحیح کلمات وارد شده آنها را لمس کنید"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"طرح زمینه صفحه کلید"</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_de_qwerty_keyboard" msgid="54890770769303956">"صفحه کلید QWERTY آلمانی"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"صفحه کلید فنلاندی"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"صفحه کلید کرواتی"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"صفحه کلید مجارستانی"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"صفحه کلید ترکی"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"صدای آفریکانس"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"صدای چک"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"صدای آلمانی"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"صدای انگلیسی"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"صدای اسپانیایی"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"صدای فرانسوی"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"صدای ایتالیایی"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"صدای ژاپنی"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"صدای کره ای"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"صدای هلندی"</string>
-    <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>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"صدای چینی، یوئه"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"صدای چینی، ماندارین"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"صدای ایزی زولو"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"آلمانی QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"انگیسی (انگلستان)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"انگیسی (US)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"حالت بررسی قابلیت استفاده"</string>
 </resources>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index bd9fc1c..1b45be7 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Android-näppäimistö"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Android-näppäimistön asetukset"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Syöttövalinnat"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Käytä värinää näppäimiä painettaessa"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Toista ääni näppäimiä painettaessa"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Ponnahdusikkuna painalluksella"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Käytä yhteystietojen nimiä ehdotuksissa ja korjauksissa"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automaattiset isot kirjaimet"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Määritä sanakirjat"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Pikakorjaukset"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Korjaa yleiset kirjoitusvirheet"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Näytä korjausehdotukset"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Näytä sanaehdotukset kirjoitettaessa"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Näytä aina"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Piilota aina"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Autom. korjaus"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Välilyönnit ja välimerkit korjaavat väärinkirjoitetut sanat automaattisesti"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Älä käytä"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Valmis"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Lähetä"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikr. symbolinäppäim."</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Ääniohjaus on pois käytöstä"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Valitse syöttötapa"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Määritä syöttötavat"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Syöttökielet"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Valitse syöttökielet"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Tallenna koskettamalla uudelleen"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Sanakirja saatavilla"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Ota käyttäjäpalaute käyttöön"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Korjaa sanoja koskettamalla"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Korjaa annetut sanat napauttamalla. (Vain, kun ehdotuksia on näkyvillä.)"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Näppäimistö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_de_qwerty_keyboard" msgid="54890770769303956">"Saksalainen QWERTY-näppäimistö"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Näppäimistö: suomi"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Näppäimistö: kroatia"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Näppäimistö: unkari"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Näppäimistö: turkki"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Ääni: afrikaans"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Ääni: tšekki"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Ääni: saksa"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Ääni: englanti"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Ääni: espanja"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Ääni: ranska"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"italia (ääni)"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Ääni: japani"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Ääni: korea"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"hollanti (ääni)"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Ääni: puola"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Ääni: portugali"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Ääni: venäjä"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Ääni: turkki"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Ääni: kiina, yue"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Ääni: mandariinikiina"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Ääni: isiZulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"saksa, QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"englanti (UK)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"englanti (USA)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Käytettävyystutkimustila"</string>
 </resources>
diff --git a/java/res/values-fr/donottranslate.xml b/java/res/values-fr/donottranslate.xml
index 09c37e3..695750f 100644
--- a/java/res/values-fr/donottranslate.xml
+++ b/java/res/values-fr/donottranslate.xml
@@ -25,5 +25,5 @@
     <!-- 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>
+    <string name="symbols_excluded_from_word_separators">\u0027</string>
 </resources>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index f54b27f..e658da0 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Clavier Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Paramètres du clavier Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Options de saisie"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer à chaque touche"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Son à chaque touche"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Agrandir les caractères"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utiliser des noms de contacts pour les suggestions et corrections"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Majuscules auto"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Configurer les dictionnaires"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Corrections rapides"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Corrige les fautes de frappe courantes"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Afficher les suggestions de correction"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Afficher les suggestions de terme lors de la saisie"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Toujours afficher"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Toujours masquer"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Correction auto."</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Corriger autom. orthographe (pression sur barre espace/signes ponctuation)"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Désactiver"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"OK"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Envoyer"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micro sur clavier symboles"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Saisie vocale désactivée"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Sélectionner un mode de saisie."</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Configurer les modes de saisie"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Langues de saisie"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Sélectionner les langues de saisie"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Appuyer de nouveau pour enregistrer"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Dictionnaire disponible"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Autoriser les commentaires des utilisateurs"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Appuyer pour corriger"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Clavier QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Clavier finnois"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Clavier croate"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Clavier hongrois"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Clavier turc"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Voix parlant afrikaans"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Voix tchèque"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Voix allemande"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Voix parlant anglais"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Voix espagnole"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Voix française"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Voix parlant italien"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Voix japonaise"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Voix coréenne"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Voix parlant néerlandais"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Voix polonaise"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Voix portugaise"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Voix russe"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Voix turque"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Voix parlant chinois (cantonais)"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Voix parlant chinois (mandarin)"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Voix parlant zoulou"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Clavier QWERTY allemand"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Anglais (Royaume-Uni)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Anglais (États-Unis)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode d\'étude de l\'utilisabilité"</string>
 </resources>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index d22432a..55d4b14 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Android tipkovnica"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Postavke tipkovnice za Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opcije ulaza"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibracija pri pritisku na tipku"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Upotreba imena iz Kontakata za prijedloge i ispravke"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automatsko pisanje velikih slova"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Konfiguracija rječnika"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Pokaži prijedloge ispravka"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Prikazivanje predloženih riječi prilikom upisivanja"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Uvijek prikaži"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Uvijek sakrij"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Samoispravak"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Razm. i intrp. aut. ispr. kr. rči."</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Isključeno"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Gotovo"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Pošalji"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik. na tipk. simb."</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Glas. unos onemog."</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Odabir ulazne metode"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Konfiguriraj načine ulaza"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Jezici unosa"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Odaberite jezike unosa"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Dodirnite opet za spremanje"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Rječnik je dostupan"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Omogući korisničke povratne informacije"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Dodirnite za ispravak riječi"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Njemačka QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Finska tipkovnica"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Hrvatska tipkovnica"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Mađarska tipkovnica"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Turska tipkovnica"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"afrikaans glasovno"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Češki glasovni"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Njemački glasovni"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"engleski glasovno"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Španjolski glas"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Francuski glas"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Talijanski glas"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Japanski glas"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Korejski glas"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Nizozemski glas"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Poljski glas"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Portugalski glas"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Ruski glas"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Turski glas"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"kineski, Yue glasovno"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"kineski, mandarinski glasovno"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"isiZulu glasovno"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"njemački QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"engleski (Velika Britanija)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"engleski (SAD)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Način studije upotrebljivosti"</string>
 </resources>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 7411923..d682b82 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Android-billentyűzet"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Android billentyűzetbeállítások"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Beviteli beállítások"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Rezgés billentyű megnyomása esetén"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"A névjegyek használata a javaslatokhoz és javításokhoz"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automatikusan nagy kezdőbetű"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Szótárak konfigurálása"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Javítási ajánlások megjelenítése"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"A javasolt szavak megjelenítése gépelés közben"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mindig látszik"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Mindig rejtve"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Automatikus javítás"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Szóköz és központozás automatikusan javítja az elgépelést"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Ki"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Kész"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Küldés"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,6 +133,7 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikr. a szimbólumoknál"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hangbevivel KI"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Beviteli mód kiválasztása"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Beviteli módok beállítása"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Beviteli nyelvek"</string>
     <string name="select_language" msgid="2573265881207142437">"Válassza ki a beviteli nyelveket"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Érintse meg újra a mentéshez"</string>
@@ -143,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Javítás a szavak megérintésével"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Német QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Finn billentyűzet"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Horvát billentyűzet"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Magyar billentyűzet"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Török billentyűzet"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Afrikaans hang"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Cseh hang"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Német hang"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Angol hang"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Spanyol hang"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Francia hang"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Olasz hang"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Japán hang"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Koreai hang"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Holland hang"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Lengyel hang"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Portugál hang"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Orosz hang"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Török hang"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Kínai (jüe) hang"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Kínai (mandarin) hang"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"isiZulu hang"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Német QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"angol (brit)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"angol (amerikai)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Használhatósági teszt"</string>
 </resources>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index 6cdf857..d8bb604 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Keyboard Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Setelan keyboard Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opsi masukan"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Getar jika tombol ditekan"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Berbunyi jika tombol ditekan"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Muncul saat tombol ditekan"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Menggunakan nama dari Kenalan untuk saran dan koreksi"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Kapitalisasi otomatis"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Konfigurasikan kamus"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Perbaikan cepat"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Memperbaiki kesalahan ketik umum"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Tampilkan saran koreksi"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Tampilkan kata yang disarankan ketika mengetik"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Selalu tampilkan"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Selalu sembunyikan"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Koreksi otomatis"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Bilah spasi dan tanda baca secara otomatis dikoreksi pada kata yang salah ketik"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Mati"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Selesai"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Kirimkan"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik pada keyboard simbol"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Masukan suara dinonaktifkan"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Pilih metode masukan"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Konfigurasi metode masukan"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Bahasa masukan"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Pilih bahasa masukan"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Sentuh sekali lagi untuk menyimpan"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Kamus yang tersedia"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Aktifkan umpan balik pengguna"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Sentuh untuk memperbaiki kata"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Keyboard QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Keyboard Suomi"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Keyboard Kroat"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Keyboard Magyar"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Keyboard Bahasa Turki"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Suara Bahasa Afrika"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Suara Bahasa Cheska"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Suara Bahasa Jerman"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Suara Bahasa Inggris"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Suara Bahasa Spanyol"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Suara Bahasa Prancis"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Suara Bahasa Italia"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Suara Bahasa Jepang"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Suara Bahasa Korea"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Suara Bahasa Belanda"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Suara Bahasa Polandia"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Suara Bahasa Portugis"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Suara Bahasa Rusia"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Suara Bahasa Turki"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Suara Bahasa China, Yue"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Suara Bahasa China, Mandarin"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Suara Zulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY Jerman"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Inggris (UK)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Inggris (AS)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modus studi daya guna"</string>
 </resources>
diff --git a/java/res/values-it/donottranslate.xml b/java/res/values-it/donottranslate.xml
index adb2a9a..58e9436 100644
--- a/java/res/values-it/donottranslate.xml
+++ b/java/res/values-it/donottranslate.xml
@@ -19,5 +19,5 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Symbols that do NOT separate words -->
-    <string name="non_word_separator_symbols"></string>
+    <string name="symbols_excluded_from_word_separators"></string>
 </resources>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 360b92f..d122f50 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -23,6 +23,7 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Tastiera Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Impostazioni tastiera Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opzioni inserimento"</string>
+    <string name="spell_checker_service_name" msgid="2003013122022285508">"Correzione Android"</string>
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrazione tasti"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Suono tasti"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Popup sui tasti"</string>
@@ -39,18 +40,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizza nomi di Contatti per suggerimenti e correzioni"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Maiuscole automatiche"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Configura dizionari"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostra suggerimenti correzioni"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Visualizza le parole suggerite durante la digitazione"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Nascondi sempre"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Correzione automatica"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Barra spaziatrice/punteggiatura correggono parole con errori"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Off"</string>
@@ -67,6 +62,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Fine"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Invia"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,6 +132,7 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic su tastiera simboli"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Comandi vocali disatt."</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Seleziona metodo di inserimento"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Configura metodi di immissione"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Lingue comandi"</string>
     <string name="select_language" msgid="2573265881207142437">"Seleziona lingue di immissione"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Tocca di nuovo per salvare"</string>
@@ -143,46 +142,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tocca per correggere"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Tastiera QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Tastiera finlandese"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Tastiera croata"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Tastiera ungherese"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Tastiera turca"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Voce afrikaans"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Voce ceca"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Voce tedesca"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Voce inglese"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Voce spagnola"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Voce francese"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Voce italiana"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Voce giapponese"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Voce coreana"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Voce olandese"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Voce polacca"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Voce portoghese"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Voce russa"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Voce turca"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Voce cinese Yue"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Voce cinese mandarino"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Voce isiZulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY tedesca"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Inglese (UK)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Inglese (USA)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modalità Studio sull\'usabilità"</string>
 </resources>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index 93da028..b490913 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"רטט עם לחיצה על מקשים"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"צלילים עם לחיצה על מקשים"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"חלון קופץ עם לחיצה על מקשים"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"השתמש בשמות מאנשי קשר עבור הצעות ותיקונים"</string>
     <string name="auto_cap" msgid="1719746674854628252">"הפיכה אוטומטית של אותיות לרישיות"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"הגדרת מילונים"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"תיקונים מהירים"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"מתקן שגיאות הקלדה נפוצות"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"הצג הצעות לתיקונים"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"הצג הצעות למילים בעת הקלדה"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"הצג תמיד"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"הסתר תמיד"</string>
     <string name="auto_correction" msgid="4979925752001319458">"תיקון אוטומטי"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"מקש הרווח ופיסוק מתקנים אוטומטית שגיאות הקלדה"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"כבוי"</string>
@@ -66,7 +62,10 @@
     <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_to_alpha_key" msgid="4793983863798817523">"אבג"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"עוד"</string>
     <string name="label_pause_key" msgid="181098308428035340">"השהה"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"המתן"</string>
@@ -134,9 +133,9 @@
     <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="configure_input_method" msgid="373356270290742459">"הגדרת שיטות קלט"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"שפות קלט"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"בחר שפות קלט"</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>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"גע כדי לתקן מילים"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"גע במילים שהוזנו כדי לתקן אותן, רק כאשר הצעות מוצגות"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"עיצוב מקלדת"</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_de_qwerty_keyboard" msgid="54890770769303956">"מקלדת QWERTY גרמנית"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"מקלדת פינית"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"מקלדת קרואטית"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"מקלדת הונגרית"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"מקלדת טורקית"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Google Voice באפריקאנס"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Google Voice צ\'כי"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Google Voice גרמני"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Google Voice באנגלית"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Google Voice ספרדי"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Google Voice צרפתי"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"קול באיטלקית"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Google Voice יפני"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Google Voice קוריאני"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"קול בהולנדית"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Google Voice פולני"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Google Voice פורטוגזי"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Google Voice רוסי"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Google Voice תורכי"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Google Voice בסינית, יו"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Google Voice בסינית, מנדרינית"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Google Voice באיסיזולו"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"מקלדת QWERTY גרמנית"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"אנגלית (בריטניה)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"אנגלית (ארה\"ב)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"מצב מחקר שימושיות"</string>
 </resources>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 3f819ce..8ac407a 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"キー操作バイブ"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"キー操作音"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"キー押下時ポップアップ"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"連絡先の名前を使用して候補表示や自動修正を行います"</string>
     <string name="auto_cap" msgid="1719746674854628252">"自動大文字変換"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"辞書を設定"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"クイックフィックス"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"よくある誤字・脱字を修正します"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"修正候補を表示する"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"入力中に入力候補を表示する"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"常に表示"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"常に非表示"</string>
     <string name="auto_correction" msgid="4979925752001319458">"自動修正"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"誤入力をスペースまたは句読点キーで修正する"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"OFF"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"完了"</string>
     <string name="label_send_key" msgid="2815056534433717444">"送信"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <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="configure_input_method" msgid="373356270290742459">"入力方法の設定"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"入力言語"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"入力言語の選択"</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>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"タップして語句を修正"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"候補が表示されているときのみ、入力した語句をタップして修正する"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"キーボードのテーマ"</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_de_qwerty_keyboard" msgid="54890770769303956">"ドイツ語QWERTYキーボード"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"フィンランド語キーボード"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"クロアチア語キーボード"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"ハンガリー語キーボード"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"トルコ語のキーボード"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"アフリカーンス語の音声"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"チェコ語の音声"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"ドイツ語の音声"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"英語の音声"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"スペイン語の音声"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"フランス語の音声"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"イタリア語の音声"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"日本語の音声"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"韓国語の音声"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"オランダ語の音声"</string>
-    <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>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"中国語(広東語)の音声"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"中国語(標準語)の音声"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"ズールー語の音声"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"ドイツ語QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"英語(英国)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"英語(米国)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"使いやすさの研究モード"</string>
 </resources>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index e9c2e4c..c9000d1 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"키를 누를 때 진동 발생"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"키를 누를 때 소리 발생"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"키를 누를 때 팝업"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"추천 및 수정에 주소록의 이름 사용"</string>
     <string name="auto_cap" msgid="1719746674854628252">"자동 대문자화"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"사전 설정"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"빠른 수정"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"자주 발생하는 오타를 수정합니다."</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"수정 제안 표시"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"글자를 입력하는 동안 추천 단어 표시"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"항상 표시"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"항상 숨기기"</string>
     <string name="auto_correction" msgid="4979925752001319458">"자동 수정"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"스페이스바와 문장부호 키를 사용하면 오타가 자동으로 교정됩니다."</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"사용 안함"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"완료"</string>
     <string name="label_send_key" msgid="2815056534433717444">"전송"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"더보기"</string>
     <string name="label_pause_key" msgid="181098308428035340">"일시 중지"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"대기"</string>
@@ -134,9 +133,9 @@
     <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="configure_input_method" msgid="373356270290742459">"입력 방법 설정"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"입력 언어"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"입력 언어 선택"</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>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"터치하여 단어 수정"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"입력한 단어를 터치하여 수정(추천 단어가 표시되는 경우에만)"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"키보드 테마"</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_de_qwerty_keyboard" msgid="54890770769303956">"독일어 QWERTY 키보드"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"핀란드어 키보드"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"크로아티아어 키보드"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"헝가리어 키보드"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"터키어 키보드"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"아프리칸스어 음성"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"체코어 음성"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"독일어 음성"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"영어 음성"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"스페인어 음성"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"프랑스어 음성"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"이탈리아어 음성"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"일본어 키보드"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"한국어 음성"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"네덜란드어 음성"</string>
-    <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>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"중국어, 광둥어 음성"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"중국어, 북경어 음성"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"줄루어 음성"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"독일어 QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"영어(영국)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"영어(미국)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"가용성 연구 모드"</string>
 </resources>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index 191b018..4113ef3 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"„Android“ klaviatūra"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"„Android“ klaviatūros nustatymai"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Įvesties parinktys"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibruoti, kai paspaudžiami klavišai"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Klavišo paspaudimo garsas"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Iššoka paspaudus klavišą"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Siūlant ir taisant naudoti vardus iš „Kontaktų“"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automatinis didžiųjų raidžių rašymas"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Konfigūruoti žodynus"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Rodyti taisymo pasiūlymus"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Įvedant tekstą pateikti siūlomų žodžių"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Visada rodyti"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Visada slėpti"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Automatinis taisymas"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Tarpo kl. ir skyr. ženkl. aut. išt. neteis. įv. žodž."</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Išjungta"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Atlikta"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Siųsti"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrof. simb. klav."</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Balso įv. neleidž."</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Pasirinkti įvesties metodą"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Konfigūruoti įvesties metodus"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Įvesties kalbos"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Pasirinkite įvesties kalbas"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Kad išsaugotumėte, dar kartą palieskite"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Žodynas galimas"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Įgalinti naudotojų atsiliepimus"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Jei norite ištais. žodž., paliesk."</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Vokiška QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Suomiška klaviatūra"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Kroatiška klaviatūra"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Vengriška klaviatūra"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Turkiška klaviatūra"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"„Voice“ afrikanų k."</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"„Voice“ čekų k."</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"„Voice“ vokiečių k."</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"„Voice“ anglų k."</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"„Voice“ ispanų k."</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"„Voice“ prancūzų k."</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"„Voice“ italų k."</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"„Voice“ japonų k."</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"„Voice“ korėjiečių k."</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"„Voice“ olandų k."</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"„Voice“ lenkų k."</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"„Voice“ portugalų k."</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"„Voice“ rusų k."</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"„Voice“ turkų k."</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"„Voice“ kinų (dziue) k."</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"„Voice“ kinų (mandarinų) k."</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"„Voice“ zulų k."</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY klaviatūra vokiečių k."</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Anglų k. (JK)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Anglų k. (JAV)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Tinkamumo tyrimo režimas"</string>
 </resources>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index 8a77d00..e5da1dc 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Android tastatūra"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Android tastatūras iestatījumi"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Ievades opcijas"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrēt, nospiežot taustiņu"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Izmantot kontaktpersonu vārdus kā ieteikumus un labojumus"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automātiska lielo burtu lietošana"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Konfigurēt vārdnīcas"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Rādīt labojumu ieteikumus"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Ievades laikā attēlot ieteiktos vārdus"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vienmēr rādīt"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Vienmēr slēpt"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Automāt. korekcija"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Atstarpes taustiņš un interpunkcija; automātiska kļūdainu vārdu labošana"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Izslēgta"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Gatavs"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Sūtīt"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,6 +133,7 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikr.uz simb.tastat."</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Balss iev. atspējota"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Atlasīt ievades metodi"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Ievades metožu konfigurēšana"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Ievades valodas"</string>
     <string name="select_language" msgid="2573265881207142437">"Atlasīt ievades valodas"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Pieskarieties vēlreiz, lai saglabātu"</string>
@@ -143,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Pieskarties, lai izlabotu vārdus"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Vācu QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Somu valodas tastatūra"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Horvātu valodas tastatūra"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Ungāru valodas tastatūra"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Turku valodas tastatūra"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Balss afrikandu valodā"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Voice čehu valodā"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Voice vācu valodā"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Balss angļu valodā"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Voice spāņu valodā"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Voice franču valodā"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Itāļu balss"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Voice japāņu valodā"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Voice korejiešu valodā"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Holandiešu balss"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Voice poļu valodā"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Voice portugāļu valodā"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Voice krievu valodā"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Voice turku valodā"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Balss ķīniešu val. (Kantonas dial.)"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Balss ķīniešu v. (mandarīnu dial.)"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Balss zulu valodā"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Vācu valodas QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Angļu valoda (Lielbritānija)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Angļu valoda (ASV)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Lietojamības izpētes režīms"</string>
 </resources>
diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml
index a0f0c20..63fd655 100644
--- a/java/res/values-ms/strings.xml
+++ b/java/res/values-ms/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <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>
@@ -30,34 +32,21 @@
     <string name="correction_category" msgid="2236750915056607613">"Pembetulan teks"</string>
     <string name="ngram_category" msgid="5337109164339320257">"Cadangan berdasarkan 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="advanced_settings" msgid="362895144495591463">"Tetapan terperinci"</string>
+    <string name="advanced_settings_summary" msgid="5193513161106637254">"Pilihan untuk pengguna pakar"</string>
+    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Pop tmbl knci ketpkn lengah"</string>
+    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Tiada lengah"</string>
+    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Lalai"</string>
+    <string name="use_contacts_dict" msgid="4435317977804180815">"Cadangkan nama Kenalan"</string>
+    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Menggunakan nama daripada Kenalan untuk cadangan dan pembetulan"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Huruf besar auto"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Konfigurasikan kamus"</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>
@@ -74,81 +63,49 @@
     <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>
+    <!-- no translation found for label_to_symbol_key (8516904117128967293) -->
+    <skip />
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
-    <!-- no translation found for spoken_current_text_is (2485723011272583845) -->
-    <skip />
-    <!-- no translation found for spoken_no_text_entered (7479685225597344496) -->
-    <skip />
-    <!-- no translation found for spoken_description_unknown (3197434010402179157) -->
-    <skip />
-    <!-- no translation found for spoken_description_shift (244197883292549308) -->
-    <skip />
-    <!-- no translation found for spoken_description_shift_shifted (954941524766465022) -->
-    <skip />
-    <!-- no translation found for spoken_description_caps_lock (5660626444912131764) -->
-    <skip />
-    <!-- no translation found for spoken_description_delete (8740376944276199801) -->
-    <skip />
-    <!-- no translation found for spoken_description_to_symbol (5486340107500448969) -->
-    <skip />
-    <!-- no translation found for spoken_description_to_alpha (23129338819771807) -->
-    <skip />
-    <!-- no translation found for spoken_description_to_numeric (591752092685161732) -->
-    <skip />
-    <!-- no translation found for spoken_description_settings (4627462689603838099) -->
-    <skip />
-    <!-- no translation found for spoken_description_tab (2667716002663482248) -->
-    <skip />
-    <!-- no translation found for spoken_description_space (2582521050049860859) -->
-    <skip />
-    <!-- no translation found for spoken_description_mic (615536748882611950) -->
-    <skip />
-    <!-- no translation found for spoken_description_smiley (2256309826200113918) -->
-    <skip />
-    <!-- no translation found for spoken_description_return (8178083177238315647) -->
-    <skip />
-    <!-- no translation found for spoken_description_comma (4970844442999724586) -->
-    <skip />
-    <!-- no translation found for spoken_description_period (5286614628077903945) -->
-    <skip />
-    <!-- no translation found for spoken_description_left_parenthesis (8524822120595052415) -->
-    <skip />
-    <!-- no translation found for spoken_description_right_parenthesis (1085757995851933164) -->
-    <skip />
-    <!-- no translation found for spoken_description_colon (4312420908484277077) -->
-    <skip />
-    <!-- no translation found for spoken_description_semicolon (37737920987155179) -->
-    <skip />
-    <!-- no translation found for spoken_description_exclamation_mark (2625684427460737157) -->
-    <skip />
-    <!-- no translation found for spoken_description_question_mark (7074097784255379666) -->
-    <skip />
-    <!-- no translation found for spoken_description_double_quote (5485320575389905967) -->
-    <skip />
-    <!-- no translation found for spoken_description_single_quote (4451320362665463938) -->
-    <skip />
-    <!-- no translation found for spoken_description_dot (40711082435231673) -->
-    <skip />
-    <!-- no translation found for spoken_description_square_root (190595160284757811) -->
-    <skip />
-    <!-- no translation found for spoken_description_pi (4554418247799952239) -->
-    <skip />
-    <!-- no translation found for spoken_description_delta (3607948313655721579) -->
-    <skip />
-    <!-- no translation found for spoken_description_trademark (475877774077871369) -->
-    <skip />
-    <!-- no translation found for spoken_description_care_of (7492800237237796530) -->
-    <skip />
-    <!-- no translation found for spoken_description_star (1009742725387231977) -->
-    <skip />
-    <!-- no translation found for spoken_description_pound (5530577649206922631) -->
-    <skip />
-    <!-- no translation found for spoken_description_ellipsis (1687670869947652062) -->
-    <skip />
-    <!-- no translation found for spoken_description_low_double_quote (3551394572784840975) -->
-    <skip />
+    <string name="spoken_current_text_is" msgid="2485723011272583845">"Teks semasa adalah %s"</string>
+    <string name="spoken_no_text_entered" msgid="7479685225597344496">"Tiada teks dimasukkan"</string>
+    <string name="spoken_description_unknown" msgid="3197434010402179157">"Kod kunci %d"</string>
+    <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
+    <string name="spoken_description_shift_shifted" msgid="954941524766465022">"Shift didayakan"</string>
+    <string name="spoken_description_caps_lock" msgid="5660626444912131764">"Kunci huruf besar didayakan"</string>
+    <string name="spoken_description_delete" msgid="8740376944276199801">"Padam"</string>
+    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"Simbol"</string>
+    <string name="spoken_description_to_alpha" msgid="23129338819771807">"Huruf"</string>
+    <string name="spoken_description_to_numeric" msgid="591752092685161732">"Nombor"</string>
+    <string name="spoken_description_settings" msgid="4627462689603838099">"Tetapan"</string>
+    <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
+    <string name="spoken_description_space" msgid="2582521050049860859">"Space"</string>
+    <string name="spoken_description_mic" msgid="615536748882611950">"Input suara"</string>
+    <string name="spoken_description_smiley" msgid="2256309826200113918">"Muka senyum"</string>
+    <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
+    <string name="spoken_description_comma" msgid="4970844442999724586">"Koma"</string>
+    <string name="spoken_description_period" msgid="5286614628077903945">"Tempoh"</string>
+    <string name="spoken_description_left_parenthesis" msgid="8524822120595052415">"Tanda kurung kiri"</string>
+    <string name="spoken_description_right_parenthesis" msgid="1085757995851933164">"Tanda kurung kanan"</string>
+    <string name="spoken_description_colon" msgid="4312420908484277077">"Titik bertindih"</string>
+    <string name="spoken_description_semicolon" msgid="37737920987155179">"Koma bertitik"</string>
+    <string name="spoken_description_exclamation_mark" msgid="2625684427460737157">"Tanda seru"</string>
+    <string name="spoken_description_question_mark" msgid="7074097784255379666">"Tanda soal"</string>
+    <string name="spoken_description_double_quote" msgid="5485320575389905967">"Tanda petikan berganda"</string>
+    <string name="spoken_description_single_quote" msgid="4451320362665463938">"Tanda petikan tunggal"</string>
+    <string name="spoken_description_dot" msgid="40711082435231673">"Titik"</string>
+    <string name="spoken_description_square_root" msgid="190595160284757811">"Punca kuasa dua"</string>
+    <string name="spoken_description_pi" msgid="4554418247799952239">"Pi"</string>
+    <string name="spoken_description_delta" msgid="3607948313655721579">"Delta"</string>
+    <string name="spoken_description_trademark" msgid="475877774077871369">"Tanda dagangan"</string>
+    <string name="spoken_description_care_of" msgid="7492800237237796530">"Dengan alamat"</string>
+    <string name="spoken_description_star" msgid="1009742725387231977">"Bintang"</string>
+    <string name="spoken_description_pound" msgid="5530577649206922631">"Paun"</string>
+    <string name="spoken_description_ellipsis" msgid="1687670869947652062">"Elipsis"</string>
+    <string name="spoken_description_low_double_quote" msgid="3551394572784840975">"Tanda petikan berganda rendah"</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 Privasi Mudah Alih"</a>" digunakan."</string>
@@ -177,9 +134,9 @@
     <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="configure_input_method" msgid="373356270290742459">"Konfigurasikan kaedah masukan"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Bahasa input"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Pilih bahasa input"</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>
@@ -187,53 +144,8 @@
     <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="8451164783510487501">"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>
-    <!-- no translation found for subtype_mode_de_qwerty_keyboard (54890770769303956) -->
-    <skip />
-    <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>
-    <!-- no translation found for subtype_mode_tr_keyboard (3155981874829226370) -->
-    <skip />
-    <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="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY Bahasa Jerman"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Bahasa Inggeris (UK)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Bahasa Inggeris (AS)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mod kajian kebolehgunaan"</string>
 </resources>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 4689507..e865822 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Skjermtastatur"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Innstillinger for skjermtastatur"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Inndataalternativer"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer ved tastetrykk"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Lyd ved tastetrykk"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Hurtigvindu ved tastetrykk"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Bruk navn fra Kontakter til forslag og korrigeringer"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Stor forbokstav"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Konfigurer ordbøker"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Autokorrektur"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Retter vanlige stavefeil"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Vis rettingsforslag"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Vis ordforslag under skriving"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vis alltid"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Skjul alltid"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Automatisk retting"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Mellomromstast og skilletegn retter automat. feilstavede ord"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Av"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Utfør"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Send"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofon på talltastatur"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Taleinndata er deaktiv."</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Velg inndatametode"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Konfigurer inndatametoder"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Inndataspråk"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Velg inndataspråk"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"Trykk på nytt for å lagre"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Ordbok tilgjengelig"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Aktiver brukertilbakemelding"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Trykk for å endre ord"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Tysk QWERTY-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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Finsk tastatur"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Kroatisk tastatur"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Ungarsk tastatur"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Tyrkisk tastatur"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Afrikaans tale"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Tsjekkisk tale"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Tysk tale"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Engelsk tale"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Spansk tale"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Fransk tale"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Italiensk stemme"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Japansk tale"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Koreansk tale"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Nederlandsk stemme"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Polsk tale"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Portugisisk tale"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Russisk tale"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Tyrkisk tale"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Kinesisk (yue) tale"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Kinesisk (mandarin) tale"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"isiZulu tale"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Tysk QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Engelsk (Storbritannia)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Engelsk (USA)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Nyttighetsmodus"</string>
 </resources>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index 8dac48c..ebb1c06 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Android-toetsenbord"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Instellingen voor Android-toetsenbord"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropties"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Trillen bij toetsaanslag"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Geluid bij toetsaanslag"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up bij toetsaanslag"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen uit Contacten gebruiken voor suggesties en correcties"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Auto-hoofdlettergebruik"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Woordenboeken configureren"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Snelle oplossingen"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Hiermee worden veelvoorkomende typefouten gecorrigeerd"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Correctievoorstellen weergeven"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Voorgestelde woorden weergeven tijdens typen"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Altijd weergeven"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Altijd verbergen"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Autocorrectie"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Met spatiebalk en interpunctie worden verkeerd gespelde woorden automatisch gecorrigeerd"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Uitgeschakeld"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Gereed"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Verzenden"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic op symb.toetsb."</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Spraakinvoer is uit"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Invoermethode selecteren"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Invoermethoden configureren"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Invoertalen"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Invoertalen selecteren"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Raak nogmaals aan om op te slaan"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Woordenboek beschikbaar"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Gebruikersfeedback inschakelen."</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Raak aan om woorden te corrigeren"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Duits QWERTY-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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Fins toetsenbord"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Kroatisch toetsenbord"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Hongaars toetsenbord"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Turks toetsenbord"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Afrikaanse stem"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Tsjechische stem"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Duitse stem"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Engelse stem"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Spaanse stem"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Franse stem"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Italiaanse stem"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Japanse stem"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Koreaanse stem"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Nederlandse stem"</string>
-    <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">"Russische stem"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Turkse stem"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Chinese stem (Yue)"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Chinese stem (Mandarijn)"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"isiZulu stem"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Duits QWERTY-toetsenbord"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Engels (Verenigd Koninkrijk)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Engels (Verenigde Staten)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modus voor gebruiksvriendelijkheidsonderzoek"</string>
 </resources>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index 3ff6bfc..1cf1416 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Klawiatura Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Ustawienia klawiatury Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opcje wprowadzania"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Wibracja przy naciśnięciu"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"W propozycjach i poprawkach użyj nazwisk z kontaktów"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Wstawiaj wielkie litery"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Konfiguruj słowniki"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Szybkie poprawki"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Poprawia częste błędy wpisywania"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Pokazuj propozycje poprawek"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Wyświetl proponowane słowa podczas wpisywania"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Zawsze pokazuj"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Zawsze ukrywaj"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Autokorekta"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Spacja i znaki przestankowe poprawiają błędnie wpisane słowa"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Wyłącz"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"OK"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Wyślij"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofon na klawiaturze z symbolami"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Wprowadzanie głosowe jest wyłączone"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Wybierz sposób wprowadzania tekstu"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Konfigurowanie metod wprowadzania"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Języki wprowadzania"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Wybierz języki wprowadzania"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Dotknij ponownie, aby zapisać"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Słownik dostępny"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Włącz przesyłanie opinii użytkownika"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Popraw dotknięte słowo"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Niemiecka klawiatura QWERTY"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Klawiatura fińska"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Klawiatura chorwacka"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Klawiatura węgierska"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Klawiatura turecka"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Mowa afrikaans"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Mowa czeska"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Mowa niemiecka"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Mowa angielska"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Mowa hiszpańska"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Mowa francuska"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Mowa włoska"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Mowa japońska"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Mowa koreańska"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Mowa holenderska"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Mowa polska"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Mowa portugalska"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Mowa rosyjska"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Mowa turecka"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Mowa chińska (kantoński)"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Mowa chińska (mandaryński)"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Mowa isiZulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Niemiecka QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Angielska (Wielka Brytania)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Angielska (USA)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Tryb badania przydatności"</string>
 </resources>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index 3add66c..22c99f2 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Teclado do Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Definições de teclado do Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opções de introdução"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao primir as teclas"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizar nomes dos Contactos para sugestões e correções"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Letras maiúsculas automáticas"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Configurar dicionários"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostrar sugestões de correcção"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Apresentar sugestões de palavras ao escrever"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar sempre"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Ocultar sempre"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Auto correcção"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Correcção automática de palavras mal escritas c/ barra de espaços e pontuação"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desligar"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Feito"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Enviar"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic. tecl. símbolos"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entr. voz desact."</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Seleccionar método de entrada"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de introdução"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Selecionar idiomas de entrada"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Toque novamente para guardar"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Dicionário disponível"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Activar comentários do utilizador"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tocar para corrigir palavras"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Teclado QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Teclado finlandês"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Teclado croata"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Teclado húngaro"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Teclado Turco"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Voz em africânder"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Voz checa"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Voz alemã"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Voz em inglês"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Voz espanhola"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Voz francesa"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Voz italiana"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Voz japonesa"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Voz coreana"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Voz holandesa"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Voz polaca"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Voz portuguesa"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Voz russa"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Voz turca"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Voz em yue, chinês"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Voz em mandarim, chinês"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Voz em isiZulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY Alemão"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Inglês (RU)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Inglês (EUA)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudo da capacidade de utilização"</string>
 </resources>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 38373e3..167ae0e 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Teclado Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Configurações de teclado Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opções de entrada"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao tocar a tecla"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Usar nomes dos Contatos para sugestões e correções"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Capitaliz. automática"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Configurar dicionários"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Exibir sugestões de correção"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Exibir sugestões de palavras durante a digitação"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar sempre"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Sempre ocultar"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Autocorreção"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"A barra de espaço e a pontuação corrigem automaticamente palavras com erro de digitação"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desativado"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Feito"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Enviar"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic. no teclado"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Texto por voz desat."</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Selecionar método de entrada"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de entrada"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Selecionar idiomas de entrada"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Toque novamente para salvar"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Dicionário disponível"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Ativar comentário do usuário"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tocar para corrigir"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Teclado alemão QWERTY"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Teclado finlandês"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Teclado croata"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Teclado húngaro"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Teclado turco"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Voz em africâner"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Voz em tcheco"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Voz em alemão"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Voz em inglês"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Voz em espanhol"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Voz em francês"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Voz italiana"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Voz em japonês"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Voz em coreano"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Voz holandesa"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Voz em polonês"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Voz em português"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Voz em russo"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Voz em turco"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Voz em chinês, cantonês"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Voz em chinês, mandarim"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Voz em zulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Alemão QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Inglês (Reino Unido)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Inglês (EUA)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudo de utilização"</string>
 </resources>
diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml
index d6d38ad..359dc7a 100644
--- a/java/res/values-rm/strings.xml
+++ b/java/res/values-rm/strings.xml
@@ -24,6 +24,8 @@
     <string name="english_ime_settings" msgid="6661589557206947774">"Parameters da la tastatura Android"</string>
     <!-- no translation found for english_ime_input_options (3909945612939668554) -->
     <skip />
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar cun smatgar in buttun"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Tun cun smatgar in buttun"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up cun smatgar ina tasta"</string>
@@ -52,8 +54,6 @@
     <string name="auto_cap" msgid="1719746674854628252">"Maiusclas automaticas"</string>
     <!-- no translation found for configure_dictionaries_title (3758288002414557345) -->
     <skip />
-    <string name="quick_fixes" msgid="5353213327680897927">"Correcturas sveltas"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Curregia sbagls da tippar currents"</string>
     <!-- no translation found for prefs_show_suggestions (8026799663445531637) -->
     <skip />
     <!-- no translation found for prefs_show_suggestions_summary (1583132279498502825) -->
@@ -64,16 +64,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) -->
-    <skip />
-    <!-- no translation found for settings_key_mode_always_show_name (3047567041784760575) -->
-    <skip />
-    <!-- no translation found for settings_key_mode_always_hide_name (7833948046716923994) -->
-    <skip />
     <!-- outdated translation 7911639788808958255 -->     <string name="auto_correction" msgid="4979925752001319458">"Propostas da pleds"</string>
     <!-- outdated translation 6881047311475758267 -->     <string name="auto_correction_summary" msgid="5625751551134658006">"Curreger automaticamain il pled precedent"</string>
     <!-- no translation found for auto_correction_threshold_mode_off (8470882665417944026) -->
@@ -97,6 +89,10 @@
     <string name="label_send_key" msgid="2815056534433717444">"Trametter"</string>
     <!-- no translation found for label_to_alpha_key (4793983863798817523) -->
     <skip />
+    <!-- no translation found for label_to_symbol_key (8516904117128967293) -->
+    <skip />
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <!-- no translation found for label_more_key (3760239494604948502) -->
     <skip />
     <!-- no translation found for label_pause_key (181098308428035340) -->
@@ -210,6 +206,8 @@
     <skip />
     <!-- no translation found for selectInputMethod (315076553378705821) -->
     <skip />
+    <!-- no translation found for configure_input_method (373356270290742459) -->
+    <skip />
     <string name="language_selection_title" msgid="1651299598555326750">"Linguas da cumonds vocals"</string>
     <!-- no translation found for select_language (2573265881207142437) -->
     <skip />
@@ -222,87 +220,11 @@
     <!-- no translation found for prefs_enable_recorrection_summary (5082041365862396329) -->
     <skip />
     <!-- outdated translation 437433231038683666 -->     <string name="keyboard_layout" msgid="8451164783510487501">"Design da la tastatura"</string>
-    <!-- no translation found for subtype_mode_cs_keyboard (1141718931112377586) -->
+    <!-- no translation found for subtype_de_qwerty (3358900499589259491) -->
     <skip />
-    <!-- no translation found for subtype_mode_ar_keyboard (2655338636329774995) -->
+    <!-- no translation found for subtype_en_GB (88170601942311355) -->
     <skip />
-    <!-- no translation found for subtype_mode_da_keyboard (1243570804427922104) -->
-    <skip />
-    <!-- no translation found for subtype_mode_de_keyboard (1990979135959462145) -->
-    <skip />
-    <!-- no translation found for subtype_mode_de_qwerty_keyboard (54890770769303956) -->
-    <skip />
-    <!-- no translation found for subtype_mode_en_GB_keyboard (7945856548410373708) -->
-    <skip />
-    <!-- no translation found for subtype_mode_en_US_keyboard (3708655163769735410) -->
-    <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) -->
-    <skip />
-    <!-- no translation found for subtype_mode_sv_keyboard (4933838139861753401) -->
-    <skip />
-    <!-- no translation found for subtype_mode_tr_keyboard (3155981874829226370) -->
-    <skip />
-    <!-- no translation found for subtype_mode_af_voice (7542487489657902699) -->
-    <skip />
-    <!-- no translation found for subtype_mode_cs_voice (1136386688120958641) -->
-    <skip />
-    <!-- no translation found for subtype_mode_de_voice (8378803143958089866) -->
-    <skip />
-    <!-- no translation found for subtype_mode_en_voice (6643420989651848728) -->
-    <skip />
-    <!-- no translation found for subtype_mode_es_voice (1323473601346507487) -->
-    <skip />
-    <!-- no translation found for subtype_mode_fr_voice (4675914209337824269) -->
-    <skip />
-    <!-- no translation found for subtype_mode_it_voice (5077373057157441323) -->
-    <skip />
-    <!-- no translation found for subtype_mode_ja_voice (6604859132669646367) -->
-    <skip />
-    <!-- no translation found for subtype_mode_ko_voice (4890391190762324561) -->
-    <skip />
-    <!-- no translation found for subtype_mode_nl_voice (2603552312869575021) -->
-    <skip />
-    <!-- no translation found for subtype_mode_pl_voice (2076196021014840487) -->
-    <skip />
-    <!-- no translation found for subtype_mode_pt_voice (8036522712795994397) -->
-    <skip />
-    <!-- no translation found for subtype_mode_ru_voice (8034596947963787529) -->
-    <skip />
-    <!-- no translation found for subtype_mode_tr_voice (3402067436761140005) -->
-    <skip />
-    <!-- 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) -->
+    <!-- no translation found for subtype_en_US (6160452336634534239) -->
     <skip />
     <!-- no translation found for prefs_usability_study_mode (1261130555134595254) -->
     <skip />
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index e9cbd50..c317cfd 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Tastatură Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Setările tastaturii Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opţiuni de introducere text"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrare la apăsarea tastei"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizaţi numele din Agendă pentru sugestii şi corecţii"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalizare"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Configuraţi dicţionare"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Remedieri rapide"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Corectează greşelile introduse frecvent"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Afişaţi sugestii de corectare"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Afişaţi sugestii de cuvinte în timpul introducerii textului"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Afişaţi întotdeauna"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Ascundeţi întotdeauna"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Autocorecţie"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Corectare automată cuvinte prin bară spaţiu/semne punctuaţie"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Dezactivată"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Terminat"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Trimiteţi"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micr. pe tast. simb."</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Intr. vocală dezact."</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Selectaţi metoda de introducere a textului"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Configuraţi metodele de intrare"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Selectaţi limba"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Selectaţi limbile de intrare"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Atingeţi din nou pentru a salva"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Dicţionar disponibil"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Activaţi feedback de la utilizatori"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Atingeţi pentru a corecta cuvintele"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Tastatură germană QWERTY"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Tastatură finlandeză"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Tastatură croată"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Tastatură maghiară"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Tastatură turcă"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Voce afrikaans"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Voce cehă"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Voce germană"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Voce engleză"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Voce spaniolă"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Voce franceză"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Voce italiană"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Voce japoneză"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Voce coreeană"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Voce olandeză"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Voce poloneză"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Voce portugheză"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Voce rusă"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Voce turcă"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Voce chineză, yue"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Voce chineză, mandarină"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Voce isiZulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Tastatură germană QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Engleză (Marea Britanie)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Engleză (S.U.A.)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modul Studiu privind utilizarea"</string>
 </resources>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index eac2026..68d1ee2 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Виброотклик клавиш"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук клавиш"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Увеличение нажатых"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Подсказки и исправления на основе имен из контактов"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Заглавные автоматически"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Настроить словари"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Быстрое исправление"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Исправлять распространенные опечатки"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Показать варианты исправлений"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Предлагать варианты слов во время ввода"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Всегда показывать"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Всегда скрывать"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Автоисправление"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Автоматическое исправление опечаток при вводе знака препинания или пробела"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Откл."</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Готово"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Отправить"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"АБВ"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"Ещё"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Приостановить"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Подождите"</string>
@@ -134,9 +133,9 @@
     <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="configure_input_method" msgid="373356270290742459">"Настройка раскладки клавиатуры"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Языки ввода"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Выберите языки ввода"</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>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Исправление нажатием"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Нажмите на слово, чтобы исправить его (при наличии подсказок)"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Тема клавиатуры"</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_de_qwerty_keyboard" msgid="54890770769303956">"Немецкая раскладка QWERTY"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Финская раскладка"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Хорватская раскладка"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Венгерская раскладка"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Турецкая раскладка"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Голосовой ввод на африкаанс"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Голос: чешский"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Голос: немецкий"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Голосовой ввод на английском"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Голос: испанский"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Голос: французский"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Голосовой ввод на итальянском"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Голос: японский"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Голос: корейский"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Голосовой ввод на голландском"</string>
-    <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>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Голосовой ввод на китайском (диалект юэ)"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Голосовой ввод на китайском (мандаринский диалект)"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Голосовой ввод на зулу"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Немецкая клавиатура QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"английский (Великобритания)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"английский (США)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим проверки удобства использования"</string>
 </resources>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index 8cdc737..a8c5700 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Klávesnica Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Nastavenia klávesnice Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávania textu a údajov"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Pri stlačení klávesu vibrovať"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Používať mená z Kontaktov na návrhy a opravy"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Veľké písmená automaticky"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Konfigurovať slovníky"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Zobraziť návrhy opráv"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Zobrazovať navrhované slová počas písania"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vždy zobrazovať"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Vždy skrývať"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Automatické opravy"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Stlačením medzerníka a interpunkcie sa aut. opravia chybné slová"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Vypnuté"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Hotovo"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Odoslať"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofón na klávesnici so symbolmi"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hlasový vstup je zakázaný"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Výber metódy vstupu"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Konfigurovať metódy vstupu"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Jazyky vstupu"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Vybrať jazyky vstupu"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Ďalším dotykom slovo uložíte"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"K dispozícii je slovník"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Povoliť spätnú väzbu od používateľov"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Dotykom opravíte slová"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Nemecká klávesnica QWERTY"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Fínska klávesnica"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Chorvátska klávesnica"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Maďarská klávesnica"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"turecká klávesnica"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Hlas – afrikánčina"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"hlas – čeština"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"hlas – nemčina"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Hlas – angličtina"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"hlas – španielčina"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"hlas – francúzština"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"hlas – taliančina"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"hlas – japončina"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"hlas – kórejčina"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"hlas – holandčina"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"hlas – poľština"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"hlas – portugalčina"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"hlas – ruština"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"hlas – turečtina"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Hlas – čínština, kantónčina"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Hlas – čínština, mandarínska"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Hlas – Zulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Nemecká klávesnica QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Anglická klávesnica (UK)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Anglická klávesnica (US)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Režim štúdie použiteľnosti"</string>
 </resources>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index a599ab5..19bf04c 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Tipkovnica Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Nastavitve tipkovnice Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti vnosa"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibriranje ob pritisku tipke"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Uporaba imen iz stikov za predloge in popravke"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Samodejne velike začetnice"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Konfiguracija slovarjev"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Hitri popravki"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Popravi pogoste tipkarske napake"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Pokaži predloge popravkov"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Pokaži predlagane besede med tipkanjem"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vedno pokaži"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Vedno skrij"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Samodejni popravek"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Preslednica in ločila samodejno popravijo napačno vtipkane besede"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Izklopljeno"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Dokončano"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Pošlji"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik. na tipk. s sim."</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Glas. vnos je onem."</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Izberite način vnosa"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Nastavitev načinov vnosa"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Jeziki vnosa"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Izberite jezike vnosa"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Še enkrat se dotaknite, da shranite"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Slovar je na voljo"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Omogoči povratne informacije uporabnikov"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Dotaknite se besed in jih popravite"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Nemška tipkovnica QWERTY"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Finska tipkovnica"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Hrvaška tipkovnica"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Madžarska tipkovnica"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Turška tipkovnica"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Govor v afrikanščini"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Govor v češčini"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Govor v nemščini"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Govor v angleščini"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Govor v španščini"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Govor v francoščini"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Govor v italijanščini"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Govor v japonščini"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Govor v korejščini"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Govor v nizozemščini"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Govor v poljščini"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Govor v portugalščini"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Govor v ruščini"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Govor v turščini"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Govor v kitajščini, jue"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Govor v kitajščini, mandarinščini"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Govor v zulujščini"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Nemška QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"angl. (bri.)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"angl. (ZDA)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Način za preučevanje uporabnosti"</string>
 </resources>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index bfbcd1a..46daea9 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Вибрирај на притисак тастера"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук на притисак тастера"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Искачући прозор приликом притиска тастера"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Користи имена из Контаката за предлоге и исправке"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Аутоматски унос великих слова"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Конфигуриши речнике"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Брзе исправке"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Исправља честе грешке у куцању"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Прикажи предлоге за исправку"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Приказивање предложених речи током уноса текста"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Увек прикажи"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Увек сакриј"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Аутоматско исправљање"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Размак и интерпункција аутоматски исправљају грешке у куцању"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Искључи"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Готово"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Пошаљи"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"Још"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Паузирај"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Сачекајте"</string>
@@ -134,9 +133,9 @@
     <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="configure_input_method" msgid="373356270290742459">"Конфигурисање метода уноса"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Језици за унос"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Изабери језике за унос"</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>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Додирните да бисте исправили речи"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Додирните унете речи да бисте их исправили само када су предлози видљиви"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Тема тастатуре"</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_de_qwerty_keyboard" msgid="54890770769303956">"QWERTY тастатура за немачки"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Језик тастатуре: фински"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Језик тастатуре: хрватски"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Језик тастатуре: мађарски"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Језик тастатуре: турски"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Глас на африкансу"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Глас на чешком"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Глас на немачком"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Глас на енглеском"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Глас на шпанском"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Глас на француском"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Глас на италијанском"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Глас на јапанском"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Глас на корејском"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Глас на холандском"</string>
-    <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>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Глас на јуе кинеском"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Глас на мандаринском кинеском"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Глас на језику исизулу"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY тастатура за немачки"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"енглески (УК)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"енглески (САД)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим за студију могућности коришћења"</string>
 </resources>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 7387cfa..2c8bc73 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Androids tangentbord"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Inställningar för Androids tangentbord"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Inmatningsalternativ"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrera vid tangenttryck"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Knappljud"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Popup vid knapptryck"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Använd namn från Kontakter för förslag och korrigeringar"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Automatiska versaler"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Konfigurera ordböcker"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Snabba lösningar"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Åtgärdar automatiskt vanliga misstag"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Visa rättningsförslag"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Visar ordförslag när du skriver"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Visa alltid"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Dölj alltid"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Autokorrigering"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Blanksteg/skiljetecken rättar felstavning"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Av"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Färdig"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Skicka"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mick bland symboler"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Röstinmatning inaktiv"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Välj inmatningsmetod"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Konfigurera inmatningsmetoder"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Inmatningsspråk"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Välj inmatningsspråk"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Tryck igen för att spara"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"En ordlista är tillgänglig"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Aktivera synpunkter från användare"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tryck om du vill korrigera ord"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Tyskt QWERTY-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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Finskt tangentbord"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Kroatiskt tangentbord"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Ungerskt tangentbord"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Turkiskt tangentbord"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Röst, afrikaans"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Röst på tjeckiska"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Röst på tyska"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Röst, engelska"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Röst på spanska"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Röst på franska"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Röst på italienska"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Röst på japanska"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Röst på koreanska"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Röst på holländska"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Röst på polska"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Röst på portugisiska"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Röst på ryska"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Röst på turkiska"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Röst, kinesiska, yue"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Röst, kinesiska, mandarin"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Röst, isiZulusk"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Tyskt QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Engelskt (brittiskt)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Engelskt (amerikanskt)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Läge för studie av användbarhet"</string>
 </resources>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index e9f077b..a916535 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <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>
@@ -51,20 +53,13 @@
     <string name="auto_cap" msgid="1719746674854628252">"Uwekaji wa herufi kubwa kiotomatiki"</string>
     <!-- no translation found for configure_dictionaries_title (3758288002414557345) -->
     <skip />
-    <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>
@@ -84,6 +79,10 @@
     <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>
+    <!-- no translation found for label_to_symbol_key (8516904117128967293) -->
+    <skip />
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -187,6 +186,8 @@
     <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>
+    <!-- no translation found for configure_input_method (373356270290742459) -->
+    <skip />
     <string name="language_selection_title" msgid="1651299598555326750">"Lugha za uingizaji"</string>
     <!-- no translation found for select_language (2573265881207142437) -->
     <skip />
@@ -197,62 +198,11 @@
     <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>
     <!-- outdated translation 437433231038683666 -->     <string name="keyboard_layout" msgid="8451164783510487501">"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) -->
+    <!-- no translation found for subtype_de_qwerty (3358900499589259491) -->
     <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>
-    <!-- no translation found for subtype_mode_de_qwerty_keyboard (54890770769303956) -->
+    <!-- no translation found for subtype_en_GB (88170601942311355) -->
     <skip />
-    <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_tr_keyboard (3155981874829226370) -->
-    <skip />
-    <!-- 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) -->
+    <!-- no translation found for subtype_en_US (6160452336634534239) -->
     <skip />
     <!-- outdated translation 6937813623647419810 -->     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"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 5d4967f..c75fcc0 100644
--- a/java/res/values-sw600dp-land/dimens.xml
+++ b/java/res/values-sw600dp-land/dimens.xml
@@ -51,5 +51,4 @@
     <fraction name="key_uppercase_letter_ratio">29%</fraction>
 
     <dimen name="candidate_strip_padding">40.0mm</dimen>
-    <integer name="candidate_count_in_strip">5</integer>
 </resources>
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml
index 260f360..ade376a 100644
--- a/java/res/values-sw600dp/config.xml
+++ b/java/res/values-sw600dp/config.xml
@@ -20,10 +20,10 @@
 
 <resources>
     <bool name="config_enable_show_settings_key_option">true</bool>
+    <bool name="config_default_show_settings_key">true</bool>
     <bool name="config_enable_show_voice_key_option">false</bool>
     <bool name="config_enable_show_popup_on_keypress_option">false</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_sliding_key_input_enabled">false</bool>
     <bool name="config_digit_popup_characters_enabled">false</bool>
@@ -31,7 +31,6 @@
     <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>
     <!-- The language is never displayed if == 0, always displayed if < 0 -->
diff --git a/java/res/values-sw768dp-land/dimens.xml b/java/res/values-sw768dp-land/dimens.xml
index 18837fe..0cfdffd 100644
--- a/java/res/values-sw768dp-land/dimens.xml
+++ b/java/res/values-sw768dp-land/dimens.xml
@@ -59,5 +59,4 @@
     <dimen name="key_preview_offset_ics">0.05in</dimen>
 
     <dimen name="candidate_strip_padding">40.0mm</dimen>
-    <integer name="candidate_count_in_strip">5</integer>
 </resources>
diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml
index 8260568..091d62e 100644
--- a/java/res/values-sw768dp/config.xml
+++ b/java/res/values-sw768dp/config.xml
@@ -20,10 +20,10 @@
 
 <resources>
     <bool name="config_enable_show_settings_key_option">false</bool>
+    <bool name="config_default_show_settings_key">true</bool>
     <bool name="config_enable_show_voice_key_option">false</bool>
     <bool name="config_enable_show_popup_on_keypress_option">false</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_sliding_key_input_enabled">false</bool>
     <bool name="config_digit_popup_characters_enabled">false</bool>
@@ -31,7 +31,6 @@
     <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>
     <integer name="config_delay_update_suggestions">180</integer>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index 75befea..780ff02 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"สั่นเมื่อกดปุ่ม"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"ส่งเสียงเมื่อกดปุ่ม"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"ป๊อปอัปเมื่อกดแป้น"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"ใช้ชื่อจากรายชื่อติดต่อสำหรับคำแนะนำและการแก้ไข"</string>
     <string name="auto_cap" msgid="1719746674854628252">"ปรับเป็นตัวพิมพ์ใหญ่อัตโนมัติ"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"กำหนดค่าพจนานุกรม"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"แก้ไขด่วน"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"แก้ไขข้อผิดพลาดในการพิมพ์ที่พบบ่อย"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"แสดงคำแนะนำการแก้ไข"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"แสดงคำที่แนะนำขณะพิมพ์"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"แสดงทุกครั้ง"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"ซ่อนตลอดเวลา"</string>
     <string name="auto_correction" msgid="4979925752001319458">"การแก้ไขอัตโนมัติ"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"กดเว้นวรรคและเครื่องหมายจะแก้คำผิดอัตโนมัติ"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"ปิด"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"เสร็จสิ้น"</string>
     <string name="label_send_key" msgid="2815056534433717444">"ส่ง"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"เพิ่มเติม"</string>
     <string name="label_pause_key" msgid="181098308428035340">"หยุดชั่วคราว"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"รอ"</string>
@@ -134,9 +133,9 @@
     <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="configure_input_method" msgid="373356270290742459">"กำหนดค่าวิธีการป้อนข้อมูล"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"ภาษาในการป้อนข้อมูล"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"เลือกภาษาสำหรับการป้อนข้อมูล"</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>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"แตะเพื่อแก้ไขคำ"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"แตะคำที่ป้อนไว้เพื่อแก้ไข เฉพาะเมื่อเห็นข้อเสนอแนะเท่านั้น"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"ชุดรูปแบบแป้นพิมพ์"</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_de_qwerty_keyboard" msgid="54890770769303956">"แป้นพิมพ์ภาษาเยอรมันแบบ QWERTY"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"แป้นพิมพ์ภาษาฟินแลนด์"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"แป้นพิมพ์ภาษาโครเอเชีย"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"แป้นพิมพ์ภาษาฮังกาเรียน"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"แป้นพิมพ์ภาษาตุรกี"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"เสียงภาษาแอฟริกัน"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"เสียงภาษาเช็ก"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"เสียงภาษาเยอรมัน"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"เสียงภาษาอังกฤษ"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"เสียงภาษาสเปน"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"เสียงภาษาฝรั่งเศส"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"เสียงภาษาอิตาลี"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"เสียงภาษาญี่ปุ่น"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"เสียงภาษาเกาหลี"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"เสียงภาษาดัตช์"</string>
-    <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>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"เสียงภาษาจีนกวางตุ้ง"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"เสียงภาษาจีนกลาง"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"เสียงภาษาซูลู"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY ภาษาเยอรมัน"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"อังกฤษ (สหราชอาณาจักร)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"อังกฤษ (สหรัฐอเมริกา)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"โหมดศึกษาประโยชน์ในการใช้งาน"</string>
 </resources>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index d8652ea..b0806be 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Android keyboard"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Mga setting ng Android keyboard"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Mga pagpipilian sa input"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Mag-vibrate sa keypress"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Tunog sa keypress"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Popup sa keypress"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Gamitin pangalan mula Mga Contact sa mga mungkahi\'t pagwawasto"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalization"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"I-configure ang mga diksyunaryo"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Magpakita ng mga suhestiyon ng pagwawasto"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Ipakita ang mga iminumungkahing salita habang nagta-type"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Palaging ipakita"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Palaging itago"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Awtomatikong pagwasto"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Awto tinatama ng spacebar at bantas ang maling na-type"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Naka-off"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Tapos na"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Ipadala"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic sa keyboard ng mga simbolo"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hindi pinagana ang voice input"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Pumili ng paraan ng pag-input"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"I-configure ang mga pamamaraan ng pag-input"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Mag-input ng mga wika"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Pumili ng mga wika ng input"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Pinduting muli upang i-save"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Available ang diksyunaryo"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Paganahin ang feedback ng user"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Pindutin upang itama ang mga salita"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"German na QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Finnish na Keyboard"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Croatian na Keyboard"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Hungarian na Keyboard"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Turkish na Keyboard"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Afrikaans na Boses"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Czech na Boses"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"German na Boses"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Ingles na Boses"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Spanish na Boses"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"French na Boses"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Italian na Boses"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Japanese na Boses"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Korean na Boses"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Dutch na Boses"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Polish na Boses"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Portuguese na Boses"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Russian na Boses"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Turkish na Boses"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Chinese, Yue na Boses"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Chinese, Mandarin na Boses"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"isiZulu na Boses"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"German QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Ingles (UK)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Ingles (US)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Study mode ng pagiging kapaki-pakinabang"</string>
 </resources>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index 00dcaac..103f016 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Android klavyesi"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Android klavye ayarları"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Giriş seçenekleri"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Tuşa basıldığında titret"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Öneri ve düzeltmeler için Kişiler\'deki adları kullan"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Otomatik olarak büyük harf yap"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Sözlükleri yapılandır"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Düzeltme önerilerini göster"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Yazarken, önerilen kelimeleri görüntüle"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Her zaman göster"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Her zaman gizle"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Otomatik düzeltme"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Boşluk çbğ ve nokt işr yanlış yazılan kelimeleri oto düzeltir"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Kapalı"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Bitti"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Gönder"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Simge klavysnd mikrf"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Sesle grş devre dışı"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Giriş yöntemini seç"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Giriş yöntemlerini yapılandır"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Giriş dilleri"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Giriş dillerini seçin"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Kaydetmek için tekrar dokunun"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Sözlük kullanılabilir"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Kullanıcı geri bildirimini etkinleştir"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Kelimeleri düzeltmek için dokunun"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Almanca QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Fince klavye"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Hırvatça Klavye"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Macarca Klavye"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Türkçe Klavye"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Afrikanca Ses"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Çekçe Ses"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Almanca Ses"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"İngilizce Ses"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"İspanyolca Ses"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Fransızca Ses"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"İtalyanca Ses"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Japonca Ses"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Korece Ses"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Felemenkçe Ses"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Lehçe Ses"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Portekizce Ses"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Rusça Ses"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Türkçe Ses"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Çince, Yue Ses"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Çince, Mandarin Ses"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Zuluca Ses"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Almanca QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"İngilizce (İngiltere)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"İngilizce (ABD)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Kullanılabilirlik çalışması modu"</string>
 </resources>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index 9ba53ca..cca56b8 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Вібр при натиску клав."</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук при натиску клав."</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Сплив. при нат.клав."</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Використ. імена зі списку контактів для пропозицій і виправлень"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Авто викор. вел. літер"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Налаштувати словники"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"Шв. виправлення"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"Виправляє поширені помилки"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Показувати пропозиції виправлень"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Відображати пропоновані слова під час вводу"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Завжди показувати"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Завжди ховати"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Автомат. виправлення"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Пробіл і пунктуація автоматично виправляють слова з помилками"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Вимк."</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Готово"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Надісл."</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"Алфавіт"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"Більше"</string>
     <string name="label_pause_key" msgid="181098308428035340">"Пауза"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"Чек."</string>
@@ -134,6 +133,7 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Miкр. на симв. клавіат."</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Голос. ввід вимкнено"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Вибрати метод введення"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Налаштувати методи введення"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Мови вводу"</string>
     <string name="select_language" msgid="2573265881207142437">"Вибрати мову введення"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Торкн. ще, щоб збер."</string>
@@ -143,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Торкн., щоб виправ. слова"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Торкніться введених слів, щоб виправити їх, лише коли ввімкнено пропозиції"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Тема клавіатури"</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_de_qwerty_keyboard" msgid="54890770769303956">"Німецька клавіатура QWERTY"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Фінська розкладка"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Хорватська розкладка"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Угорська розкладка"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Турецька розкладка"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Voice мовою африкаанс"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Голос чеською"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Голос німецькою"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Voice англійською мовою"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Голос іспанською"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Голос французькою"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Voice італійською мовою"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Голос японською"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Голос корейською"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Voice голландською мовою"</string>
-    <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>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Voice китайською, юеською мовою"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Voice китайською, мандарин. мовою"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Voice мовою ісізулу"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Німецька клавіатура QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Англійська (Великобританія)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Англійська (США)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим вивчення зручності у використанні"</string>
 </resources>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index d35bdb2..e7c684d 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -23,6 +23,8 @@
     <string name="english_ime_name" msgid="7252517407088836577">"Bàn phím Android"</string>
     <string name="english_ime_settings" msgid="6661589557206947774">"Cài đặt bàn phím Android"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Tùy chọn nhập"</string>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Rung khi nhấn phím"</string>
     <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>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Sử dụng tên từ Danh bạ cho các đề xuất và chỉnh sửa"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Tự động viết hoa"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"Cấu hình từ điển"</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>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Hiển thị gợi ý sửa"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Hiển thị từ được đề xuất khi nhập"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Luôn hiển thị"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Luôn ẩn"</string>
     <string name="auto_correction" msgid="4979925752001319458">"Tự động sửa"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Phím cách và dấu câu tự động sửa từ nhập sai"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Tắt"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"Xong"</string>
     <string name="label_send_key" msgid="2815056534433717444">"Gửi"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -134,9 +133,9 @@
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micrô trên bàn phím biểu tượng"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Nhập liệu bằng giọng nói đã bị vô hiệu hóa"</string>
     <string name="selectInputMethod" msgid="315076553378705821">"Chọn phương thức nhập"</string>
+    <string name="configure_input_method" msgid="373356270290742459">"Định cấu hình phương thức nhập"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Ngôn ngữ nhập"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"Chọn ngôn ngữ nhập"</string>
     <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Chạm lại để lưu"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Có sẵn từ điển"</string>
     <string name="prefs_enable_log" msgid="6620424505072963557">"Bật phản hồi của người dùng"</string>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Chạm để sửa từ"</string>
     <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="8451164783510487501">"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_de_qwerty_keyboard" msgid="54890770769303956">"Bàn phím QWERTY 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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"Bàn phím tiếng Phần Lan"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"Bàn phím tiếng Croatia"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"Bàn phím tiếng Hungari"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"Bàn phím tiếng Thổ Nhĩ Kỳ"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"Giọng tiếng Hà Lan ở Nam Phi"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"Giọng nói tiếng Séc"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"Giọng nói tiếng Đức"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"Giọng tiếng Anh"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"Giọng nói tiếng Tây Ban Nha"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"Giọng nói tiếng Pháp"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"Giọng nói tiếng Ý"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"Giọng nói tiếng Nhật"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"Giọng nói tiếng Hàn"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"Giọng nói tiếng Hà Lan"</string>
-    <string name="subtype_mode_pl_voice" msgid="2076196021014840487">"Giọng nói tiếng Ba Lan"</string>
-    <string name="subtype_mode_pt_voice" msgid="8036522712795994397">"Giọng nói tiếng Bồ Đào Nha"</string>
-    <string name="subtype_mode_ru_voice" msgid="8034596947963787529">"Giọng nói tiếng Nga"</string>
-    <string name="subtype_mode_tr_voice" msgid="3402067436761140005">"Giọng nói tiếng Thổ Nhĩ Kỳ"</string>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"Giọng tiếng Trung, Yue"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"Giọng tiếng Trung, tiếng Quan thoại"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"Giọng isiZulu"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"Bàn phím QWERTY tiếng Đức"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"Tiếng Anh (Anh)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"Tiếng Anh (Mỹ)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Chế độ nghiên cứu tính khả dụng"</string>
 </resources>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index 750e29c..189c174 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"按键时振动"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"按键时播放音效"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"按键时显示弹出窗口"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"使用联系人中的姓名提供建议和更正"</string>
     <string name="auto_cap" msgid="1719746674854628252">"自动大写"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"配置字典"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"快速纠正"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"纠正常见的输入错误"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"显示更正建议"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"键入时显示建议的字词"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"始终显示"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"始终隐藏"</string>
     <string name="auto_correction" msgid="4979925752001319458">"自动更正"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"空格键或标点自动更正拼写错误的字词"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"关闭"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"完成"</string>
     <string name="label_send_key" msgid="2815056534433717444">"发送"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"更多"</string>
     <string name="label_pause_key" msgid="181098308428035340">"暂停"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"等待"</string>
@@ -134,9 +133,9 @@
     <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="configure_input_method" msgid="373356270290742459">"配置输入法"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"输入语言"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"选择输入语言"</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>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"触摸以更正字词"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"仅在系统显示建议后,才触摸输入的字词进行更正"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"键盘主题"</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_de_qwerty_keyboard" msgid="54890770769303956">"德语 QWERTY 键盘"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"芬兰语键盘"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"克罗地亚语键盘"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"匈牙利语键盘"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"土耳其语键盘"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"南非荷兰语语音"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"捷克语语音"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"德语语音"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"英语语音"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"西班牙语语音"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"法语语音"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"意大利语语音"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"日语语音"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"韩语语音"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"荷兰语语音"</string>
-    <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>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"中文,粤语语音"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"中文,普通话语音"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"祖鲁语语音"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"德语 QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"英语(英式)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"英语(美式)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"可用性研究模式"</string>
 </resources>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 6dca7ed..aa65cdd 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"按鍵時震動"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"按鍵時播放音效"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"按鍵時顯示彈出式視窗"</string>
@@ -39,18 +41,12 @@
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"使用「聯絡人」的名稱提供建議與修正"</string>
     <string name="auto_cap" msgid="1719746674854628252">"自動大寫"</string>
     <string name="configure_dictionaries_title" msgid="3758288002414557345">"設定字典"</string>
-    <string name="quick_fixes" msgid="5353213327680897927">"快速修正"</string>
-    <string name="quick_fixes_summary" msgid="3405028402510332373">"修正一般打字錯誤"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"顯示修正建議"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"輸入時顯示建議字詞"</string>
     <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"一律顯示"</string>
     <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>
-    <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"永遠隱藏"</string>
     <string name="auto_correction" msgid="4979925752001319458">"自動修正"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"自動插入空白鍵和標點符號鍵盤,以修正拼字錯誤"</string>
     <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"關閉"</string>
@@ -67,6 +63,9 @@
     <string name="label_done_key" msgid="2441578748772529288">"完成"</string>
     <string name="label_send_key" msgid="2815056534433717444">"傳送"</string>
     <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+    <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <string name="label_more_key" msgid="3760239494604948502">"更多"</string>
     <string name="label_pause_key" msgid="181098308428035340">"暫停"</string>
     <string name="label_wait_key" msgid="6402152600878093134">"等候"</string>
@@ -134,9 +133,9 @@
     <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="configure_input_method" msgid="373356270290742459">"設定輸入法"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"輸入語言"</string>
-    <!-- no translation found for select_language (2573265881207142437) -->
-    <skip />
+    <string name="select_language" msgid="2573265881207142437">"選取輸入語言"</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>
@@ -144,46 +143,8 @@
     <string name="prefs_enable_recorrection" msgid="4588408906649533582">"輕觸此處可修正字詞"</string>
     <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"輕觸輸入的字詞即可加以修正 (出現建議時才適用)"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"鍵盤主題"</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_de_qwerty_keyboard" msgid="54890770769303956">"德文 QWERTY 鍵盤"</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>
-    <string name="subtype_mode_fi_keyboard" msgid="3198596464082614532">"芬蘭文鍵盤"</string>
-    <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>
-    <string name="subtype_mode_hr_keyboard" msgid="7177182405440070112">"克羅埃西亞文鍵盤"</string>
-    <string name="subtype_mode_hu_keyboard" msgid="8843338355732633647">"匈牙利文鍵盤"</string>
-    <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>
-    <string name="subtype_mode_tr_keyboard" msgid="3155981874829226370">"土耳其文鍵盤"</string>
-    <string name="subtype_mode_af_voice" msgid="7542487489657902699">"南非荷蘭文語音"</string>
-    <string name="subtype_mode_cs_voice" msgid="1136386688120958641">"捷克文語音"</string>
-    <string name="subtype_mode_de_voice" msgid="8378803143958089866">"德文語音"</string>
-    <string name="subtype_mode_en_voice" msgid="6643420989651848728">"英文語音"</string>
-    <string name="subtype_mode_es_voice" msgid="1323473601346507487">"西班牙文語音"</string>
-    <string name="subtype_mode_fr_voice" msgid="4675914209337824269">"法文語音"</string>
-    <string name="subtype_mode_it_voice" msgid="5077373057157441323">"義大利文語音"</string>
-    <string name="subtype_mode_ja_voice" msgid="6604859132669646367">"日文語音"</string>
-    <string name="subtype_mode_ko_voice" msgid="4890391190762324561">"韓文語音"</string>
-    <string name="subtype_mode_nl_voice" msgid="2603552312869575021">"荷蘭文語音"</string>
-    <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>
-    <string name="subtype_mode_yue_voice" msgid="1576887891614624263">"中文 (粵語) 語音"</string>
-    <string name="subtype_mode_zh_voice" msgid="4360533229467271152">"中文 (華語) 語音"</string>
-    <string name="subtype_mode_zu_voice" msgid="1146122571698884636">"祖魯文語音"</string>
+    <string name="subtype_de_qwerty" msgid="3358900499589259491">"德文 QWERTY"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"英文 (英式)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"英文 (美式)"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"使用性研究模式"</string>
 </resources>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index 76f87b0..4929c12 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -23,6 +23,8 @@
     <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>
+    <!-- no translation found for spell_checker_service_name (2003013122022285508) -->
+    <skip />
     <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>
@@ -51,20 +53,13 @@
     <string name="auto_cap" msgid="1719746674854628252">"Ukwenza ofeleba okuzenzakalelayo"</string>
     <!-- no translation found for configure_dictionaries_title (3758288002414557345) -->
     <skip />
-    <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>
@@ -84,6 +79,10 @@
     <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>
+    <!-- no translation found for label_to_symbol_key (8516904117128967293) -->
+    <skip />
+    <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+    <skip />
     <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>
@@ -159,11 +158,11 @@
     <skip />
     <!-- no translation found for spoken_description_low_double_quote (3551394572784840975) -->
     <skip />
-    <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_warning_title" msgid="4419354150908395008">"Okufakwa ngezwi"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Okufakwa ngezwi akusekelwa olimini lwakho, kodwa kuyasebenza nge-English."</string>
+    <string name="voice_warning_may_not_understand" msgid="5596289095878251072">"Okufakwayo ngezwi kusebenzisa ukufanisa izwi kwe-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 ngezwi, iya kuzilungiselelo zendlela yokufakwayo"</string>
+    <string name="voice_hint_dialog_message" msgid="1420686286820661548">"Ukusebenzisa okufakwayo ngezwi, 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>
@@ -179,80 +178,31 @@
     <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" msgid="2466640768843347841">"Okufakwa ngezwi"</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="voice_input_modes_summary_off" msgid="63875609591897607">"Okufakwayo ngezwi kuvinjelwe"</string>
+    <string name="selectInputMethod" msgid="315076553378705821">"Khetha indlela yokufaka"</string>
+    <!-- no translation found for configure_input_method (373356270290742459) -->
+    <skip />
     <string name="language_selection_title" msgid="1651299598555326750">"Izilimi zokufakwayo"</string>
     <!-- no translation found for select_language (2573265881207142437) -->
     <skip />
     <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_description_log" msgid="5827825607258246003">"Siza ukuthuthukisa lo mhleli wendlela yokufakwa ngokusithumela ngokuzenzakalela izibalo zokusetshenziswa nokukhubeka ku-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>
     <!-- outdated translation 437433231038683666 -->     <string name="keyboard_layout" msgid="8451164783510487501">"Indikimba Yekhibhodi"</string>
-    <string name="subtype_mode_cs_keyboard" msgid="1141718931112377586">"Ikhibhodi yesi-Czech"</string>
-    <!-- no translation found for subtype_mode_ar_keyboard (2655338636329774995) -->
+    <!-- no translation found for subtype_de_qwerty (3358900499589259491) -->
     <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>
-    <!-- no translation found for subtype_mode_de_qwerty_keyboard (54890770769303956) -->
+    <!-- no translation found for subtype_en_GB (88170601942311355) -->
     <skip />
-    <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_tr_keyboard (3155981874829226370) -->
-    <skip />
-    <!-- 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) -->
+    <!-- no translation found for subtype_en_US (6160452336634534239) -->
     <skip />
     <!-- outdated translation 6937813623647419810 -->     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Imodi Yesitadi Yokusebenziseka"</string>
 </resources>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index fa5c08b..172ca2f 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -78,8 +78,6 @@
         <attr name="keyPreviewLeftBackground" format="reference" />
         <!-- The background for the right edge key press feedback. -->
         <attr name="keyPreviewRightBackground" format="reference" />
-        <!-- The background for spacebar press feedback. -->
-        <attr name="keyPreviewSpacebarBackground" format="reference" />
         <!-- The text color for key press feedback. -->
         <attr name="keyPreviewTextColor" format="color" />
         <!-- Vertical offset of the key press feedback from the key. -->
@@ -118,6 +116,7 @@
         <attr name="colorAutoCorrect" format="color" />
         <attr name="colorSuggested" format="color" />
         <attr name="candidateCountInStrip" format="integer" />
+        <attr name="centerCandidatePercentile" format="integer" />
     </declare-styleable>
 
     <declare-styleable name="Keyboard">
@@ -152,20 +151,19 @@
         <attr name="popupKeyboardTemplate" format="reference" />
         <!-- Locale of the keyboard layout -->
         <attr name="keyboardLocale" format="string" />
+        <!-- True if the keyboard is Right-To-Left -->
+        <attr name="isRtlKeyboard" format="boolean" />
         <!-- 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="iconDeleteRtlKey" 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="iconShortcutKey" format="reference" />
+        <attr name="iconShortcutForLabel" 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" />
@@ -178,13 +176,6 @@
         <attr name="popupCharacters" format="string" />
         <!-- Maximum column of popup keyboard -->
         <attr name="maxPopupKeyboardColumn" format="integer" />
-        <!-- Key edge flags. -->
-        <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 functional key which has different key top than normal key. -->
         <attr name="isFunctional" format="boolean" />
         <!-- Whether this is a toggle key. -->
@@ -211,34 +202,33 @@
             <flag name="hasPopupHint" value="0x200" />
             <flag name="hasUppercaseLetter" value="0x400" />
             <flag name="hasHintLabel" value="0x800" />
+            <flag name="withIconLeft" value="0x1000" />
+            <flag name="withIconRight" value="0x2000" />
         </attr>
         <!-- The icon to display on the key instead of the label. -->
         <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="iconDeleteRtlKey" value="5" />
-            <enum name="iconSettingsKey" value="6" />
-            <enum name="iconShortcutKey" value="7" />
-            <enum name="iconSpaceKey" value="8" />
-            <enum name="iconReturnKey" value="9" />
-            <enum name="iconSearchKey" value="10" />
-            <enum name="iconTabKey" value="11" />
+            <enum name="iconDeleteKey" value="2" />
+            <enum name="iconSettingsKey" value="3" />
+            <enum name="iconSpaceKey" value="4" />
+            <enum name="iconReturnKey" value="5" />
+            <enum name="iconSearchKey" value="6" />
+            <enum name="iconTabKey" value="7" />
+            <enum name="iconShortcutKey" value="8" />
+            <enum name="iconShortcutForLabel" value="9" />
         </attr>
         <!-- Shift key icon for shifted state -->
         <attr name="keyIconShifted" format="enum">
             <!-- This should be aligned with KeyboardIcons.ICON_SHIFTED_* -->
-            <enum name="iconShiftedShiftKey" value="12" />
+            <enum name="iconShiftedShiftKey" value="10" />
         </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="13" />
-            <enum name="iconPreviewTabKey" value="14" />
-            <enum name="iconPreviewSettingsKey" value="15" />
-            <enum name="iconPreviewShortcutKey" value="16" />
+            <enum name="iconPreviewTabKey" value="11" />
+            <enum name="iconPreviewSettingsKey" value="12" />
+            <enum name="iconPreviewShortcutKey" value="13" />
         </attr>
         <!-- The key style to specify a set of key attributes defined by <key_style/> -->
         <attr name="keyStyle" format="string" />
@@ -252,16 +242,6 @@
         <attr name="keyXPos" format="dimension|fraction" />
     </declare-styleable>
 
-    <declare-styleable name="Keyboard_Row">
-        <!-- Row edge flags. -->
-        <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. -->
-            <flag name="bottom" value="8" />
-        </attr>
-    </declare-styleable>
-
     <declare-styleable name="Keyboard_Include">
         <attr name="keyboardLayout" format="reference" />
     </declare-styleable>
@@ -287,8 +267,8 @@
             <enum name="shortcutImeOrSettings" value="3" />
         </attr>
         <attr name="clobberSettingsKey" format="boolean" />
-        <attr name="voiceKeyEnabled" format="boolean" />
-        <attr name="hasVoiceKey" format="boolean" />
+        <attr name="shortcutKeyEnabled" format="boolean" />
+        <attr name="hasShortcutKey" format="boolean" />
         <attr name="imeAction" format="enum">
             <!-- This should be aligned with EditorInfo.IME_ACTION_* -->
             <enum name="actionUnspecified" value="0" />
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index bc64eb2..731f63f 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -20,18 +20,16 @@
 
 <resources>
     <bool name="config_enable_show_settings_key_option">true</bool>
+    <bool name="config_default_show_settings_key">false</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_sliding_key_input_enabled">true</bool>
     <bool name="config_digit_popup_characters_enabled">true</bool>
     <!-- Whether or not Popup on key press is enabled by default -->
     <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>
@@ -41,7 +39,6 @@
     <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 -->
@@ -63,7 +60,7 @@
     <!-- Long pressing shift will invoke caps-lock if > 0, never invoke caps-lock if == 0 -->
     <integer name="config_long_press_shift_key_timeout">1200</integer>
     <!-- Long pressing space will invoke IME switcher if > 0, never invoke IME switcher if == 0 -->
-    <integer name="config_long_press_space_key_timeout">1200</integer>
+    <integer name="config_long_press_space_key_timeout">@integer/config_long_press_key_timeout</integer>
     <integer name="config_touch_noise_threshold_millis">40</integer>
     <integer name="config_double_spaces_turn_into_period_timeout">1100</integer>
     <dimen name="config_touch_noise_threshold_distance">2.0mm</dimen>
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index ff0458c..2c4b35e 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -57,7 +57,7 @@
     <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>
+    <dimen name="keyboard_vertical_correction">0.0in</dimen>
 
     <fraction name="key_letter_ratio">55%</fraction>
     <fraction name="key_large_letter_ratio">65%</fraction>
@@ -86,6 +86,7 @@
     <dimen name="candidate_padding">6dip</dimen>
     <dimen name="candidate_text_size">18dip</dimen>
     <integer name="candidate_count_in_strip">3</integer>
+    <integer name="center_candidate_percentile">36</integer>
 
     <!-- If the screen height in landscape is larger than the below value, then the keyboard
          will not go into extract (fullscreen) mode. -->
diff --git a/java/res/values/donottranslate-altchars.xml b/java/res/values/donottranslate-altchars.xml
index 9d06d06..fbde4b9 100644
--- a/java/res/values/donottranslate-altchars.xml
+++ b/java/res/values/donottranslate-altchars.xml
@@ -50,9 +50,7 @@
     <string name="alternates_for_currency_general">¢,$,€,£,¥,₱</string>
     <string name="alternates_for_smiley">":-)|:-) ,:-(|:-( ,;-)|;-) ,:-P|:-P ,=-O|=-O ,:-*|:-* ,:O|:O ,B-)|B-) ,:-$|:-$ ,:-!|:-! ,:-[|:-[ ,O:-)|O:-) ,:-\\\\\\\\|:-\\\\\\\\ ,:\'(|:\'( ,:-D|:-D "</string>
     <string name="alternates_for_punctuation">"\\,,\?,!,:,-,\',\",(,),/,;,+,&amp;,\@"</string>
-    <string name="alternates_for_web_tab_punctuation">".,\\,,\?,!,:,-,\',\",(,),/,;,+,&amp;,\@"</string>
     <integer name="popup_keyboard_column_for_punctuation">7</integer>
-    <integer name="popup_keyboard_column_for_web_punctuation">8</integer>
     <string name="keyhintlabel_for_punctuation"></string>
     <string name="keylabel_for_popular_domain">".com"</string>
     <!-- popular web domains for the locale - most popular, displayed on the keyboard -->
@@ -77,12 +75,20 @@
     <string name="alternates_for_symbols_8"></string>
     <string name="alternates_for_symbols_9"></string>
     <string name="alternates_for_symbols_0">ⁿ,∅</string>
-    <string name="keylabel_for_symbols_comma">,</string>
+    <string name="keylabel_for_comma">,</string>
+    <string name="keylabel_for_f1">,</string>
     <string name="keylabel_for_symbols_question">\?</string>
     <string name="keylabel_for_symbols_semicolon">;</string>
-    <string name="alternates_for_symbols_comma"></string>
+    <string name="keylabel_for_symbols_percent">%</string>
+    <string name="alternates_for_comma"></string>
+    <string name="alternates_for_f1"></string>
+    <!-- @icon/3 is iconSettingsKey -->
+    <string name="alternates_for_f1_settings">\@icon/3|\@integer/key_settings</string>
+    <!-- @icon/7 is iconTabKey -->
+    <string name="alternates_for_f1_navigate">\@icon/7|\@integer/key_tab</string>
     <string name="alternates_for_symbols_question">¿</string>
     <string name="alternates_for_symbols_semicolon"></string>
+    <string name="alternates_for_symbols_percent">‰</string>
     <string name="keylabel_for_apostrophe">\'</string>
     <string name="keylabel_for_dash">-</string>
     <string name="keyhintlabel_for_apostrophe">\"</string>
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index f88d2df..fb28766 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -19,7 +19,7 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Symbols that are suggested between words -->
-    <string name="suggested_punctuations">!?,\u0022\u0027:();-/@_</string>
+    <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 -->
@@ -27,21 +27,22 @@
     <!-- 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>
+    <string name="symbols_excluded_from_word_separators">\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 -->
+            magic_space_neutral_symbols \ symbols_excluded_from_word_separators -->
 
-    <!-- Label for ALT modifier key.  Must be short to fit on key! -->
-    <string name="label_alt_key">ALT</string>
+    <!-- Label for "switch to more symbol" modifier key.  Must be short to fit on key! -->
+    <string name="label_to_more_symbol_key">= \\ &lt;</string>
+    <!-- Label for "switch to more symbol" modifier key on tablets.  Must be short to fit on key! -->
+    <string name="label_to_more_symbol_for_tablet_key">\\ ^ [ {</string>
+
     <!-- Label for "Tab" key.  Must be short to fit on key! -->
     <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 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>
+    <string name="label_to_phone_symbols_key">\uff0a\uff03</string>
 
     <!-- Character for candidate divider (BOX DRAWINGS LIGHT VERTICAL) -->
     <string name="label_candidate_divider">\u2502</string>
@@ -50,26 +51,6 @@
     <!-- Character for close candidates pane (BLACK UP-POINTING TRIANGLE) -->
     <string name="label_close_candidates_pane">\u25b2</string>
 
-    <!-- Option values to show/hide the settings key in onscreen keyboard -->
-    <!-- Automatically decide to show or hide the settings key -->
-    <string name="settings_key_mode_auto">0</string>
-    <!-- Always show the settings key -->
-    <string name="settings_key_mode_always_show">1</string>
-    <!-- Always hide the settings key -->
-    <string name="settings_key_mode_always_hide">2</string>
-    <!-- Array of the settings key mode values -->
-    <string-array name="settings_key_modes_values">
-        <item>@string/settings_key_mode_auto</item>
-        <item>@string/settings_key_mode_always_show</item>
-        <item>@string/settings_key_mode_always_hide</item>
-    </string-array>
-    <!-- Array of the settings key modes -->
-    <string-array name="settings_key_modes">
-        <item>@string/settings_key_mode_auto_name</item>
-        <item>@string/settings_key_mode_always_show_name</item>
-        <item>@string/settings_key_mode_always_hide_name</item>
-    </string-array>
-
     <!--  Always show the suggestion strip -->
     <string name="prefs_suggestion_visibility_show_value">0</string>
     <!--  Show the suggestion strip only on portrait mode -->
@@ -169,4 +150,7 @@
         <item>English (UK)</item>
         <item>Deutsch (QWERTY)</item>
     </string-array>
+
+    <!-- Generic subtype label -->
+    <string name="subtype_generic">%s</string>
 </resources>
diff --git a/java/res/values/keyboard-icons-black.xml b/java/res/values/keyboard-icons-black.xml
index 313bf02..23d163c 100644
--- a/java/res/values/keyboard-icons-black.xml
+++ b/java/res/values/keyboard-icons-black.xml
@@ -22,18 +22,15 @@
              sym_bkeyboard_voice_off
           -->
         <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>
-        <!-- TODO: update this icon drawable -->
-        <item name="iconDeleteRtlKey">@drawable/sym_bkeyboard_delete_rtl</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="iconShortcutKey">@drawable/sym_bkeyboard_mic</item>
+        <item name="iconShortcutForLabel">@drawable/sym_bkeyboard_label_mic</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>
diff --git a/java/res/values/keyboard-icons-ics.xml b/java/res/values/keyboard-icons-ics.xml
index a2bfd7a..f619691 100644
--- a/java/res/values/keyboard-icons-ics.xml
+++ b/java/res/values/keyboard-icons-ics.xml
@@ -21,18 +21,15 @@
              sym_keyboard_123_mic_holo
              -->
         <item name="iconShiftKey">@drawable/sym_keyboard_shift_holo</item>
-        <item name="iconToSymbolKeyWithShortcut">@drawable/sym_keyboard_123_mic_holo</item>
         <item name="iconDeleteKey">@drawable/sym_keyboard_delete_holo</item>
-        <!-- TODO: update this icon drawable -->
-        <item name="iconDeleteRtlKey">@drawable/sym_keyboard_delete_rtl_holo</item>
         <item name="iconSettingsKey">@drawable/sym_keyboard_settings_holo</item>
-        <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo</item>
         <item name="iconSpaceKey">@drawable/sym_keyboard_space_holo</item>
         <item name="iconReturnKey">@drawable/sym_keyboard_return_holo</item>
         <item name="iconSearchKey">@drawable/sym_keyboard_search</item>
         <item name="iconTabKey">@drawable/sym_keyboard_tab_holo</item>
+        <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo</item>
+        <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic_holo</item>
         <item name="iconShiftedShiftKey">@drawable/sym_keyboard_shift_locked_holo</item>
-        <item name="iconPreviewSpaceKey">@drawable/transparent</item>
         <item name="iconPreviewTabKey">@drawable/sym_keyboard_feedback_tab</item>
         <item name="iconPreviewSettingsKey">@drawable/sym_keyboard_settings_holo</item>
         <item name="iconPreviewShortcutKey">@drawable/sym_keyboard_feedback_mic</item>
diff --git a/java/res/values/keyboard-icons-white.xml b/java/res/values/keyboard-icons-white.xml
index b6fa890..a1765a4 100644
--- a/java/res/values/keyboard-icons-white.xml
+++ b/java/res/values/keyboard-icons-white.xml
@@ -18,18 +18,15 @@
     <style name="KeyboardIcons">
         <!-- 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>
-        <!-- TODO: update this icon drawable -->
-        <item name="iconDeleteRtlKey">@drawable/sym_keyboard_delete_rtl</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="iconShortcutKey">@drawable/sym_keyboard_mic</item>
+        <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic</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>
diff --git a/java/res/values/keycodes.xml b/java/res/values/keycodes.xml
index ee34529..59cc075 100644
--- a/java/res/values/keycodes.xml
+++ b/java/res/values/keycodes.xml
@@ -27,5 +27,5 @@
     <integer name="key_switch_alpha_symbol">-2</integer>
     <integer name="key_delete">-5</integer>
     <integer name="key_settings">-6</integer>
-    <integer name="key_shortcut">-8</integer>
+    <integer name="key_shortcut">-7</integer>
 </resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 739f72f..ed69fc9 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -25,6 +25,9 @@
     <!-- Title for Latin keyboard input options dialog [CHAR LIMIT=25] -->
     <string name="english_ime_input_options">Input options</string>
 
+    <!-- Title for Latin Keyboard spell checker service -->
+    <string name="spell_checker_service_name">Android correction</string>
+
     <!-- Option to provide vibrate/haptic feedback on keypress -->
     <string name="vibrate_on_keypress">Vibrate on keypress</string>
 
@@ -69,11 +72,6 @@
     <!-- Option to configure dictionaries -->
     <string name="configure_dictionaries_title">Configure dictionaries</string>
 
-    <!-- Option to enable quick fixes -->
-    <string name="quick_fixes">Quick fixes</string>
-    <!-- Description for quick fixes -->
-    <string name="quick_fixes_summary">Corrects commonly typed mistakes</string>
-
     <!-- Option to enable showing suggestions -->
     <string name="prefs_show_suggestions">Show correction suggestions</string>
     <!-- Description for show suggestions -->
@@ -81,17 +79,9 @@
     <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>
-    <!-- Option to automatically decide to show/hide the settings key -->
-    <string name="settings_key_mode_auto_name">Automatic</string>
-    <!-- Option to always show the settings key -->
-    <string name="settings_key_mode_always_show_name">Always show</string>
-    <!-- Option to always hide the settings key -->
-    <string name="settings_key_mode_always_hide_name">Always hide</string>
 
     <!-- Option to decide the auto correction threshold score -->
     <!-- Option to enable auto correction [CHAR LIMIT=20]-->
@@ -119,17 +109,22 @@
     <!-- Indicates that a word has been added to the dictionary -->
     <string name="added_word"><xliff:g id="word">%s</xliff:g> : Saved</string>
 
-    <!-- Label for soft enter key when it performs GO action.  Must be short to fit on key! -->
+    <!-- Label for soft enter key when it performs GO action.  Must be short to fit on key! [CHAR LIMIT=5] -->
     <string name="label_go_key">Go</string>
-    <!-- Label for soft enter key when it performs NEXT action.  Must be short to fit on key! -->
+    <!-- Label for soft enter key when it performs NEXT action.  Must be short to fit on key! [CHAR LIMIT=5] -->
     <string name="label_next_key">Next</string>
-    <!-- Label for soft enter key when it performs DONE action.  Must be short to fit on key! -->
+    <!-- Label for soft enter key when it performs DONE action.  Must be short to fit on key! [CHAR LIMIT=5] -->
     <string name="label_done_key">Done</string>
-    <!-- Label for soft enter key when it performs SEND action.  Must be short to fit on key! -->
+    <!-- Label for soft enter key when it performs SEND action.  Must be short to fit on key! [CHAR LIMIT=5] -->
     <string name="label_send_key">Send</string>
-    <!-- Label for "switch to alphabetic" key.  Must be short to fit on key! -->
+    <!-- Label for "switch to alphabetic" key.  Must be short to fit on key! [CHAR LIMIT=3] -->
     <string name="label_to_alpha_key">ABC</string>
-    <!-- Label for Shift modifier key of symbol keyboard.  Must be short to fit on key! -->
+    <!-- Label for "switch to symbols" key.  Must be short to fit on key! [CHAR LIMIT=4] -->
+    <string name="label_to_symbol_key">\?123</string>
+    <!-- Label for "switch to symbols with microphone" key. This string shouldn't include the "mic"
+         part because it'll be appended by the code. Must be short to fit on key! [CHAR LIMIT=3] -->
+    <string name="label_to_symbol_with_microphone_key">123</string>
+    <!-- Label for Shift modifier key of symbol keyboard.  Must be short to fit on key! [CHAR LIMIT=5] -->
     <string name="label_more_key">More</string>
     <!-- Label for "Pause" key of phone number keyboard.  Must be short to fit on key! [CHAR LIMIT=5] -->
     <string name="label_pause_key">Pause</string>
@@ -301,6 +296,9 @@
     <!-- Title of the dialog for selecting input methods. [CHAR LIMIT=20] -->
     <string name="selectInputMethod">Select input method</string>
 
+    <!-- Title for configuring input method settings [CHAR LIMIT=35] -->
+    <string name="configure_input_method">Configure input methods</string>
+
     <!-- Title for input language selection screen -->
     <string name="language_selection_title">Input languages</string>
 
@@ -325,89 +323,12 @@
     <!-- Title of the item to change the keyboard theme [CHAR LIMIT=20]-->
     <string name="keyboard_layout">Keyboard theme</string>
 
-    <!-- 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>
+    <string name="subtype_de_qwerty">German QWERTY</string>
     <!-- Description for English (United Kingdom) keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_en_GB_keyboard">English (UK) Keyboard</string>
+    <string name="subtype_en_GB">English (UK)</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] -->
-    <string name="subtype_mode_cs_voice">Czech Voice</string>
-    <!-- Description for German voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_de_voice">German Voice</string>
-    <!-- Description for English voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_en_voice">English Voice</string>
-    <!-- Description for Spanish voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_es_voice">Spanish Voice</string>
-    <!-- Description for French voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_fr_voice">French Voice</string>
-    <!-- Description for Italian voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_it_voice">Italian Voice</string>
-    <!-- Description for Japanese voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_ja_voice">Japanese Voice</string>
-    <!-- Description for Korean voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_ko_voice">Korean Voice</string>
-    <!-- Description for Dutch voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_nl_voice">Dutch Voice</string>
-    <!-- Description for Polish voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_pl_voice">Polish Voice</string>
-    <!-- Description for Portuguese voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_pt_voice">Portuguese Voice</string>
-    <!-- Description for Russian voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_ru_voice">Russian Voice</string>
-    <!-- Description for Turkish voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_tr_voice">Turkish Voice</string>
-    <!-- Description for Chinese, Yue voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_yue_voice">Chinese, Yue Voice</string>
-    <!-- Description for Chinese, Mandarin voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_zh_voice">Chinese, Mandarin Voice</string>
-    <!-- Description for isiZulu voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_zu_voice">isiZulu Voice</string>
+    <string name="subtype_en_US">English (US)</string>
 
     <!-- Title of an option for usability study mode -->
     <string name="prefs_usability_study_mode">Usability study mode</string>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 8ed3343..1ebd2ce 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -56,7 +56,6 @@
         <item name="keyPreviewBackground">@drawable/keyboard_key_feedback</item>
         <item name="keyPreviewLeftBackground">@null</item>
         <item name="keyPreviewRightBackground">@null</item>
-        <item name="keyPreviewSpacebarBackground">@drawable/keyboard_key_feedback</item>
         <item name="keyPreviewTextColor">#FFFFFFFF</item>
         <item name="keyPreviewOffset">@dimen/key_preview_offset</item>
         <item name="keyPreviewHeight">@dimen/key_preview_height</item>
@@ -91,6 +90,7 @@
         <item name="colorAutoCorrect">#FFFCAE00</item>
         <item name="colorSuggested">#FFFCAE00</item>
         <item name="candidateCountInStrip">@integer/candidate_count_in_strip</item>
+        <item name="centerCandidatePercentile">@integer/center_candidate_percentile</item>
     </style>
     <!-- Theme "Basic high contrast" -->
     <style name="KeyboardView.HighContrast" parent="KeyboardView">
@@ -163,7 +163,6 @@
         <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_ics</item>
         <item name="keyPreviewLeftBackground">@drawable/keyboard_key_feedback_left_ics</item>
         <item name="keyPreviewRightBackground">@drawable/keyboard_key_feedback_right_ics</item>
-        <item name="keyPreviewSpacebarBackground">@drawable/transparent</item>
         <item name="keyPreviewTextColor">#FFFFFFFF</item>
         <item name="keyPreviewHeight">@dimen/key_preview_height_ics</item>
         <item name="keyPreviewOffset">@dimen/key_preview_offset_ics</item>
@@ -194,6 +193,7 @@
         <item name="colorAutoCorrect">#FF3DC8FF</item>
         <item name="colorSuggested">#FFFFFFFF</item>
         <item name="candidateCountInStrip">@integer/candidate_count_in_strip</item>
+        <item name="centerCandidatePercentile">@integer/center_candidate_percentile</item>
     </style>
     <style name="PopupMiniKeyboardAnimation">
         <item name="android:windowEnterAnimation">@anim/mini_keyboard_fadein</item>
diff --git a/java/res/xml-ar/kbd_qwerty.xml b/java/res/xml-ar/kbd_qwerty.xml
index 57a6d2c..b26a938 100644
--- a/java/res/xml-ar/kbd_qwerty.xml
+++ b/java/res/xml-ar/kbd_qwerty.xml
@@ -21,6 +21,7 @@
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
     latin:keyboardLocale="ar"
+    latin:isRtlKeyboard="true"
 >
     <include
         latin:keyboardLayout="@xml/kbd_rows_arabic" />
diff --git a/java/res/xml-ar/kbd_symbols.xml b/java/res/xml-ar/kbd_symbols.xml
new file mode 100644
index 0000000..9e5c255
--- /dev/null
+++ b/java/res/xml-ar/kbd_symbols.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:isRtlKeyboard="true"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_symbols" />
+</Keyboard>
diff --git a/java/res/xml-ar/kbd_symbols_shift.xml b/java/res/xml-ar/kbd_symbols_shift.xml
new file mode 100644
index 0000000..934e6f8
--- /dev/null
+++ b/java/res/xml-ar/kbd_symbols_shift.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:isRtlKeyboard="true"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_symbols_shift" />
+</Keyboard>
diff --git a/java/res/xml-iw/kbd_qwerty.xml b/java/res/xml-iw/kbd_qwerty.xml
index dc2c5d3..54cd4b5 100644
--- a/java/res/xml-iw/kbd_qwerty.xml
+++ b/java/res/xml-iw/kbd_qwerty.xml
@@ -21,6 +21,7 @@
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
     latin:keyboardLocale="iw"
+    latin:isRtlKeyboard="true"
 >
     <include
         latin:keyboardLayout="@xml/kbd_rows_hebrew" />
diff --git a/java/res/xml-iw/kbd_symbols.xml b/java/res/xml-iw/kbd_symbols.xml
new file mode 100644
index 0000000..9e5c255
--- /dev/null
+++ b/java/res/xml-iw/kbd_symbols.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:isRtlKeyboard="true"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_symbols" />
+</Keyboard>
diff --git a/java/res/xml-iw/kbd_symbols_shift.xml b/java/res/xml-iw/kbd_symbols_shift.xml
new file mode 100644
index 0000000..934e6f8
--- /dev/null
+++ b/java/res/xml-iw/kbd_symbols_shift.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:isRtlKeyboard="true"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_symbols_shift" />
+</Keyboard>
diff --git a/java/res/xml-sw600dp/kbd_key_styles.xml b/java/res/xml-sw600dp/kbd_key_styles.xml
index 388e293..05b6837 100644
--- a/java/res/xml-sw600dp/kbd_key_styles.xml
+++ b/java/res/xml-sw600dp/kbd_key_styles.xml
@@ -31,14 +31,14 @@
             latin:clobberSettingsKey="true"
         >
             <key-style
-                latin:styleName="settingsPopupStyle"
+                latin:styleName="f2PopupStyle"
                 latin:parentStyle="functionalKeyStyle" />
         </case>
         <default>
             <key-style
-                latin:styleName="settingsPopupStyle"
+                latin:styleName="f2PopupStyle"
                 latin:keyLabelOption="hasPopupHint"
-                latin:popupCharacters="\@icon/6|\@integer/key_settings"
+                latin:popupCharacters="\@icon/3|\@integer/key_settings"
                 latin:parentStyle="functionalKeyStyle" />
         </default>
     </switch>
@@ -50,24 +50,12 @@
         latin:keyIconShifted="iconShiftedShiftKey"
         latin:parentStyle="functionalKeyStyle"
         latin:isSticky="true" />
-    <switch>
-        <case latin:languageCode="ar|iw">
-            <key-style
-                latin:styleName="deleteKeyStyle"
-                latin:code="@integer/key_delete"
-                latin:keyIcon="iconDeleteRtlKey"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isRepeatable="true" />
-        </case>
-        <default>
-            <key-style
-                latin:styleName="deleteKeyStyle"
-                latin:code="@integer/key_delete"
-                latin:keyIcon="iconDeleteKey"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isRepeatable="true" />
-        </default>
-    </switch>
+    <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"
@@ -75,12 +63,10 @@
         latin:parentStyle="functionalKeyStyle" />
     <key-style
         latin:styleName="spaceKeyStyle"
-        latin:code="@integer/key_space"
-        latin:keyIconPreview="iconPreviewSpaceKey" />
+        latin:code="@integer/key_space" />
     <key-style
         latin:styleName="nonSpecialBackgroundSpaceKeyStyle"
-        latin:code="@integer/key_space"
-        latin:keyIconPreview="iconPreviewSpaceKey" />
+        latin:code="@integer/key_space" />
     <key-style
         latin:styleName="smileyKeyStyle"
         latin:keyLabel=":-)"
@@ -93,7 +79,7 @@
         latin:code="@integer/key_shortcut"
         latin:keyIcon="iconShortcutKey"
         latin:keyIconPreview="iconPreviewShortcutKey"
-        latin:parentStyle="settingsPopupStyle" />
+        latin:parentStyle="f2PopupStyle" />
     <key-style
         latin:styleName="settingsKeyStyle"
         latin:code="@integer/key_settings"
@@ -119,12 +105,17 @@
         latin:keyLabelOption="fontNormal"
         latin:parentStyle="functionalKeyStyle" />
     <key-style
-        latin:styleName="moreKeyStyle"
+        latin:styleName="toMoreSymbolKeyStyle"
         latin:code="@integer/key_shift"
-        latin:keyLabel="@string/label_more_key"
+        latin:keyLabel="@string/label_to_more_symbol_for_tablet_key"
         latin:keyLabelOption="fontNormal"
-        latin:parentStyle="functionalKeyStyle"
-        latin:isSticky="true" />
+        latin:parentStyle="functionalKeyStyle" />
+    <key-style
+        latin:styleName="backFromMoreSymbolKeyStyle"
+        latin:code="@integer/key_shift"
+        latin:keyLabel="@string/label_to_symbol_key"
+        latin:keyLabelOption="fontNormal"
+        latin:parentStyle="functionalKeyStyle" />
     <key-style
         latin:styleName="comKeyStyle"
         latin:keyLabel="@string/keylabel_for_popular_domain"
diff --git a/java/res/xml-sw600dp/kbd_number.xml b/java/res/xml-sw600dp/kbd_number.xml
index b3a1010..46114de 100644
--- a/java/res/xml-sw600dp/kbd_number.xml
+++ b/java/res/xml-sw600dp/kbd_number.xml
@@ -30,11 +30,12 @@
         <case
             latin:passwordInput="true"
         >
-            <!-- This row is intentionally not marked as a top row -->
             <Row>
+                <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                <Spacer
+                    latin:keyWidth="24.875%p" />
                 <Key
-                    latin:keyStyle="num1KeyStyle"
-                    latin:keyXPos="24.875%p" />
+                    latin:keyStyle="num1KeyStyle" />
                 <Key
                     latin:keyStyle="num2KeyStyle" />
                 <Key
@@ -42,13 +43,14 @@
                 <Key
                     latin:keyStyle="deleteKeyStyle"
                     latin:keyXPos="-11.00%p"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
             </Row>
             <Row>
+                <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                <Spacer
+                    latin:keyWidth="24.875%p" />
                 <Key
-                    latin:keyStyle="num4KeyStyle"
-                    latin:keyXPos="24.875%p" />
+                    latin:keyStyle="num4KeyStyle" />
                 <Key
                     latin:keyStyle="num5KeyStyle" />
                 <Key
@@ -56,24 +58,26 @@
                 <Key
                     latin:keyStyle="returnKeyStyle"
                     latin:keyXPos="-11.00%p"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
             </Row>
             <Row>
+                <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                <Spacer
+                    latin:keyWidth="24.875%p" />
                 <Key
-                    latin:keyStyle="num7KeyStyle"
-                    latin:keyXPos="24.875%p" />
+                    latin:keyStyle="num7KeyStyle" />
                 <Key
                     latin:keyStyle="num8KeyStyle" />
                 <Key
                     latin:keyStyle="num9KeyStyle" />
+                <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+                <Spacer
+                    latin:keyWidth="0%p" />
             </Row>
-            <!-- This row is intentionally not marked as a bottom row -->
             <Row>
                 <Key
                     latin:keyStyle="tabKeyStyle"
-                    latin:keyWidth="11.00%p"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyWidth="11.00%p" />
                 <Spacer
                     latin:keyXPos="24.875%p" />
                 <Key
@@ -87,12 +91,13 @@
         </case>
         <!-- latin:passwordInput="false" -->
         <default>
-            <!-- This row is intentionally not marked as a top row -->
             <Row>
+                <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                <Spacer
+                    latin:keyWidth="11.00%p" />
                 <Key
                     latin:keyLabel="-"
                     latin:keyStyle="numKeyStyle"
-                    latin:keyXPos="11.00%p"
                     latin:keyWidth="9.25%p" />
                 <Key
                     latin:keyLabel="+"
@@ -115,13 +120,14 @@
                 <Key
                     latin:keyStyle="deleteKeyStyle"
                     latin:keyXPos="-11.00%p"
-                    latin:keyWidth="fillBoth"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillBoth" />
             </Row>
             <Row>
+                <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                <Spacer
+                    latin:keyWidth="11.00%p" />
                 <Key
                     latin:keyStyle="numStarKeyStyle"
-                    latin:keyXPos="11.00%p"
                     latin:keyWidth="9.25%p" />
                 <Key
                     latin:keyLabel="/"
@@ -144,17 +150,15 @@
                 <Key
                     latin:keyStyle="returnKeyStyle"
                     latin:keyXPos="-11.00%p"
-                    latin:keyWidth="fillBoth"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillBoth" />
             </Row>
             <Row>
-                <!-- 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. -->
+                <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                <Spacer
+                    latin:keyWidth="11.00%p" />
                 <Key
                     latin:keyLabel="("
                     latin:keyStyle="numKeyStyle"
-                    latin:keyXPos="11.00%p"
                     latin:keyWidth="9.25%p" />
                 <Key
                     latin:keyLabel=")"
@@ -174,13 +178,14 @@
                 <Key
                     latin:keyLabel="9"
                     latin:keyStyle="numKeyStyle" />
+                <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+                <Spacer
+                    latin:keyWidth="0%p" />
             </Row>
-            <!-- This row is intentionally not marked as a bottom row -->
             <Row>
                 <Key
                     latin:keyStyle="tabKeyStyle"
-                    latin:keyWidth="11.00%p"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyWidth="11.00%p" />
                 <Key
                     latin:keyStyle="nonSpecialBackgroundSpaceKeyStyle"
                     latin:keyWidth="27.75%p" />
diff --git a/java/res/xml-sw600dp/kbd_phone.xml b/java/res/xml-sw600dp/kbd_phone.xml
index dd53a20..0331389 100644
--- a/java/res/xml-sw600dp/kbd_phone.xml
+++ b/java/res/xml-sw600dp/kbd_phone.xml
@@ -26,12 +26,13 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <include
         latin:keyboardLayout="@xml/kbd_numkey_styles" />
-    <!-- This row is intentionally not marked as a top row -->
     <Row>
+        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+        <Spacer
+            latin:keyWidth="15.625%p" />
         <Key
             latin:keyLabel="-"
             latin:keyStyle="numKeyStyle"
-            latin:keyXPos="15.625%p"
             latin:keyWidth="9.25%p" />
         <Key
             latin:keyLabel="+"
@@ -47,14 +48,15 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-11.00%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row>
+        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+        <Spacer
+            latin:keyWidth="15.625%p" />
         <Key
             latin:keyLabel=","
             latin:keyStyle="numKeyStyle"
-            latin:keyXPos="15.625%p"
             latin:keyWidth="9.25%p" />
         <Key
             latin:keyLabel="."
@@ -70,16 +72,12 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-11.00%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row>
         <Key
-            latin:keyStyle="moreKeyStyle"
-            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. -->
+            latin:keyStyle="toMoreSymbolKeyStyle"
+            latin:keyWidth="11.0%p" />
         <Key
             latin:keyLabel="("
             latin:keyStyle="numKeyStyle"
@@ -96,13 +94,14 @@
             latin:keyStyle="num8KeyStyle" />
         <Key
             latin:keyStyle="num9KeyStyle" />
+        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+        <Spacer
+            latin:keyWidth="0%p" />
         </Row>
-    <!-- This row is intentionally not marked as a bottom row -->
     <Row>
         <Key
             latin:keyStyle="tabKeyStyle"
-            latin:keyWidth="11.00%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="11.00%p" />
         <Key
             latin:keyStyle="nonSpecialBackgroundSpaceKeyStyle"
             latin:keyXPos="15.625%p"
diff --git a/java/res/xml-sw600dp/kbd_phone_symbols.xml b/java/res/xml-sw600dp/kbd_phone_shift.xml
similarity index 82%
rename from java/res/xml-sw600dp/kbd_phone_symbols.xml
rename to java/res/xml-sw600dp/kbd_phone_shift.xml
index d083ecb..2b6936d 100644
--- a/java/res/xml-sw600dp/kbd_phone_symbols.xml
+++ b/java/res/xml-sw600dp/kbd_phone_shift.xml
@@ -26,12 +26,13 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <include
         latin:keyboardLayout="@xml/kbd_numkey_styles" />
-    <!-- This row is intentionally not marked as a top row -->
     <Row>
+        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+        <Spacer
+            latin:keyWidth="11.00%p" />
         <Key
             latin:keyLabel="-"
             latin:keyStyle="numKeyStyle"
-            latin:keyXPos="11.00%p"
             latin:keyWidth="9.25%p" />
         <Key
             latin:keyLabel="+"
@@ -51,14 +52,15 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-11.00%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row>
+        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+        <Spacer
+            latin:keyWidth="11.00%p" />
         <Key
             latin:keyLabel=","
             latin:keyStyle="numKeyStyle"
-            latin:keyXPos="11.00%p"
             latin:keyWidth="9.25%p" />
         <Key
             latin:keyLabel="."
@@ -78,16 +80,12 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-11.00%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row>
         <Key
-            latin:keyStyle="moreKeyStyle"
-            latin:keyWidth="11.00%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. -->
+            latin:keyStyle="backFromMoreSymbolKeyStyle"
+            latin:keyWidth="11.00%p" />
         <Key
             latin:keyLabel="("
             latin:keyStyle="numKeyStyle"
@@ -107,13 +105,14 @@
             latin:keyStyle="num8KeyStyle" />
         <Key
             latin:keyStyle="num9KeyStyle" />
+        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+        <Spacer
+            latin:keyWidth="0%p" />
     </Row>
-    <!-- This row is intentionally not marked as a bottom row -->
     <Row>
         <Key
             latin:keyStyle="tabKeyStyle"
-            latin:keyWidth="11.00%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="11.00%p" />
         <Key
             latin:keyStyle="nonSpecialBackgroundSpaceKeyStyle"
             latin:keyWidth="27.75%p" />
diff --git a/java/res/xml-sw600dp/kbd_qwerty_f2.xml b/java/res/xml-sw600dp/kbd_qwerty_f2.xml
index 7638ee4..b25afc1 100644
--- a/java/res/xml-sw600dp/kbd_qwerty_f2.xml
+++ b/java/res/xml-sw600dp/kbd_qwerty_f2.xml
@@ -27,22 +27,20 @@
         >
             <Key
                 latin:keyStyle="settingsKeyStyle"
-                latin:keyWidth="fillBoth"
-                latin:keyEdgeFlags="right" />
+                latin:keyWidth="fillBoth" />
         </case>
         <case
             latin:f2KeyMode="shortcutIme"
         >
             <switch>
                 <case
-                    latin:voiceKeyEnabled="true"
+                    latin:shortcutKeyEnabled="true"
                 >
                     <Key
                         latin:keyStyle="shortcutKeyStyle"
-                        latin:keyWidth="fillBoth"
-                        latin:keyEdgeFlags="right" />
+                        latin:keyWidth="fillBoth" />
                 </case>
-                <!-- voiceKeyEnabled="false" -->
+                <!-- shortcutKeyEnabled="false" -->
                 <default>
                     <Spacer />
                 </default>
@@ -53,19 +51,17 @@
         >
             <switch>
                 <case
-                    latin:voiceKeyEnabled="true"
+                    latin:shortcutKeyEnabled="true"
                 >
                     <Key
                         latin:keyStyle="shortcutKeyStyle"
-                        latin:keyWidth="fillBoth"
-                        latin:keyEdgeFlags="right" />
+                        latin:keyWidth="fillBoth" />
                 </case>
-                <!-- voiceKeyEnabled="false" -->
+                <!-- shortcutKeyEnabled="false" -->
                 <default>
                     <Key
                         latin:keyStyle="settingsKeyStyle"
-                        latin:keyWidth="fillBoth"
-                        latin:keyEdgeFlags="right" />
+                        latin:keyWidth="fillBoth" />
                 </default>
             </switch>
         </case>
diff --git a/java/res/xml-sw600dp/kbd_qwerty_row1.xml b/java/res/xml-sw600dp/kbd_qwerty_row1.xml
index b781d68..c7dfcc8 100644
--- a/java/res/xml-sw600dp/kbd_qwerty_row1.xml
+++ b/java/res/xml-sw600dp/kbd_qwerty_row1.xml
@@ -23,12 +23,10 @@
 >
     <Row
         latin:keyWidth="9.0%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
             latin:keyLabel="q"
-            latin:popupCharacters="@string/alternates_for_q"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="@string/alternates_for_q" />
         <Key
             latin:keyLabel="w"
             latin:popupCharacters="@string/alternates_for_w" />
@@ -59,7 +57,6 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
 </merge>
diff --git a/java/res/xml-sw600dp/kbd_qwerty_row2.xml b/java/res/xml-sw600dp/kbd_qwerty_row2.xml
index 05b005a..78004fe 100644
--- a/java/res/xml-sw600dp/kbd_qwerty_row2.xml
+++ b/java/res/xml-sw600dp/kbd_qwerty_row2.xml
@@ -27,8 +27,7 @@
         <Key
             latin:keyLabel="a"
             latin:popupCharacters="@string/alternates_for_a"
-            latin:keyXPos="4.5%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyXPos="4.5%p" />
         <Key
             latin:keyLabel="s"
             latin:popupCharacters="@string/alternates_for_s" />
@@ -53,7 +52,6 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
 </merge>
diff --git a/java/res/xml-sw600dp/kbd_qwerty_row3.xml b/java/res/xml-sw600dp/kbd_qwerty_row3.xml
index b4eed8a..456f3c8 100644
--- a/java/res/xml-sw600dp/kbd_qwerty_row3.xml
+++ b/java/res/xml-sw600dp/kbd_qwerty_row3.xml
@@ -26,8 +26,7 @@
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="10.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="10.0%p" />
         <Key
             latin:keyLabel="z"
             latin:popupCharacters="@string/alternates_for_z" />
diff --git a/java/res/xml-sw600dp/kbd_qwerty_row4.xml b/java/res/xml-sw600dp/kbd_qwerty_row4.xml
index 5cd47d9..f22b69f 100644
--- a/java/res/xml-sw600dp/kbd_qwerty_row4.xml
+++ b/java/res/xml-sw600dp/kbd_qwerty_row4.xml
@@ -21,15 +21,13 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <!-- This row is intentionally not marked as a bottom row -->
     <Row
         latin:keyWidth="8.9%p"
     >
         <Key
             latin:keyStyle="toSymbolKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="13.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="13.0%p" />
         <Key
             latin:keyStyle="tabKeyStyle" />
         <switch>
diff --git a/java/res/xml-sw600dp/kbd_row3_smiley.xml b/java/res/xml-sw600dp/kbd_row3_smiley.xml
index 5e9584f..0ae3ae4 100644
--- a/java/res/xml-sw600dp/kbd_row3_smiley.xml
+++ b/java/res/xml-sw600dp/kbd_row3_smiley.xml
@@ -28,8 +28,7 @@
             <Key
                 latin:keyLabel="\@"
                 latin:keyXPos="-8.9%p"
-                latin:keyWidth="fillBoth"
-                latin:keyEdgeFlags="right" />
+                latin:keyWidth="fillBoth" />
         </case>
         <case
             latin:mode="url"
@@ -40,8 +39,7 @@
                 latin:keyHintLabel="_"
                 latin:popupCharacters="_"
                 latin:keyXPos="-8.9%p"
-                latin:keyWidth="fillBoth"
-                latin:keyEdgeFlags="right" />
+                latin:keyWidth="fillBoth" />
         </case>
         <case
             latin:imeAction="actionSearch"
@@ -52,15 +50,13 @@
                 latin:keyHintLabel="+"
                 latin:popupCharacters="+"
                 latin:keyXPos="-8.9%p"
-                latin:keyWidth="fillBoth"
-                latin:keyEdgeFlags="right" />
+                latin:keyWidth="fillBoth" />
         </case>
         <default>
             <Key
                 latin:keyStyle="smileyKeyStyle"
                 latin:keyXPos="-8.9%p"
-                latin:keyWidth="fillBoth"
-                latin:keyEdgeFlags="right" />
+                latin:keyWidth="fillBoth" />
         </default>
     </switch>
 </merge>
diff --git a/java/res/xml-sw600dp/kbd_rows_arabic.xml b/java/res/xml-sw600dp/kbd_rows_arabic.xml
index 4c19094..4c20f51 100644
--- a/java/res/xml-sw600dp/kbd_rows_arabic.xml
+++ b/java/res/xml-sw600dp/kbd_rows_arabic.xml
@@ -27,8 +27,7 @@
         latin:keyWidth="8.0%p"
     >
         <Key
-            latin:keyLabel="ض"
-            latin:keyEdgeFlags="left" />
+            latin:keyLabel="ض" />
         <Key
             latin:keyLabel="ص" />
         <Key
@@ -57,8 +56,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="8.0%p"
@@ -105,8 +103,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="8.0%p"
@@ -117,8 +114,7 @@
                 latin:mode="email"
             >
                 <Key
-                    latin:keyLabel="\@"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyLabel="\@" />
             </case>
             <case
                 latin:mode="url"
@@ -127,8 +123,7 @@
                     latin:keyLabel="-"
                     latin:keyLabelOption="hasUppercaseLetter"
                     latin:keyHintLabel="_"
-                    latin:popupCharacters="_"
-                    latin:keyEdgeFlags="left" />
+                    latin:popupCharacters="_" />
             </case>
             <case
                 latin:imeAction="actionSearch"
@@ -137,13 +132,11 @@
                     latin:keyLabel=":"
                     latin:keyLabelOption="hasUppercaseLetter"
                     latin:keyHintLabel="+"
-                    latin:popupCharacters="+"
-                    latin:keyEdgeFlags="left" />
+                    latin:popupCharacters="+" />
             </case>
             <default>
                 <Key
-                    latin:keyStyle="smileyKeyStyle"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyStyle="smileyKeyStyle" />
             </default>
         </switch>
         <Key
diff --git a/java/res/xml-sw600dp/kbd_rows_azerty.xml b/java/res/xml-sw600dp/kbd_rows_azerty.xml
index 698ef22..406b4e6 100644
--- a/java/res/xml-sw600dp/kbd_rows_azerty.xml
+++ b/java/res/xml-sw600dp/kbd_rows_azerty.xml
@@ -25,12 +25,10 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
         latin:keyWidth="8.5%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
             latin:keyLabel="a"
-            latin:popupCharacters="@string/alternates_for_a"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="@string/alternates_for_a" />
         <Key
             latin:keyLabel="z"
             latin:popupCharacters="@string/alternates_for_z" />
@@ -61,8 +59,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="8.5%p"
@@ -70,8 +67,7 @@
         <Key
             latin:keyLabel="q"
             latin:popupCharacters="@string/alternates_for_q"
-            latin:keyXPos="5.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyXPos="5.0%p" />
         <Key
             latin:keyLabel="s"
             latin:popupCharacters="@string/alternates_for_s" />
@@ -98,16 +94,14 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="8.5%p"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="10.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="10.0%p" />
         <Key
             latin:keyLabel="w"
             latin:popupCharacters="@string/alternates_for_w" />
diff --git a/java/res/xml-sw600dp/kbd_rows_hebrew.xml b/java/res/xml-sw600dp/kbd_rows_hebrew.xml
index 8740915..fc2247c 100644
--- a/java/res/xml-sw600dp/kbd_rows_hebrew.xml
+++ b/java/res/xml-sw600dp/kbd_rows_hebrew.xml
@@ -25,7 +25,6 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
         latin:keyWidth="9.0%p"
-        latin:rowEdgeFlags="top"
     >
         <include
             latin:keyboardLayout="@xml/kbd_row4_apostrophe_dash" />
@@ -48,8 +47,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-12.000%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="9.0%p"
@@ -89,8 +87,7 @@
             >
                 <Key
                     latin:keyLabel="\@"
-                    latin:keyWidth="10.0%p"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyWidth="10.0%p" />
             </case>
             <case
                 latin:mode="url"
@@ -100,8 +97,7 @@
                     latin:keyLabelOption="hasUppercaseLetter"
                     latin:keyHintLabel="_"
                     latin:popupCharacters="_"
-                    latin:keyWidth="10.0%p"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyWidth="10.0%p" />
             </case>
             <case
                 latin:imeAction="actionSearch"
@@ -111,14 +107,12 @@
                     latin:keyLabelOption="hasUppercaseLetter"
                     latin:keyHintLabel="+"
                     latin:popupCharacters="+"
-                    latin:keyWidth="10.0%p"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyWidth="10.0%p" />
             </case>
             <default>
                 <Key
                     latin:keyStyle="smileyKeyStyle"
-                    latin:keyWidth="10.0%p"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyWidth="10.0%p" />
             </default>
         </switch>
         <Key
@@ -146,8 +140,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-10.400%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
diff --git a/java/res/xml-sw600dp/kbd_rows_qwertz.xml b/java/res/xml-sw600dp/kbd_rows_qwertz.xml
index 7ec895e..3d177e7 100644
--- a/java/res/xml-sw600dp/kbd_rows_qwertz.xml
+++ b/java/res/xml-sw600dp/kbd_rows_qwertz.xml
@@ -25,12 +25,10 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
         latin:keyWidth="9.0%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
             latin:keyLabel="q"
-            latin:popupCharacters="@string/alternates_for_q"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="@string/alternates_for_q" />
         <Key
             latin:keyLabel="w"
             latin:popupCharacters="@string/alternates_for_w" />
@@ -61,8 +59,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row2" />
@@ -71,8 +68,7 @@
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="10.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="10.0%p" />
         <Key
             latin:keyLabel="y"
             latin:popupCharacters="@string/alternates_for_y" />
diff --git a/java/res/xml-sw600dp/kbd_rows_russian.xml b/java/res/xml-sw600dp/kbd_rows_russian.xml
index 4452989..7588f6c 100644
--- a/java/res/xml-sw600dp/kbd_rows_russian.xml
+++ b/java/res/xml-sw600dp/kbd_rows_russian.xml
@@ -25,11 +25,9 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
         latin:keyWidth="7.60%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
-            latin:keyLabel="й"
-            latin:keyEdgeFlags="left" />
+            latin:keyLabel="й" />
         <Key
             latin:keyLabel="ц" />
         <Key
@@ -56,8 +54,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.60%p"
@@ -88,15 +85,13 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.60%p"
     >
         <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="shiftKeyStyle" />
         <Key
             latin:keyLabel="я" />
         <Key
diff --git a/java/res/xml-sw600dp/kbd_rows_scandinavian.xml b/java/res/xml-sw600dp/kbd_rows_scandinavian.xml
index 5ac2b36..5059137 100644
--- a/java/res/xml-sw600dp/kbd_rows_scandinavian.xml
+++ b/java/res/xml-sw600dp/kbd_rows_scandinavian.xml
@@ -25,12 +25,10 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
         latin:keyWidth="7.9%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
             latin:keyLabel="q"
-            latin:popupCharacters="@string/alternates_for_q"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="@string/alternates_for_q" />
         <Key
             latin:keyLabel="w"
             latin:popupCharacters="@string/alternates_for_w" />
@@ -63,8 +61,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.9%p"
@@ -72,8 +69,7 @@
         <Key
             latin:keyLabel="a"
             latin:keyXPos="3.5%p"
-            latin:popupCharacters="@string/alternates_for_a"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="@string/alternates_for_a" />
         <Key
             latin:keyLabel="s"
             latin:popupCharacters="@string/alternates_for_s" />
@@ -104,16 +100,14 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.9%p"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="10.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="10.0%p" />
         <Spacer
             latin:keyWidth="4.35%p" />
         <Key
diff --git a/java/res/xml-sw600dp/kbd_rows_serbian.xml b/java/res/xml-sw600dp/kbd_rows_serbian.xml
index 5d39fd4..db7560c 100644
--- a/java/res/xml-sw600dp/kbd_rows_serbian.xml
+++ b/java/res/xml-sw600dp/kbd_rows_serbian.xml
@@ -25,12 +25,10 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
         latin:keyWidth="8.0%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
             latin:keyLabel="љ"
-            latin:keyXPos="2.15%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyXPos="2.15%p" />
         <Key
             latin:keyLabel="њ" />
         <Key
@@ -54,15 +52,13 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.9%p"
     >
         <Key
-            latin:keyLabel="а"
-            latin:keyEdgeFlags="left" />
+            latin:keyLabel="а" />
         <Key
             latin:keyLabel="с" />
         <Key
@@ -86,16 +82,14 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.5%p"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="8.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="8.0%p" />
         <Key
             latin:keyLabel="ѕ" />
         <Key
diff --git a/java/res/xml-sw600dp/kbd_rows_spanish.xml b/java/res/xml-sw600dp/kbd_rows_spanish.xml
index 7ef44bb..49243eb 100644
--- a/java/res/xml-sw600dp/kbd_rows_spanish.xml
+++ b/java/res/xml-sw600dp/kbd_rows_spanish.xml
@@ -31,8 +31,7 @@
         <Key
             latin:keyLabel="a"
             latin:popupCharacters="@string/alternates_for_a"
-            latin:keyXPos="5.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyXPos="5.0%p" />
         <Key
             latin:keyLabel="s"
             latin:popupCharacters="@string/alternates_for_s" />
@@ -59,8 +58,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row3" />
diff --git a/java/res/xml-sw600dp/kbd_symbols.xml b/java/res/xml-sw600dp/kbd_rows_symbols.xml
similarity index 88%
rename from java/res/xml-sw600dp/kbd_symbols.xml
rename to java/res/xml-sw600dp/kbd_rows_symbols.xml
index dea08dd..4f6a9bc 100644
--- a/java/res/xml-sw600dp/kbd_symbols.xml
+++ b/java/res/xml-sw600dp/kbd_rows_symbols.xml
@@ -18,21 +18,19 @@
 */
 -->
 
-<Keyboard
+<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_currency_key_styles" />
-    <!-- This row is intentionally not marked as a top row -->
     <Row
         latin:keyWidth="9.0%p"
     >
         <Key
             latin:keyLabel="@string/keylabel_for_symbols_1"
-            latin:popupCharacters="@string/alternates_for_symbols_1"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="@string/alternates_for_symbols_1" />
         <Key
             latin:keyLabel="@string/keylabel_for_symbols_2"
             latin:popupCharacters="@string/alternates_for_symbols_2" />
@@ -63,8 +61,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="9.0%p"
@@ -75,8 +72,8 @@
         <Key
             latin:keyStyle="currencyKeyStyle" />
         <Key
-            latin:keyLabel="%"
-            latin:popupCharacters="‰" />
+            latin:keyLabel="@string/keylabel_for_symbols_percent"
+            latin:popupCharacters="@string/alternates_for_symbols_percent" />
         <Key
             latin:keyLabel="&amp;" />
         <Key
@@ -97,16 +94,14 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="8.9%p"
     >
         <Key
-            latin:keyStyle="moreKeyStyle"
-            latin:keyWidth="10.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="toMoreSymbolKeyStyle"
+            latin:keyWidth="10.0%p" />
         <Key
             latin:keyLabel="&lt;"
             latin:popupCharacters="≤,«,‹" />
@@ -133,8 +128,8 @@
             latin:keyLabel="@string/keylabel_for_symbols_semicolon"
             latin:popupCharacters="@string/alternates_for_symbols_semicolon" />
         <Key
-            latin:keyLabel="@string/keylabel_for_symbols_comma"
-            latin:popupCharacters="@string/alternates_for_symbols_comma" />
+            latin:keyLabel="@string/keylabel_for_comma"
+            latin:popupCharacters="@string/alternates_for_comma" />
         <Key
             latin:keyLabel="." />
         <Key
@@ -147,15 +142,13 @@
             latin:keyLabel="/"
             latin:keyWidth="fillRight" />
     </Row>
-    <!-- This row is intentionally not marked as a bottom row -->
     <Row
         latin:keyWidth="8.9%p"
     >
         <Key
             latin:keyStyle="toAlphaKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="13.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="13.0%p" />
         <Key
             latin:keyStyle="tabKeyStyle" />
         <Key
@@ -177,4 +170,4 @@
         <include
             latin:keyboardLayout="@xml/kbd_qwerty_f2" />
     </Row>
-</Keyboard>
+</merge>
diff --git a/java/res/xml-sw600dp/kbd_symbols_shift.xml b/java/res/xml-sw600dp/kbd_rows_symbols_shift.xml
similarity index 89%
rename from java/res/xml-sw600dp/kbd_symbols_shift.xml
rename to java/res/xml-sw600dp/kbd_rows_symbols_shift.xml
index b25292f..1dca8c4 100644
--- a/java/res/xml-sw600dp/kbd_symbols_shift.xml
+++ b/java/res/xml-sw600dp/kbd_rows_symbols_shift.xml
@@ -18,12 +18,11 @@
 */
 -->
 
-<Keyboard
+<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="9.0%p"
     >
@@ -60,8 +59,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-10.0%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="9.0%p"
@@ -97,16 +95,14 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-14.6%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="8.9%p"
     >
         <Key
-            latin:keyStyle="moreKeyStyle"
-            latin:keyWidth="10.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="backFromMoreSymbolKeyStyle"
+            latin:keyWidth="10.0%p" />
         <Key
             latin:keyLabel="\\" />
         <Key
@@ -132,15 +128,13 @@
             latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="¿" />
     </Row>
-    <!-- This row is intentionally not marked as a bottom row -->
     <Row
         latin:keyWidth="8.9%p"
     >
         <Key
             latin:keyStyle="toAlphaKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="13.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="13.0%p" />
         <Key
             latin:keyStyle="tabKeyStyle" />
         <Key
@@ -153,4 +147,4 @@
         <include
             latin:keyboardLayout="@xml/kbd_qwerty_f2" />
     </Row>
-</Keyboard>
+</merge>
diff --git a/java/res/xml-sw768dp/kbd_key_styles.xml b/java/res/xml-sw768dp/kbd_key_styles.xml
index ab8784a..1711c42 100644
--- a/java/res/xml-sw768dp/kbd_key_styles.xml
+++ b/java/res/xml-sw768dp/kbd_key_styles.xml
@@ -32,24 +32,12 @@
         latin:keyIconShifted="iconShiftedShiftKey"
         latin:parentStyle="functionalKeyStyle"
         latin:isSticky="true" />
-    <switch>
-        <case latin:languageCode="ar|iw">
-            <key-style
-                latin:styleName="deleteKeyStyle"
-                latin:code="@integer/key_delete"
-                latin:keyIcon="iconDeleteRtlKey"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isRepeatable="true" />
-        </case>
-        <default>
-            <key-style
-                latin:styleName="deleteKeyStyle"
-                latin:code="@integer/key_delete"
-                latin:keyIcon="iconDeleteKey"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isRepeatable="true" />
-        </default>
-    </switch>
+    <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"
@@ -57,12 +45,10 @@
         latin:parentStyle="functionalKeyStyle" />
     <key-style
         latin:styleName="spaceKeyStyle"
-        latin:code="@integer/key_space"
-        latin:keyIconPreview="iconPreviewSpaceKey" />
+        latin:code="@integer/key_space" />
     <key-style
         latin:styleName="nonSpecialBackgroundSpaceKeyStyle"
-        latin:code="@integer/key_space"
-        latin:keyIconPreview="iconPreviewSpaceKey" />
+        latin:code="@integer/key_space" />
     <key-style
         latin:styleName="smileyKeyStyle"
         latin:keyLabel=":-)"
@@ -101,12 +87,17 @@
         latin:keyLabelOption="fontNormal"
         latin:parentStyle="functionalKeyStyle" />
     <key-style
-        latin:styleName="moreKeyStyle"
+        latin:styleName="toMoreSymbolKeyStyle"
         latin:code="@integer/key_shift"
-        latin:keyLabel="@string/label_more_key"
+        latin:keyLabel="@string/label_to_more_symbol_for_tablet_key"
         latin:keyLabelOption="fontNormal"
-        latin:parentStyle="functionalKeyStyle"
-        latin:isSticky="true" />
+        latin:parentStyle="functionalKeyStyle" />
+    <key-style
+        latin:styleName="backFromMoreSymbolKeyStyle"
+        latin:code="@integer/key_shift"
+        latin:keyLabel="@string/label_to_symbol_key"
+        latin:keyLabelOption="fontNormal"
+        latin:parentStyle="functionalKeyStyle" />
     <key-style
         latin:styleName="comKeyStyle"
         latin:keyLabel="@string/keylabel_for_popular_domain"
diff --git a/java/res/xml-sw768dp/kbd_number.xml b/java/res/xml-sw768dp/kbd_number.xml
index a3bed2f..369e91a 100644
--- a/java/res/xml-sw768dp/kbd_number.xml
+++ b/java/res/xml-sw768dp/kbd_number.xml
@@ -30,13 +30,11 @@
         <case
             latin:passwordInput="true"
         >
-            <!-- This row is intentionally not marked as a top row -->
             <Row>
                 <Key
                     latin:keyStyle="tabKeyStyle"
                     latin:keyLabelOption="alignLeft"
-                    latin:keyWidth="11.172%p"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyWidth="11.172%p" />
                 <Key
                     latin:keyStyle="num1KeyStyle"
                     latin:keyXPos="32.076%p" />
@@ -47,13 +45,14 @@
                 <Key
                     latin:keyStyle="deleteKeyStyle"
                     latin:keyXPos="-11.172%p"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
             </Row>
             <Row>
+                <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                <Spacer
+                    latin:keyWidth="32.076%p" />
                 <Key
-                    latin:keyStyle="num4KeyStyle"
-                    latin:keyXPos="32.076%p" />
+                    latin:keyStyle="num4KeyStyle" />
                 <Key
                     latin:keyStyle="num5KeyStyle" />
                 <Key
@@ -61,41 +60,39 @@
                 <Key
                     latin:keyStyle="returnKeyStyle"
                     latin:keyXPos="-11.172%p"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
             </Row>
             <Row>
+                <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                <Spacer
+                    latin:keyWidth="32.076%p" />
                 <Key
-                    latin:keyStyle="num7KeyStyle"
-                    latin:keyXPos="32.076%p" />
+                    latin:keyStyle="num7KeyStyle" />
                 <Key
                     latin:keyStyle="num8KeyStyle" />
                 <Key
                     latin:keyStyle="num9KeyStyle" />
-                <!-- There is an empty area below the "Enter" key and right of the "9" key. To
-                     ignore the touch event on the area, "9" is intentionally not marked as a right
-                     edge key. -->
+                <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+                <Spacer
+                    latin:keyWidth="0%p" />
             </Row>
-            <!-- This row is intentionally not marked as a bottom row -->
             <Row>
                 <Spacer
                     latin:keyXPos="32.076%p" />
                 <Key
                     latin:keyStyle="num0KeyStyle" />
-                <!-- There is an empty area below the "Enter" key and right of the "#" key. To
-                     ignore the touch event on the area, "#" is intentionally not marked as a right
-                     edge key. -->
+                <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+                <Spacer
+                    latin:keyWidth="0%p" />
             </Row>
         </case>
         <!-- latin:passwordInput="false" -->
         <default>
-            <!-- This row is intentionally not marked as a top row -->
             <Row>
                 <Key
                     latin:keyStyle="tabKeyStyle"
                     latin:keyLabelOption="alignLeft"
-                    latin:keyWidth="11.172%p"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyWidth="11.172%p" />
                 <Key
                     latin:keyLabel="-"
                     latin:keyStyle="numKeyStyle"
@@ -122,13 +119,14 @@
                 <Key
                     latin:keyStyle="deleteKeyStyle"
                     latin:keyXPos="-11.172%p"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
             </Row>
             <Row>
+                <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                <Spacer
+                    latin:keyWidth="13.829%p" />
                 <Key
                     latin:keyStyle="numStarKeyStyle"
-                    latin:keyXPos="13.829%p"
                     latin:keyWidth="8.047%p" />
                 <Key
                     latin:keyLabel="/"
@@ -151,17 +149,15 @@
                 <Key
                     latin:keyStyle="returnKeyStyle"
                     latin:keyXPos="-11.172%p"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
             </Row>
             <Row>
-                <!-- 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. -->
+                <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                <Spacer
+                    latin:keyWidth="13.829%p" />
                 <Key
                     latin:keyLabel="("
                     latin:keyStyle="numKeyStyle"
-                    latin:keyXPos="13.829%p"
                     latin:keyWidth="8.047%p" />
                 <Key
                     latin:keyLabel=")"
@@ -181,11 +177,10 @@
                 <Key
                     latin:keyLabel="9"
                     latin:keyStyle="numKeyStyle" />
-                <!-- There is an empty area below the "Enter" key and right of the "9" key. To
-                     ignore the touch event on the area, "9" is intentionally not marked as a right
-                     edge key. -->
+                <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+                <Spacer
+                    latin:keyWidth="0%p" />
             </Row>
-            <!-- This row is intentionally not marked as a bottom row -->
             <Row>
                 <switch>
                     <case latin:hasSettingsKey="true">
@@ -193,6 +188,11 @@
                             latin:keyStyle="settingsKeyStyle"
                             latin:keyWidth="8.047%p" />
                     </case>
+                    <default>
+                        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                        <Spacer
+                            latin:keyWidth="8.047%p" />
+                    </default>
                 </switch>
                 <Key
                     latin:keyStyle="nonSpecialBackgroundSpaceKeyStyle"
@@ -209,13 +209,18 @@
                     latin:keyStyle="numKeyStyle" />
                 <switch>
                     <case
-                        latin:voiceKeyEnabled="true"
+                        latin:shortcutKeyEnabled="true"
                     >
                         <Key
                             latin:keyStyle="shortcutKeyStyle"
                             latin:keyXPos="-8.047%p"
                             latin:keyWidth="fillRight" />
                     </case>
+                    <default>
+                        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+                        <Spacer
+                            latin:keyWidth="0%p" />
+                    </default>
                 </switch>
             </Row>
         </default>
diff --git a/java/res/xml-sw768dp/kbd_phone.xml b/java/res/xml-sw768dp/kbd_phone.xml
index 0935992..e55b184 100644
--- a/java/res/xml-sw768dp/kbd_phone.xml
+++ b/java/res/xml-sw768dp/kbd_phone.xml
@@ -26,13 +26,11 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <include
         latin:keyboardLayout="@xml/kbd_numkey_styles" />
-    <!-- This row is intentionally not marked as a top row -->
     <Row>
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="11.172%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="11.172%p" />
         <Key
             latin:keyLabel="-"
             latin:keyStyle="numKeyStyle"
@@ -52,14 +50,12 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-11.172%p"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row>
         <Key
-            latin:keyStyle="moreKeyStyle"
-            latin:keyWidth="11.172%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="toMoreSymbolKeyStyle"
+            latin:keyWidth="11.172%p" />
         <Key
             latin:keyLabel=","
             latin:keyStyle="numKeyStyle"
@@ -79,16 +75,15 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-11.172%p"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row>
-        <!-- 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. -->
+        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+        <Spacer
+            latin:keyWidth="20.400%p" />
         <Key
             latin:keyLabel="("
             latin:keyStyle="numKeyStyle"
-            latin:keyXPos="20.400%p"
             latin:keyWidth="8.047%p" />
         <Key
             latin:keyLabel=")"
@@ -101,10 +96,10 @@
             latin:keyStyle="num8KeyStyle" />
         <Key
             latin:keyStyle="num9KeyStyle" />
-        <!-- There is an empty area below the "Enter" key and right of the "9" key.  To ignore
-             the touch event on the area, "9" is intentionally not marked as a right edge key. -->
+        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+        <Spacer
+            latin:keyWidth="0%p" />
         </Row>
-    <!-- This row is intentionally not marked as a bottom row -->
     <Row>
         <switch>
             <case latin:hasSettingsKey="true">
@@ -112,6 +107,11 @@
                     latin:keyStyle="settingsKeyStyle"
                     latin:keyWidth="8.047%p" />
             </case>
+            <default>
+                <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                <Spacer
+                    latin:keyWidth="8.047%p" />
+            </default>
         </switch>
         <Key
             latin:keyStyle="nonSpecialBackgroundSpaceKeyStyle"
@@ -127,13 +127,18 @@
             latin:keyStyle="numKeyStyle" />
         <switch>
             <case
-                latin:voiceKeyEnabled="true"
+                latin:shortcutKeyEnabled="true"
             >
                 <Key
                     latin:keyStyle="shortcutKeyStyle"
                     latin:keyXPos="-8.047%p"
                     latin:keyWidth="fillRight" />
             </case>
+            <default>
+                <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+                <Spacer
+                    latin:keyWidth="0%p" />
+            </default>
         </switch>
     </Row>
 </Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_phone_symbols.xml b/java/res/xml-sw768dp/kbd_phone_shift.xml
similarity index 79%
rename from java/res/xml-sw768dp/kbd_phone_symbols.xml
rename to java/res/xml-sw768dp/kbd_phone_shift.xml
index 6ee57d6..646f0b8 100644
--- a/java/res/xml-sw768dp/kbd_phone_symbols.xml
+++ b/java/res/xml-sw768dp/kbd_phone_shift.xml
@@ -26,13 +26,11 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <include
         latin:keyboardLayout="@xml/kbd_numkey_styles" />
-    <!-- This row is intentionally not marked as a top row -->
     <Row>
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="11.172%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="11.172%p" />
         <Key
             latin:keyLabel="-"
             latin:keyStyle="numKeyStyle"
@@ -56,14 +54,12 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-11.172%p"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row>
         <Key
-            latin:keyStyle="moreKeyStyle"
-            latin:keyWidth="11.172%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="backFromMoreSymbolKeyStyle"
+            latin:keyWidth="11.172%p" />
         <Key
             latin:keyLabel=","
             latin:keyStyle="numKeyStyle"
@@ -87,16 +83,15 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-11.172%p"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row>
-        <!-- 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. -->
+        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+        <Spacer
+            latin:keyWidth="13.829%p" />
         <Key
             latin:keyLabel="("
             latin:keyStyle="numKeyStyle"
-            latin:keyXPos="13.829%p"
             latin:keyWidth="8.047%p" />
         <Key
             latin:keyLabel=")"
@@ -113,10 +108,10 @@
             latin:keyStyle="num8KeyStyle" />
         <Key
             latin:keyStyle="num9KeyStyle" />
-        <!-- There is an empty area below the "Enter" key and right of the "9" key.  To ignore
-             the touch event on the area, "9" is intentionally not marked as a right edge key. -->
+        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+        <Spacer
+            latin:keyWidth="0%p" />
     </Row>
-    <!-- This row is intentionally not marked as a bottom row -->
     <Row>
         <switch>
             <case latin:hasSettingsKey="true">
@@ -124,6 +119,11 @@
                     latin:keyStyle="settingsKeyStyle"
                     latin:keyWidth="8.047%p" />
             </case>
+            <default>
+                <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+                <Spacer
+                    latin:keyWidth="8.047%p" />
+            </default>
         </switch>
         <Key
             latin:keyStyle="nonSpecialBackgroundSpaceKeyStyle"
@@ -139,13 +139,18 @@
             latin:keyStyle="numKeyStyle" />
         <switch>
             <case
-                latin:voiceKeyEnabled="true"
+                latin:shortcutKeyEnabled="true"
             >
                 <Key
                     latin:keyStyle="shortcutKeyStyle"
                     latin:keyXPos="-8.047%p"
                     latin:keyWidth="fillRight" />
             </case>
+            <default>
+                <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+                <Spacer
+                    latin:keyWidth="0%p" />
+            </default>
         </switch>
     </Row>
 </Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_qwerty_row1.xml b/java/res/xml-sw768dp/kbd_qwerty_row1.xml
index 3727cf3..0fdb463 100644
--- a/java/res/xml-sw768dp/kbd_qwerty_row1.xml
+++ b/java/res/xml-sw768dp/kbd_qwerty_row1.xml
@@ -21,15 +21,13 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <!-- This row is intentionally not marked as a top row -->
     <Row
         latin:keyWidth="8.282%p"
     >
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.969%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="7.969%p" />
         <Key
             latin:keyLabel="q"
             latin:popupCharacters="@string/alternates_for_q" />
@@ -63,7 +61,6 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-9.219%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth"/>
     </Row>
 </merge>
diff --git a/java/res/xml-sw768dp/kbd_qwerty_row2.xml b/java/res/xml-sw768dp/kbd_qwerty_row2.xml
index 45af120..7f7dd57 100644
--- a/java/res/xml-sw768dp/kbd_qwerty_row2.xml
+++ b/java/res/xml-sw768dp/kbd_qwerty_row2.xml
@@ -27,8 +27,7 @@
         <Key
             latin:keyStyle="toSymbolKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="11.172%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="11.172%p"/>
         <Key
             latin:keyLabel="a"
             latin:popupCharacters="@string/alternates_for_a" />
@@ -56,7 +55,6 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-15.704%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
 </merge>
diff --git a/java/res/xml-sw768dp/kbd_qwerty_row3.xml b/java/res/xml-sw768dp/kbd_qwerty_row3.xml
index 9041a77..677235f 100644
--- a/java/res/xml-sw768dp/kbd_qwerty_row3.xml
+++ b/java/res/xml-sw768dp/kbd_qwerty_row3.xml
@@ -26,8 +26,7 @@
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="13.829%p"/>
         <Key
             latin:keyLabel="z"
             latin:popupCharacters="@string/alternates_for_z" />
@@ -51,7 +50,6 @@
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyXPos="-13.750%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
 </merge>
diff --git a/java/res/xml-sw768dp/kbd_qwerty_row4.xml b/java/res/xml-sw768dp/kbd_qwerty_row4.xml
index 48201c9..1ff9d81 100644
--- a/java/res/xml-sw768dp/kbd_qwerty_row4.xml
+++ b/java/res/xml-sw768dp/kbd_qwerty_row4.xml
@@ -21,7 +21,6 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <!-- This row is intentionally not marked as a bottom row -->
     <Row
         latin:keyWidth="8.047%p"
     >
@@ -102,7 +101,7 @@
         </switch>
         <switch>
             <case
-                latin:voiceKeyEnabled="true"
+                latin:shortcutKeyEnabled="true"
             >
                 <Key
                     latin:keyStyle="shortcutKeyStyle"
diff --git a/java/res/xml-sw768dp/kbd_rows_arabic.xml b/java/res/xml-sw768dp/kbd_rows_arabic.xml
index f3283ae..2f10d54 100644
--- a/java/res/xml-sw768dp/kbd_rows_arabic.xml
+++ b/java/res/xml-sw768dp/kbd_rows_arabic.xml
@@ -29,8 +29,7 @@
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.500%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="7.500%p" />
         <Key
             latin:keyLabel="ض" />
         <Key
@@ -61,8 +60,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-11.500%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.375%p"
@@ -70,8 +68,7 @@
         <Key
             latin:keyStyle="toSymbolKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="9.375%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="9.375%p" />
         <Key
             latin:keyLabel="ش" />
         <Key
@@ -113,8 +110,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-9.375%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.375%p"
diff --git a/java/res/xml-sw768dp/kbd_rows_azerty.xml b/java/res/xml-sw768dp/kbd_rows_azerty.xml
index 373bda9..7aec0d1 100644
--- a/java/res/xml-sw768dp/kbd_rows_azerty.xml
+++ b/java/res/xml-sw768dp/kbd_rows_azerty.xml
@@ -29,8 +29,7 @@
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.969%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="7.969%p" />
         <Key
             latin:keyLabel="a"
             latin:popupCharacters="@string/alternates_for_a" />
@@ -64,8 +63,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-9.219%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="8.125%p"
@@ -73,8 +71,7 @@
         <Key
             latin:keyStyle="toSymbolKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="10.167%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="10.167%p" />
         <Key
             latin:keyLabel="q"
             latin:popupCharacters="@string/alternates_for_q" />
@@ -100,21 +97,18 @@
             latin:keyLabel="l"
             latin:popupCharacters="@string/alternates_for_l" />
         <Key
-            latin:keyLabel="m"
-            latin:keyEdgeFlags="right" />
+            latin:keyLabel="m" />
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-15.704%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="8.047%p"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="13.829%p" />
         <Key
             latin:keyLabel="w"
             latin:popupCharacters="@string/alternates_for_w" />
@@ -161,8 +155,7 @@
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyXPos="-13.750%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
diff --git a/java/res/xml-sw768dp/kbd_rows_hebrew.xml b/java/res/xml-sw768dp/kbd_rows_hebrew.xml
index 0ef68b6..bacc6a1 100644
--- a/java/res/xml-sw768dp/kbd_rows_hebrew.xml
+++ b/java/res/xml-sw768dp/kbd_rows_hebrew.xml
@@ -29,8 +29,7 @@
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.969%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="7.969%p" />
         <include
             latin:keyboardLayout="@xml/kbd_row4_apostrophe_dash" />
         <Key
@@ -52,8 +51,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-12.000%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="8.125%p"
@@ -61,8 +59,7 @@
         <Key
             latin:keyStyle="toSymbolKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="11.172%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="11.172%p" />
         <Key
             latin:keyLabel="ש" />
         <Key
@@ -116,8 +113,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-10.400%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
diff --git a/java/res/xml-sw768dp/kbd_rows_qwertz.xml b/java/res/xml-sw768dp/kbd_rows_qwertz.xml
index 6567c31..5556d1f 100644
--- a/java/res/xml-sw768dp/kbd_rows_qwertz.xml
+++ b/java/res/xml-sw768dp/kbd_rows_qwertz.xml
@@ -29,8 +29,7 @@
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.969%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="7.969%p" />
         <Key
             latin:keyLabel="q"
             latin:popupCharacters="@string/alternates_for_q" />
@@ -64,8 +63,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-9.219%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row2" />
@@ -74,8 +72,7 @@
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="13.829%p" />
         <Key
             latin:keyLabel="y"
             latin:popupCharacters="@string/alternates_for_y" />
@@ -119,8 +116,7 @@
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyXPos="-13.750%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
    <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
diff --git a/java/res/xml-sw768dp/kbd_rows_russian.xml b/java/res/xml-sw768dp/kbd_rows_russian.xml
index 35b2c8a..82ceaea 100644
--- a/java/res/xml-sw768dp/kbd_rows_russian.xml
+++ b/java/res/xml-sw768dp/kbd_rows_russian.xml
@@ -23,14 +23,12 @@
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
-    <!-- This row is intentionally not marked as a top row -->
     <Row
         latin:keyWidth="7.125%p"
     >
         <Key
             latin:keyStyle="tabKeyStyle"
-            latin:keyLabelOption="alignLeft"
-            latin:keyEdgeFlags="left" />
+            latin:keyLabelOption="alignLeft" />
         <Key
             latin:keyLabel="й" />
         <Key
@@ -58,8 +56,7 @@
             latin:keyLabel="ъ" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.125%p"
@@ -67,8 +64,7 @@
         <Key
             latin:keyStyle="toSymbolKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="9.375%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="9.375%p" />
         <Key
             latin:keyLabel="ф" />
         <Key
@@ -94,16 +90,14 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-9.375%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.125%p"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="12.750%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="12.750%p" />
         <Key
             latin:keyLabel="я" />
         <Key
@@ -126,8 +120,7 @@
             latin:keyboardLayout="@xml/kbd_row3_comma_period" />
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
diff --git a/java/res/xml-sw768dp/kbd_rows_scandinavian.xml b/java/res/xml-sw768dp/kbd_rows_scandinavian.xml
index c20dcee..743c6bd 100644
--- a/java/res/xml-sw768dp/kbd_rows_scandinavian.xml
+++ b/java/res/xml-sw768dp/kbd_rows_scandinavian.xml
@@ -29,8 +29,7 @@
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.500%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="7.500%p" />
         <Key
             latin:keyLabel="q"
             latin:popupCharacters="@string/alternates_for_q" />
@@ -66,8 +65,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-11.500%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.375%p"
@@ -75,8 +73,7 @@
         <Key
             latin:keyStyle="toSymbolKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="9.375%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="9.375%p" />
         <Key
             latin:keyLabel="a"
             latin:popupCharacters="@string/alternates_for_a" />
@@ -110,16 +107,14 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-9.375%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.375%p"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="12.750%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="12.750%p" />
         <Key
             latin:keyLabel="z"
             latin:popupCharacters="@string/alternates_for_z" />
@@ -143,8 +138,7 @@
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyXPos="-12.750%p"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
diff --git a/java/res/xml-sw768dp/kbd_rows_serbian.xml b/java/res/xml-sw768dp/kbd_rows_serbian.xml
index f1576de..c07176e 100644
--- a/java/res/xml-sw768dp/kbd_rows_serbian.xml
+++ b/java/res/xml-sw768dp/kbd_rows_serbian.xml
@@ -23,14 +23,12 @@
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
-    <!-- This row is intentionally not marked as a top row -->
     <Row
         latin:keyWidth="7.125%p"
     >
         <Key
             latin:keyStyle="tabKeyStyle"
-            latin:keyLabelOption="alignLeft"
-            latin:keyEdgeFlags="left" />
+            latin:keyLabelOption="alignLeft" />
         <Key
             latin:keyLabel="љ" />
         <Key
@@ -57,8 +55,7 @@
             latin:keyLabel="ђ" />
         <Key
             latin:keyLabel="ж"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right"/>
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
         latin:keyWidth="7.250%p"
@@ -66,8 +63,7 @@
         <Key
             latin:keyStyle="toSymbolKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="11.172%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="11.172%p" />
         <Key
             latin:keyLabel="а" />
         <Key
@@ -93,16 +89,14 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-9.219%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="7.250%p"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="13.829%p" />
         <Key
             latin:keyLabel="ѕ" />
         <Key
@@ -122,8 +116,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-13.750%p"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
diff --git a/java/res/xml-sw768dp/kbd_rows_spanish.xml b/java/res/xml-sw768dp/kbd_rows_spanish.xml
index 300592e..510cafb 100644
--- a/java/res/xml-sw768dp/kbd_rows_spanish.xml
+++ b/java/res/xml-sw768dp/kbd_rows_spanish.xml
@@ -31,8 +31,7 @@
         <Key
             latin:keyStyle="toSymbolKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="10.167%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="10.167%p" />
         <Key
             latin:keyLabel="a"
             latin:popupCharacters="@string/alternates_for_a" />
@@ -62,8 +61,7 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-15.704%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row3" />
diff --git a/java/res/xml-sw768dp/kbd_symbols.xml b/java/res/xml-sw768dp/kbd_rows_symbols.xml
similarity index 85%
rename from java/res/xml-sw768dp/kbd_symbols.xml
rename to java/res/xml-sw768dp/kbd_rows_symbols.xml
index eb77d4d..66b9789 100644
--- a/java/res/xml-sw768dp/kbd_symbols.xml
+++ b/java/res/xml-sw768dp/kbd_rows_symbols.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,22 +18,20 @@
 */
 -->
 
-<Keyboard
+<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_currency_key_styles" />
-    <!-- This row is intentionally not marked as a top row -->
     <Row
         latin:keyWidth="8.282%p"
     >
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.969%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="7.969%p" />
         <Key
             latin:keyLabel="@string/keylabel_for_symbols_1"
             latin:popupCharacters="@string/alternates_for_symbols_1" />
@@ -67,8 +65,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-9.219%p"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
         latin:keyWidth="8.125%p"
@@ -76,15 +73,14 @@
         <Key
             latin:keyStyle="toAlphaKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="11.172%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="11.172%p" />
         <Key
             latin:keyLabel="#" />
         <Key
             latin:keyStyle="currencyKeyStyle" />
         <Key
-            latin:keyLabel="%"
-            latin:popupCharacters="‰" />
+            latin:keyLabel="@string/keylabel_for_symbols_percent"
+            latin:popupCharacters="@string/alternates_for_symbols_percent" />
         <Key
             latin:keyLabel="&amp;" />
         <Key
@@ -105,16 +101,14 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-15.704%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="8.047%p"
     >
         <Key
-            latin:keyStyle="moreKeyStyle"
-            latin:keyWidth="13.829%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="toMoreSymbolKeyStyle"
+            latin:keyWidth="13.829%p" />
         <Key
             latin:keyLabel="&lt;"
             latin:popupCharacters="≤,«,‹" />
@@ -141,8 +135,8 @@
             latin:keyLabel="@string/keylabel_for_symbols_semicolon"
             latin:popupCharacters="@string/alternates_for_symbols_semicolon" />
         <Key
-            latin:keyLabel="@string/keylabel_for_symbols_comma"
-            latin:popupCharacters="@string/alternates_for_symbols_comma" />
+            latin:keyLabel="@string/keylabel_for_comma"
+            latin:popupCharacters="@string/alternates_for_comma" />
         <Key
             latin:keyLabel="." />
         <Key
@@ -152,12 +146,10 @@
             latin:keyLabel="@string/keylabel_for_symbols_question"
             latin:popupCharacters="@string/alternates_for_symbols_question" />
         <Key
-            latin:keyStyle="moreKeyStyle"
+            latin:keyStyle="toMoreSymbolKeyStyle"
             latin:keyXPos="-13.750%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
-    <!-- This row is intentionally not marked as a bottom row -->
     <Row
         latin:keyWidth="8.047%p"
     >
@@ -188,7 +180,7 @@
             latin:keyLabel="_" />
         <switch>
             <case
-                latin:voiceKeyEnabled="true"
+                latin:shortcutKeyEnabled="true"
             >
                 <Key
                     latin:keyStyle="shortcutKeyStyle"
@@ -197,4 +189,4 @@
             </case>
         </switch>
     </Row>
-</Keyboard>
+</merge>
diff --git a/java/res/xml-sw768dp/kbd_symbols_shift.xml b/java/res/xml-sw768dp/kbd_rows_symbols_shift.xml
similarity index 85%
rename from java/res/xml-sw768dp/kbd_symbols_shift.xml
rename to java/res/xml-sw768dp/kbd_rows_symbols_shift.xml
index b368141..1f43a0f 100644
--- a/java/res/xml-sw768dp/kbd_symbols_shift.xml
+++ b/java/res/xml-sw768dp/kbd_rows_symbols_shift.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,20 +18,18 @@
 */
 -->
 
-<Keyboard
+<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="8.282%p"
     >
         <Key
             latin:keyStyle="tabKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="7.969%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="7.969%p" />
         <Key
             latin:keyLabel="~" />
         <Key
@@ -65,8 +63,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyXPos="-9.219%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="8.125%p"
@@ -74,8 +71,7 @@
         <Key
             latin:keyStyle="toAlphaKeyStyle"
             latin:keyLabelOption="alignLeft"
-            latin:keyWidth="11.172%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="11.172%p" />
         <Key
             latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="£" />
@@ -106,16 +102,14 @@
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyXPos="-15.704%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
     <Row
         latin:keyWidth="8.047%p"
     >
         <Key
-            latin:keyStyle="moreKeyStyle"
-            latin:keyWidth="13.829%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="backFromMoreSymbolKeyStyle"
+            latin:keyWidth="13.829%p" />
         <Key
             latin:keyLabel="\\" />
         <Key
@@ -141,12 +135,10 @@
             latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="¿" />
         <Key
-            latin:keyStyle="moreKeyStyle"
+            latin:keyStyle="backFromMoreSymbolKeyStyle"
             latin:keyXPos="-13.750%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillBoth" />
     </Row>
-    <!-- This row is intentionally not marked as a bottom row -->
     <Row
         latin:keyWidth="8.047%p"
     >
@@ -163,7 +155,7 @@
             latin:keyWidth="37.500%p" />
         <switch>
             <case
-                latin:voiceKeyEnabled="true"
+                latin:shortcutKeyEnabled="true"
             >
                 <Key
                     latin:keyStyle="shortcutKeyStyle"
@@ -172,4 +164,4 @@
             </case>
         </switch>
     </Row>
-</Keyboard>
+</merge>
diff --git a/java/res/xml/kbd_key_styles.xml b/java/res/xml/kbd_key_styles.xml
index 9791001..5612251 100644
--- a/java/res/xml/kbd_key_styles.xml
+++ b/java/res/xml/kbd_key_styles.xml
@@ -25,29 +25,43 @@
     <key-style
         latin:styleName="functionalKeyStyle"
         latin:isFunctional="true" />
-    <!-- Base key style for the key which may have settings key as popup key -->
+    <!-- Base key style for the key which may have settings or tab key as popup key. -->
     <switch>
         <case
             latin:clobberSettingsKey="true"
         >
             <key-style
-                latin:styleName="settingsPopupStyle"
+                latin:styleName="f1PopupStyle"
+                latin:keyLabelOption="hasPopupHint"
+                latin:popupCharacters="@string/alternates_for_f1"
                 latin:parentStyle="functionalKeyStyle" />
         </case>
+        <!-- clobberSettingsKey="false -->
         <case
-            latin:clobberSettingsKey="false"
-            latin:hasSettingsKey="true"
+            latin:hasSettingsKey="false"
         >
             <key-style
-                latin:styleName="settingsPopupStyle"
+                latin:styleName="f1PopupStyle"
+                latin:keyLabelOption="hasPopupHint"
+                latin:popupCharacters="@string/alternates_for_f1_settings"
                 latin:parentStyle="functionalKeyStyle" />
         </case>
-        <!-- clobberSettingsKey="false" and hasSettingsKey="false" -->
+        <!-- clobberSettingsKey="false" hasSettingsKey="true" -->
+        <case
+            latin:navigateAction="true"
+        >
+            <key-style
+                latin:styleName="f1PopupStyle"
+                latin:keyLabelOption="hasPopupHint"
+                latin:popupCharacters="@string/alternates_for_f1_navigate"
+                latin:parentStyle="functionalKeyStyle" />
+        </case>
+        <!-- clobberSettingsKey="false" and hasSettingsKey="true" navigateAction="false" -->
         <default>
             <key-style
-                latin:styleName="settingsPopupStyle"
+                latin:styleName="f1PopupStyle"
                 latin:keyLabelOption="hasPopupHint"
-                latin:popupCharacters="\@icon/6|\@integer/key_settings"
+                latin:popupCharacters="@string/alternates_for_f1"
                 latin:parentStyle="functionalKeyStyle" />
         </default>
     </switch>
@@ -59,29 +73,28 @@
         latin:keyIconShifted="iconShiftedShiftKey"
         latin:parentStyle="functionalKeyStyle"
         latin:isSticky="true" />
-    <switch>
-        <case
-            latin:languageCode="ar|iw"
-        >
-            <key-style
-                latin:styleName="deleteKeyStyle"
-                latin:code="@integer/key_delete"
-                latin:keyIcon="iconDeleteRtlKey"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isRepeatable="true" />
-        </case>
-        <default>
-            <key-style
-                latin:styleName="deleteKeyStyle"
-                latin:code="@integer/key_delete"
-                latin:keyIcon="iconDeleteKey"
-                latin:parentStyle="functionalKeyStyle"
-                latin:isRepeatable="true" />
-        </default>
-    </switch>
+    <key-style
+        latin:styleName="deleteKeyStyle"
+        latin:code="@integer/key_delete"
+        latin:keyIcon="iconDeleteKey"
+        latin:parentStyle="functionalKeyStyle"
+        latin:isRepeatable="true" />
     <!-- Return key style -->
     <switch>
         <case
+            latin:mode="im"
+        >
+            <!-- Smiley key. -->
+            <key-style
+                latin:styleName="returnKeyStyle"
+                latin:keyLabel=":-)"
+                latin:keyOutputText=":-) "
+                latin:keyLabelOption="hasPopupHint"
+                latin:popupCharacters="@string/alternates_for_smiley"
+                latin:maxPopupKeyboardColumn="5"
+                latin:parentStyle="functionalKeyStyle" />
+        </case>
+        <case
             latin:imeAction="actionGo"
         >
             <key-style
@@ -137,22 +150,13 @@
     <key-style
         latin:styleName="spaceKeyStyle"
         latin:code="@integer/key_space"
-        latin:keyIconPreview="iconPreviewSpaceKey"
-        latin:parentStyle="functionalKeyStyle" />
-    <key-style
-        latin:styleName="smileyKeyStyle"
-        latin:keyLabel=":-)"
-        latin:keyOutputText=":-) "
-        latin:keyLabelOption="hasPopupHint"
-        latin:popupCharacters="@string/alternates_for_smiley"
-        latin:maxPopupKeyboardColumn="5"
         latin:parentStyle="functionalKeyStyle" />
     <key-style
         latin:styleName="shortcutKeyStyle"
         latin:code="@integer/key_shortcut"
         latin:keyIcon="iconShortcutKey"
         latin:keyIconPreview="iconPreviewShortcutKey"
-        latin:parentStyle="settingsPopupStyle" />
+        latin:parentStyle="f1PopupStyle" />
     <key-style
         latin:styleName="settingsKeyStyle"
         latin:code="@integer/key_settings"
@@ -173,16 +177,19 @@
         latin:keyIcon="iconTabKey"
         latin:keyIconPreview="iconPreviewTabKey" />
     <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. -->
+        <!-- When this qwerty keyboard has no shortcut keys but shortcut key is enabled, then symbol
+             keyboard will have a shortcut key. That means we should use
+             iconToSymbolKeyWithShortcutKey here. -->
         <case
-            latin:voiceKeyEnabled="true"
-            latin:hasVoiceKey="false"
+            latin:shortcutKeyEnabled="true"
+            latin:hasShortcutKey="false"
         >
             <key-style
                 latin:styleName="toSymbolKeyStyle"
                 latin:code="@integer/key_switch_alpha_symbol"
-                latin:keyIcon="iconToSymbolKeyWithShortcut"
+                latin:keyIcon="iconShortcutForLabel"
+                latin:keyLabel="@string/label_to_symbol_with_microphone_key"
+                latin:keyLabelOption="withIconRight"
                 latin:parentStyle="functionalKeyStyle" />
         </case>
         <default>
@@ -199,11 +206,15 @@
         latin:keyLabel="@string/label_to_alpha_key"
         latin:parentStyle="functionalKeyStyle" />
     <key-style
-        latin:styleName="altKeyStyle"
+        latin:styleName="toMoreSymbolKeyStyle"
         latin:code="@integer/key_shift"
-        latin:keyLabel="@string/label_alt_key"
-        latin:parentStyle="functionalKeyStyle"
-        latin:isSticky="true" />
+        latin:keyLabel="@string/label_to_more_symbol_key"
+        latin:parentStyle="functionalKeyStyle" />
+    <key-style
+        latin:styleName="backFromMoreSymbolKeyStyle"
+        latin:code="@integer/key_shift"
+        latin:keyLabel="@string/label_to_symbol_key"
+        latin:parentStyle="functionalKeyStyle" />
     <switch>
         <case
             latin:passwordInput="true"
@@ -228,29 +239,11 @@
         </default>
     </switch>
     <key-style
-        latin:styleName="basePunctuationKeyStyle"
+        latin:styleName="punctuationKeyStyle"
         latin:keyLabel="."
         latin:keyHintLabel="@string/keyhintlabel_for_punctuation"
         latin:keyLabelOption="hasPopupHint"
         latin:popupCharacters="@string/alternates_for_punctuation"
         latin:maxPopupKeyboardColumn="@integer/popup_keyboard_column_for_punctuation"
         latin:parentStyle="functionalKeyStyle" />
-    <switch>
-        <case
-            latin:navigateAction="true"
-        >
-            <key-style
-                latin:styleName="punctuationKeyStyle"
-                latin:keyHintLabel="@string/keyhintlabel_for_punctuation"
-                latin:keyLabelOption="hasPopupHint"
-                latin:popupCharacters="@string/alternates_for_web_tab_punctuation"
-                latin:maxPopupKeyboardColumn="@integer/popup_keyboard_column_for_web_punctuation"
-                latin:parentStyle="tabKeyStyle" />
-        </case>
-        <default>
-            <key-style
-                latin:styleName="punctuationKeyStyle"
-                latin:parentStyle="basePunctuationKeyStyle" />
-        </default>
-    </switch>
 </merge>
diff --git a/java/res/xml/kbd_number.xml b/java/res/xml/kbd_number.xml
index 4d7b6d9..aabf0eb 100644
--- a/java/res/xml/kbd_number.xml
+++ b/java/res/xml/kbd_number.xml
@@ -30,12 +30,9 @@
         <case
             latin:passwordInput="true"
         >
-            <Row
-                latin:rowEdgeFlags="top"
-            >
+            <Row>
                 <Key
-                    latin:keyStyle="num1KeyStyle"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyStyle="num1KeyStyle" />
                 <Key
                     latin:keyStyle="num2KeyStyle" />
                 <Key
@@ -43,8 +40,7 @@
             </Row>
             <Row>
                 <Key
-                    latin:keyStyle="num4KeyStyle"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyStyle="num4KeyStyle" />
                 <Key
                     latin:keyStyle="num5KeyStyle" />
                 <Key
@@ -52,39 +48,31 @@
             </Row>
             <Row>
                 <Key
-                    latin:keyStyle="num7KeyStyle"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyStyle="num7KeyStyle" />
                 <Key
                     latin:keyStyle="num8KeyStyle" />
                 <Key
                     latin:keyStyle="num9KeyStyle" />
                 <Key
                     latin:keyStyle="deleteKeyStyle"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
             </Row>
-            <Row
-                latin:rowEdgeFlags="bottom"
-            >
+            <Row>
                 <Spacer />
                 <Key
                     latin:keyStyle="num0KeyStyle" />
                 <Spacer />
                 <Key
                     latin:keyStyle="returnKeyStyle"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
             </Row>
         </case>
         <!-- latin:passwordInput="false" -->
         <default>
-            <Row
-                latin:rowEdgeFlags="top"
-            >
+            <Row>
                 <Key
                     latin:keyLabel="1"
-                    latin:keyStyle="numKeyStyle"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyStyle="numKeyStyle" />
                 <Key
                     latin:keyLabel="2"
                     latin:keyStyle="numKeyStyle" />
@@ -94,14 +82,12 @@
                 <Key
                     latin:keyLabel="-"
                     latin:keyStyle="numFunctionalKeyStyle"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
             </Row>
             <Row>
                 <Key
                     latin:keyLabel="4"
-                    latin:keyStyle="numKeyStyle"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyStyle="numKeyStyle" />
                 <Key
                     latin:keyLabel="5"
                     latin:keyStyle="numKeyStyle" />
@@ -111,14 +97,12 @@
                 <Key
                     latin:keyLabel=","
                     latin:keyStyle="numFunctionalKeyStyle"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
             </Row>
             <Row>
                 <Key
                     latin:keyLabel="7"
-                    latin:keyStyle="numKeyStyle"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyStyle="numKeyStyle" />
                 <Key
                     latin:keyLabel="8"
                     latin:keyStyle="numKeyStyle"/>
@@ -127,15 +111,11 @@
                     latin:keyStyle="numKeyStyle" />
                 <Key
                     latin:keyStyle="deleteKeyStyle"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
             </Row>
-            <Row
-                latin:rowEdgeFlags="bottom"
-            >
+            <Row>
                 <Key
-                    latin:keyStyle="numSpaceKeyStyle"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyStyle="numSpaceKeyStyle" />
                 <Key
                     latin:keyLabel="0"
                     latin:keyStyle="numKeyStyle" />
@@ -144,8 +124,7 @@
                     latin:keyStyle="numKeyStyle" />
                 <Key
                     latin:keyStyle="returnKeyStyle"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
             </Row>
         </default>
     </switch>
diff --git a/java/res/xml/kbd_numkey_styles.xml b/java/res/xml/kbd_numkey_styles.xml
index 657bfbc..c6ba23d 100644
--- a/java/res/xml/kbd_numkey_styles.xml
+++ b/java/res/xml/kbd_numkey_styles.xml
@@ -91,17 +91,16 @@
         latin:parentStyle="numKeyStyle" />
     <key-style
         latin:styleName="numSwitchToAltKeyStyle"
-        latin:code="@integer/key_switch_alpha_symbol"
+        latin:code="@integer/key_shift"
         latin:keyLabel="@string/label_to_phone_symbols_key"
         latin:parentStyle="numModeKeyStyle" />
     <key-style
         latin:styleName="numSwitchToNumericKeyStyle"
-        latin:code="@integer/key_switch_alpha_symbol"
+        latin:code="@integer/key_shift"
         latin:keyLabel="@string/label_to_phone_numeric_key"
         latin:parentStyle="numModeKeyStyle" />
     <key-style
         latin:styleName="numSpaceKeyStyle"
         latin:code="@integer/key_space"
-        latin:keyIcon="iconSpaceKey"
-        latin:keyIconPreview="iconPreviewSpaceKey" />
+        latin:keyIcon="iconSpaceKey" />
 </merge>
diff --git a/java/res/xml/kbd_phone.xml b/java/res/xml/kbd_phone.xml
index c6508fb..4588ab2 100644
--- a/java/res/xml/kbd_phone.xml
+++ b/java/res/xml/kbd_phone.xml
@@ -26,12 +26,9 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <include
         latin:keyboardLayout="@xml/kbd_numkey_styles" />
-    <Row
-        latin:rowEdgeFlags="top"
-    >
+    <Row>
         <Key
-            latin:keyStyle="num1KeyStyle"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="num1KeyStyle" />
         <Key
             latin:keyStyle="num2KeyStyle" />
         <Key
@@ -39,13 +36,11 @@
         <Key
             latin:keyLabel="-"
             latin:keyStyle="numFunctionalKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row>
         <Key
-            latin:keyStyle="num4KeyStyle"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="num4KeyStyle" />
         <Key
             latin:keyStyle="num5KeyStyle" />
         <Key
@@ -53,35 +48,28 @@
         <Key
             latin:keyLabel="."
             latin:keyStyle="numFunctionalKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row>
         <Key
-            latin:keyStyle="num7KeyStyle"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="num7KeyStyle" />
         <Key
             latin:keyStyle="num8KeyStyle" />
         <Key
             latin:keyStyle="num9KeyStyle" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
-    <Row
-        latin:rowEdgeFlags="bottom"
-    >
+    <Row>
         <Key
-            latin:keyStyle="numSwitchToAltKeyStyle"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="numSwitchToAltKeyStyle" />
         <Key
             latin:keyStyle="num0KeyStyle" />
         <Key
             latin:keyStyle="numSpaceKeyStyle" />
         <Key
             latin:keyStyle="returnKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
 </Keyboard>
diff --git a/java/res/xml/kbd_phone_symbols.xml b/java/res/xml/kbd_phone_shift.xml
similarity index 75%
rename from java/res/xml/kbd_phone_symbols.xml
rename to java/res/xml/kbd_phone_shift.xml
index 5d99122..89a9bd0 100644
--- a/java/res/xml/kbd_phone_symbols.xml
+++ b/java/res/xml/kbd_phone_shift.xml
@@ -26,13 +26,10 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <include
         latin:keyboardLayout="@xml/kbd_numkey_styles" />
-    <Row
-        latin:rowEdgeFlags="top"
-    >
+    <Row>
         <Key
             latin:keyLabel="("
-            latin:keyStyle="numKeyStyle"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="numKeyStyle" />
         <Key
             latin:keyLabel="/"
             latin:keyStyle="numKeyStyle" />
@@ -42,13 +39,11 @@
         <Key
             latin:keyLabel="-"
             latin:keyStyle="numFunctionalKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row>
         <Key
-            latin:keyLabel="N"
-            latin:keyEdgeFlags="left" />
+            latin:keyLabel="N" />
         <!-- Pause is a comma. Check PhoneNumberUtils.java to see if this 
             has changed. -->
         <Key
@@ -61,13 +56,11 @@
         <Key
             latin:keyLabel="."
             latin:keyStyle="numFunctionalKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row>
         <Key
-            latin:keyStyle="numStarKeyStyle"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="numStarKeyStyle" />
         <!-- Wait is a semicolon. -->
         <Key
             latin:code="59"
@@ -78,15 +71,11 @@
             latin:keyStyle="numKeyStyle" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
-    <Row
-        latin:rowEdgeFlags="bottom"
-    >
+    <Row>
         <Key
-            latin:keyStyle="numSwitchToNumericKeyStyle"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="numSwitchToNumericKeyStyle" />
         <Key
             latin:keyLabel="+"
             latin:keyStyle="numKeyStyle" />
@@ -94,7 +83,6 @@
             latin:keyStyle="numSpaceKeyStyle" />
         <Key
             latin:keyStyle="returnKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
 </Keyboard>
diff --git a/java/res/xml/kbd_qwerty_f1.xml b/java/res/xml/kbd_qwerty_f1.xml
index 1546f49..83b6ecc 100644
--- a/java/res/xml/kbd_qwerty_f1.xml
+++ b/java/res/xml/kbd_qwerty_f1.xml
@@ -23,7 +23,8 @@
 >
     <switch>
         <case
-            latin:hasSettingsKey="true"
+            latin:hasSettingsKey="false"
+            latin:navigateAction="false"
         >
             <switch>
                 <case
@@ -31,46 +32,30 @@
                 >
                     <Key
                         latin:keyLabel="/"
-                        latin:keyWidth="9.2%p"
-                        latin:keyStyle="settingsPopupStyle" />
+                        latin:keyStyle="f1PopupStyle" />
                 </case>
                 <case
                     latin:mode="email"
                 >
                     <Key
                         latin:keyLabel="\@"
-                        latin:keyWidth="9.2%p"
-                        latin:keyStyle="settingsPopupStyle" />
+                        latin:keyStyle="f1PopupStyle" />
                 </case>
+                <case
+                    latin:hasShortcutKey="true"
+                >
+                    <Key
+                        latin:keyStyle="shortcutKeyStyle" />
+                </case>
+                <!-- latin:hasShortcutKey="false" -->
                 <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="@string/keylabel_for_symbols_comma"
-                                latin:keyWidth="9.2%p"
-                                latin:keyStyle="settingsPopupStyle" />
-                        </default>
-                    </switch>
+                    <Key
+                        latin:keyLabel="@string/keylabel_for_comma"
+                        latin:keyStyle="f1PopupStyle" />
                 </default>
             </switch>
         </case>
-        <!-- hasSettingsKey="false" -->
+        <!-- hasSettingsKey="true" or navigateAction="true" -->
         <default>
             <switch>
                 <case
@@ -78,37 +63,30 @@
                 >
                     <Key
                         latin:keyLabel="/"
-                        latin:keyStyle="settingsPopupStyle" />
+                        latin:keyWidth="9.2%p"
+                        latin:keyStyle="f1PopupStyle" />
                 </case>
                 <case
                     latin:mode="email"
                 >
                     <Key
                         latin:keyLabel="\@"
-                        latin:keyStyle="settingsPopupStyle" />
+                        latin:keyWidth="9.2%p"
+                        latin:keyStyle="f1PopupStyle" />
                 </case>
+                <case
+                    latin:hasShortcutKey="true"
+                >
+                    <Key
+                        latin:keyStyle="shortcutKeyStyle"
+                        latin:keyWidth="9.2%p" />
+                </case>
+                <!-- hasShortcutKey="false" -->
                 <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="@string/keylabel_for_symbols_comma"
-                                latin:keyStyle="settingsPopupStyle" />
-                        </default>
-                    </switch>
+                    <Key
+                        latin:keyLabel="@string/keylabel_for_comma"
+                        latin:keyWidth="9.2%p"
+                        latin:keyStyle="f1PopupStyle" />
                 </default>
             </switch>
         </default>
diff --git a/java/res/xml/kbd_qwerty_row1.xml b/java/res/xml/kbd_qwerty_row1.xml
index cfd2efd..daa1382 100644
--- a/java/res/xml/kbd_qwerty_row1.xml
+++ b/java/res/xml/kbd_qwerty_row1.xml
@@ -23,13 +23,11 @@
 >
     <Row
         latin:keyWidth="10%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
             latin:keyLabel="q"
             latin:keyHintLabel="1"
-            latin:popupCharacters="@string/alternates_for_q"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="@string/alternates_for_q" />
         <Key
             latin:keyLabel="w"
             latin:keyHintLabel="2"
@@ -66,7 +64,6 @@
             latin:keyLabel="p"
             latin:keyHintLabel="0"
             latin:popupCharacters="@string/alternates_for_p"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
 </merge>
diff --git a/java/res/xml/kbd_qwerty_row2.xml b/java/res/xml/kbd_qwerty_row2.xml
index 57bbad7..6e4872e 100644
--- a/java/res/xml/kbd_qwerty_row2.xml
+++ b/java/res/xml/kbd_qwerty_row2.xml
@@ -27,8 +27,7 @@
         <Key
             latin:keyLabel="a"
             latin:popupCharacters="@string/alternates_for_a"
-            latin:keyXPos="5%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyXPos="5%p" />
         <Key
             latin:keyLabel="s"
             latin:popupCharacters="@string/alternates_for_s" />
@@ -49,8 +48,7 @@
             latin:popupCharacters="@string/alternates_for_k" />
         <Key
             latin:keyLabel="l"
-            latin:popupCharacters="@string/alternates_for_l"
-            latin:keyEdgeFlags="right" />
+            latin:popupCharacters="@string/alternates_for_l" />
         <!-- 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 98f0404..6a20355 100644
--- a/java/res/xml/kbd_qwerty_row3.xml
+++ b/java/res/xml/kbd_qwerty_row3.xml
@@ -27,8 +27,7 @@
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="15%p"
-            latin:visualInsetsRight="1%p"
-            latin:keyEdgeFlags="left" />
+            latin:visualInsetsRight="1%p" />
         <Key
             latin:keyLabel="z"
             latin:popupCharacters="@string/alternates_for_z" />
@@ -50,7 +49,6 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="fillBoth"
-            latin:visualInsetsLeft="1%p"
-            latin:keyEdgeFlags="right" />
+            latin:visualInsetsLeft="1%p" />
     </Row>
 </merge>
diff --git a/java/res/xml/kbd_qwerty_row4.xml b/java/res/xml/kbd_qwerty_row4.xml
index 298f0f6..eb1e9b8 100644
--- a/java/res/xml/kbd_qwerty_row4.xml
+++ b/java/res/xml/kbd_qwerty_row4.xml
@@ -23,19 +23,33 @@
 >
     <Row
         latin:keyWidth="10%p"
-        latin:rowEdgeFlags="bottom"
     >
         <switch>
             <case
-                latin:hasSettingsKey="true"
+                latin:hasSettingsKey="false"
+                latin:navigateAction="false"
             >
                 <Key
                     latin:keyStyle="toSymbolKeyStyle"
-                    latin:keyWidth="13.75%p"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyWidth="15%p" />
+                <include
+                    latin:keyboardLayout="@xml/kbd_qwerty_f1" />
                 <Key
-                    latin:keyStyle="settingsKeyStyle"
-                    latin:keyWidth="9.2%p" />
+                    latin:keyStyle="spaceKeyStyle"
+                    latin:keyWidth="50%p" />
+                <Key
+                    latin:keyStyle="punctuationKeyStyle" />
+                <Key
+                    latin:keyStyle="returnKeyStyle"
+                    latin:keyWidth="fillRight" />
+            </case>
+            <!-- hasSettingsKey="true" or navigateAction="true" -->
+            <default>
+                <Key
+                    latin:keyStyle="toSymbolKeyStyle"
+                    latin:keyWidth="13.75%p" />
+                <include
+                    latin:keyboardLayout="@xml/kbd_settings_or_tab" />
                 <include
                     latin:keyboardLayout="@xml/kbd_qwerty_f1" />
                 <Key
@@ -44,36 +58,9 @@
                 <Key
                     latin:keyStyle="punctuationKeyStyle"
                     latin:keyWidth="9.2%p" />
-            </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" />
-                <Key
-                    latin:keyStyle="punctuationKeyStyle" />
-            </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" />
+                    latin:keyWidth="fillRight" />
             </default>
         </switch>
     </Row>
diff --git a/java/res/xml/kbd_rows_arabic.xml b/java/res/xml/kbd_rows_arabic.xml
index 07e6274..daa2a65 100644
--- a/java/res/xml/kbd_rows_arabic.xml
+++ b/java/res/xml/kbd_rows_arabic.xml
@@ -29,8 +29,7 @@
         <Key
             latin:keyLabel="ض"
             latin:keyHintLabel="1"
-            latin:popupCharacters="1,١"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="1,١" />
         <Key
             latin:keyLabel="ص"
             latin:keyHintLabel="2"
@@ -43,7 +42,7 @@
         <Key
             latin:keyLabel="ف"
             latin:keyHintLabel="4"
-            latin:popupCharacters="\u06a4,4,٤" />
+            latin:popupCharacters="4,٤,\u06a4" />
         <Key
             latin:keyLabel="غ"
             latin:keyHintLabel="5"
@@ -71,15 +70,13 @@
             latin:keyLabel="ج"
             latin:keyHintLabel="0"
             latin:popupCharacters="0,٠,\u0686"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
         latin:keyWidth="10%p"
     >
         <Key
-            latin:keyLabel="ش"
-            latin:keyEdgeFlags="left" />
+            latin:keyLabel="ش" />
         <Key
             latin:keyLabel="س" />
         <!-- \u0626: ARABIC LETTER YEH WITH HAMZA ABOVE
@@ -119,16 +116,14 @@
         <Key
             latin:keyLabel="ك"
             latin:popupCharacters="گ"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
         latin:keyWidth="10%p"
     >
         <Key
             latin:keyLabel="ظ"
-            latin:keyXPos="5.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyXPos="5.0%p" />
         <Key
             latin:keyLabel="ط" />
         <Key
@@ -148,8 +143,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="fillRight"
-            latin:visualInsetsLeft="1%p"
-            latin:keyEdgeFlags="right" />
+            latin:visualInsetsLeft="1%p" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
diff --git a/java/res/xml/kbd_rows_azerty.xml b/java/res/xml/kbd_rows_azerty.xml
index 96efb66..dbf25eb 100644
--- a/java/res/xml/kbd_rows_azerty.xml
+++ b/java/res/xml/kbd_rows_azerty.xml
@@ -25,13 +25,11 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
         latin:keyWidth="10%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
             latin:keyLabel="a"
             latin:keyHintLabel="1"
-            latin:popupCharacters="@string/alternates_for_a"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="@string/alternates_for_a" />
         <Key
             latin:keyLabel="z"
             latin:keyHintLabel="2"
@@ -68,16 +66,14 @@
             latin:keyLabel="p"
             latin:keyHintLabel="0"
             latin:popupCharacters="@string/alternates_for_p"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
         latin:keyWidth="10%p"
     >
         <Key
             latin:keyLabel="q"
-            latin:popupCharacters="@string/alternates_for_q"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="@string/alternates_for_q" />
         <Key
             latin:keyLabel="s"
             latin:popupCharacters="@string/alternates_for_s" />
@@ -101,8 +97,7 @@
             latin:popupCharacters="@string/alternates_for_l" />
         <Key
             latin:keyLabel="m"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
         latin:keyWidth="10%p"
@@ -110,8 +105,7 @@
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="15%p"
-            latin:visualInsetsRight="1%p"
-            latin:keyEdgeFlags="left" />
+            latin:visualInsetsRight="1%p" />
         <Key
             latin:keyLabel="w"
             latin:popupCharacters="@string/alternates_for_w" />
@@ -135,8 +129,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="fillRight"
-            latin:visualInsetsLeft="1%p"
-            latin:keyEdgeFlags="right" />
+            latin:visualInsetsLeft="1%p" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
diff --git a/java/res/xml/kbd_rows_hebrew.xml b/java/res/xml/kbd_rows_hebrew.xml
index af34885..1ec0768 100644
--- a/java/res/xml/kbd_rows_hebrew.xml
+++ b/java/res/xml/kbd_rows_hebrew.xml
@@ -25,12 +25,10 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
         latin:keyWidth="10%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
             latin:keyLabel="ק"
-            latin:keyXPos="5%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyXPos="5%p" />
         <Key
             latin:keyLabel="ר" />
         <Key
@@ -48,15 +46,13 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="fillRight"
-            latin:visualInsetsLeft="1%p"
-            latin:keyEdgeFlags="right" />
+            latin:visualInsetsLeft="1%p" />
     </Row>
     <Row
         latin:keyWidth="10%p"
     >
         <Key
-            latin:keyLabel="ש"
-            latin:keyEdgeFlags="left" />
+            latin:keyLabel="ש" />
         <Key
             latin:keyLabel="ד" />
         <Key
@@ -78,8 +74,7 @@
             latin:keyLabel="ך" />
         <Key
             latin:keyLabel="ף"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
         latin:keyWidth="10%p"
@@ -87,8 +82,7 @@
         <Key
             latin:keyLabel="ז"
             latin:popupCharacters="ז׳"
-            latin:keyXPos="5%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyXPos="5%p" />
         <Key
             latin:keyLabel="ס" />
         <Key
@@ -107,8 +101,7 @@
             latin:popupCharacters="ת׳" />
         <Key
             latin:keyLabel="ץ"
-            latin:popupCharacters="ץ׳"
-            latin:keyEdgeFlags="right" />
+            latin:popupCharacters="ץ׳" />
         <!-- Here is 5%p space -->
     </Row>
     <include
diff --git a/java/res/xml/kbd_rows_qwertz.xml b/java/res/xml/kbd_rows_qwertz.xml
index 347ef60..f948921 100644
--- a/java/res/xml/kbd_rows_qwertz.xml
+++ b/java/res/xml/kbd_rows_qwertz.xml
@@ -25,13 +25,11 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
         latin:keyWidth="10%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
             latin:keyLabel="q"
             latin:keyHintLabel="1"
-            latin:popupCharacters="@string/alternates_for_q"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="@string/alternates_for_q" />
         <Key
             latin:keyLabel="w"
             latin:keyHintLabel="2"
@@ -68,8 +66,7 @@
             latin:keyLabel="p"
             latin:keyHintLabel="0"
             latin:popupCharacters="@string/alternates_for_p"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row2" />
@@ -79,8 +76,7 @@
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="15%p"
-            latin:visualInsetsRight="1%p"
-            latin:keyEdgeFlags="left" />
+            latin:visualInsetsRight="1%p" />
         <Key
             latin:keyLabel="y"
             latin:popupCharacters="@string/alternates_for_y" />
@@ -102,8 +98,7 @@
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="fillRight"
-            latin:visualInsetsLeft="1%p"
-            latin:keyEdgeFlags="right" />
+            latin:visualInsetsLeft="1%p" />
     </Row>
    <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
diff --git a/java/res/xml/kbd_rows_russian.xml b/java/res/xml/kbd_rows_russian.xml
index 2c10c31..216d749 100644
--- a/java/res/xml/kbd_rows_russian.xml
+++ b/java/res/xml/kbd_rows_russian.xml
@@ -25,14 +25,12 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
         latin:keyWidth="9.091%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
             latin:keyLabel="й"
             latin:keyHintLabel="1"
             latin:popupCharacters="1"
-            latin:keyWidth="8.75%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="8.75%p" />
         <Key
             latin:keyLabel="ц"
             latin:keyHintLabel="2"
@@ -71,16 +69,14 @@
             latin:popupCharacters="0" />
         <Key
             latin:keyLabel="х"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
             latin:keyWidth="9.091%p"
     >
         <Key
             latin:keyLabel="ф"
-            latin:keyWidth="8.75%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="8.75%p" />
         <Key
             latin:keyLabel="ы" />
         <Key
@@ -101,16 +97,14 @@
             latin:keyLabel="ж" />
         <Key
             latin:keyLabel="э"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
         latin:keyWidth="8.5%p"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="11.75%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="11.75%p" />
         <Key
             latin:keyLabel="я" />
         <Key
@@ -132,8 +126,7 @@
             latin:keyLabel="ю" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
diff --git a/java/res/xml/kbd_rows_scandinavian.xml b/java/res/xml/kbd_rows_scandinavian.xml
index 3f25601..7e8902e 100644
--- a/java/res/xml/kbd_rows_scandinavian.xml
+++ b/java/res/xml/kbd_rows_scandinavian.xml
@@ -25,14 +25,12 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
         latin:keyWidth="9.09%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
             latin:keyLabel="q"
             latin:keyHintLabel="1"
             latin:popupCharacters="@string/alternates_for_q"
-            latin:keyWidth="8.75%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="8.75%p" />
         <Key
             latin:keyLabel="w"
             latin:keyHintLabel="2"
@@ -71,8 +69,7 @@
             latin:popupCharacters="@string/alternates_for_p" />
         <Key
             latin:keyLabel="å"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
         latin:keyWidth="9.09%p"
@@ -80,8 +77,7 @@
         <Key
             latin:keyLabel="a"
             latin:popupCharacters="@string/alternates_for_a"
-            latin:keyWidth="8.75%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyWidth="8.75%p" />
         <Key
             latin:keyLabel="s"
             latin:popupCharacters="@string/alternates_for_s" />
@@ -109,8 +105,7 @@
         <Key
             latin:keyLabel="@string/keylabel_for_scandinavia_row2_11"
             latin:popupCharacters="@string/alternates_for_scandinavia_row2_11"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row3" />
diff --git a/java/res/xml/kbd_rows_serbian.xml b/java/res/xml/kbd_rows_serbian.xml
index 2bed276..73c807f 100644
--- a/java/res/xml/kbd_rows_serbian.xml
+++ b/java/res/xml/kbd_rows_serbian.xml
@@ -25,13 +25,11 @@
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
         latin:keyWidth="9.09%p"
-        latin:rowEdgeFlags="top"
     >
         <Key
             latin:keyLabel="љ"
             latin:keyHintLabel="1"
-            latin:popupCharacters="1"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="1" />
         <Key
             latin:keyLabel="њ"
             latin:keyHintLabel="2"
@@ -70,15 +68,13 @@
             latin:popupCharacters="0" />
         <Key
             latin:keyLabel="ш"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
         latin:keyWidth="9.09%p"
     >
         <Key
-            latin:keyLabel="а"
-            latin:keyEdgeFlags="left" />
+            latin:keyLabel="а" />
         <Key
             latin:keyLabel="с" />
         <Key
@@ -99,15 +95,13 @@
             latin:keyLabel="ч" />
         <Key
             latin:keyLabel="ћ"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
         latin:keyWidth="8.90%p"
     >
         <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="shiftKeyStyle" />
         <Key
             latin:keyLabel="ѕ" />
         <Key
@@ -128,8 +122,7 @@
             latin:keyLabel="ж" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
+            latin:keyWidth="fillRight" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row4" />
diff --git a/java/res/xml/kbd_rows_spanish.xml b/java/res/xml/kbd_rows_spanish.xml
index c5ead10..6d00718 100644
--- a/java/res/xml/kbd_rows_spanish.xml
+++ b/java/res/xml/kbd_rows_spanish.xml
@@ -30,8 +30,7 @@
     >
         <Key
             latin:keyLabel="a"
-            latin:popupCharacters="@string/alternates_for_a"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="@string/alternates_for_a" />
         <Key
             latin:keyLabel="s"
             latin:popupCharacters="@string/alternates_for_s" />
@@ -54,8 +53,7 @@
             latin:keyLabel="l"
             latin:popupCharacters="@string/alternates_for_l" />
         <Key
-            latin:keyLabel="ñ"
-            latin:keyEdgeFlags="right" />
+            latin:keyLabel="ñ" />
     </Row>
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_row3" />
diff --git a/java/res/xml-sw600dp/kbd_symbols.xml b/java/res/xml/kbd_rows_symbols.xml
similarity index 60%
copy from java/res/xml-sw600dp/kbd_symbols.xml
copy to java/res/xml/kbd_rows_symbols.xml
index dea08dd..75e21a0 100644
--- a/java/res/xml-sw600dp/kbd_symbols.xml
+++ b/java/res/xml/kbd_rows_symbols.xml
@@ -18,21 +18,19 @@
 */
 -->
 
-<Keyboard
+<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_currency_key_styles" />
-    <!-- This row is intentionally not marked as a top row -->
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="10%p"
     >
         <Key
             latin:keyLabel="@string/keylabel_for_symbols_1"
-            latin:popupCharacters="@string/alternates_for_symbols_1"
-            latin:keyEdgeFlags="left" />
+            latin:popupCharacters="@string/alternates_for_symbols_1" />
         <Key
             latin:keyLabel="@string/keylabel_for_symbols_2"
             latin:popupCharacters="@string/alternates_for_symbols_2" />
@@ -59,24 +57,21 @@
             latin:popupCharacters="@string/alternates_for_symbols_9" />
         <Key
             latin:keyLabel="@string/keylabel_for_symbols_0"
-            latin:popupCharacters="@string/alternates_for_symbols_0" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyXPos="-10.0%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:popupCharacters="@string/alternates_for_symbols_0"
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="10%p"
     >
         <Key
-            latin:keyLabel="#"
-            latin:keyXPos="4.5%p" />
+            latin:keyLabel="\@" />
+        <Key
+            latin:keyLabel="\#" />
         <Key
             latin:keyStyle="currencyKeyStyle" />
         <Key
-            latin:keyLabel="%"
-            latin:popupCharacters="‰" />
+            latin:keyLabel="@string/keylabel_for_symbols_percent"
+            latin:popupCharacters="@string/alternates_for_symbols_percent" />
         <Key
             latin:keyLabel="&amp;" />
         <Key
@@ -93,88 +88,43 @@
             latin:popupCharacters="[,{,&lt;" />
         <Key
             latin:keyLabel=")"
-            latin:popupCharacters="],},&gt;" />
-        <Key
-            latin:keyStyle="returnKeyStyle"
-            latin:keyXPos="-14.6%p"
-            latin:keyWidth="fillBoth"
-            latin:keyEdgeFlags="right" />
+            latin:popupCharacters="],},&gt;"
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
-        latin:keyWidth="8.9%p"
+        latin:keyWidth="10%p"
     >
         <Key
-            latin:keyStyle="moreKeyStyle"
-            latin:keyWidth="10.0%p"
-            latin:keyEdgeFlags="left" />
+            latin:keyStyle="toMoreSymbolKeyStyle"
+            latin:keyWidth="15%p"
+            latin:visualInsetsRight="1%p" />
         <Key
-            latin:keyLabel="&lt;"
-            latin:popupCharacters="≤,«,‹" />
+            latin:keyLabel="!"
+            latin:popupCharacters="¡" />
+        <!-- Note: DroidSans doesn't have double-high-reversed-quotation '\u201f' glyph. -->
+        <!-- latin:popupCharacters="“,”,„,‟,«,»" -->
         <Key
-            latin:keyLabel="&gt;"
-            latin:popupCharacters="≥,»,›" />
+            latin:keyLabel="&quot;"
+            latin:popupCharacters="“,”,«,»"
+            latin:maxPopupKeyboardColumn="6" />
         <Key
-            latin:keyLabel="="
-            latin:popupCharacters="≠,≈" />
-        <switch>
-            <case
-                latin:mode="url"
-            >
-                <Key
-                    latin:keyLabel="\'"
-                    latin:popupCharacters="‘,’,‚,‛" />
-            </case>
-            <default>
-                <Key
-                    latin:keyLabel=":" />
-            </default>
-        </switch>
+            latin:keyLabel="\'"
+            latin:popupCharacters="‘,’,‚,‛" />
+        <Key
+            latin:keyLabel=":" />
         <Key
             latin:keyLabel="@string/keylabel_for_symbols_semicolon"
             latin:popupCharacters="@string/alternates_for_symbols_semicolon" />
         <Key
-            latin:keyLabel="@string/keylabel_for_symbols_comma"
-            latin:popupCharacters="@string/alternates_for_symbols_comma" />
-        <Key
-            latin:keyLabel="." />
-        <Key
-            latin:keyLabel="!"
-            latin:popupCharacters="¡" />
+            latin:keyLabel="/" />
         <Key
             latin:keyLabel="@string/keylabel_for_symbols_question"
             latin:popupCharacters="@string/alternates_for_symbols_question" />
         <Key
-            latin:keyLabel="/"
-            latin:keyWidth="fillRight" />
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight"
+            latin:visualInsetsLeft="1%p" />
     </Row>
-    <!-- This row is intentionally not marked as a bottom row -->
-    <Row
-        latin:keyWidth="8.9%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyLabelOption="alignLeft"
-            latin:keyWidth="13.0%p"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyStyle="tabKeyStyle" />
-        <Key
-            latin:keyLabel="\@" />
-        <Key
-            latin:keyStyle="spaceKeyStyle"
-            latin:keyXPos="30.750%p"
-            latin:keyWidth="39.750%p" />
-        <!-- Note: DroidSans doesn't have double-high-reversed-quotation '\u201f' glyph. -->
-        <!-- latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛" -->
-        <Key
-            latin:keyLabel="&quot;"
-            latin:popupCharacters="“,”,«,»,‘,’,‚,‛" />
-        <Key
-            latin:keyLabel="_" />
-        <Spacer
-            latin:keyXPos="-10.00%p"
-            latin:keyWidth="0%p" />
-        <include
-            latin:keyboardLayout="@xml/kbd_qwerty_f2" />
-    </Row>
-</Keyboard>
+    <include
+        latin:keyboardLayout="@xml/kbd_symbols_row4" />
+</merge>
diff --git a/java/res/xml/kbd_rows_symbols_shift.xml b/java/res/xml/kbd_rows_symbols_shift.xml
new file mode 100644
index 0000000..d523415
--- /dev/null
+++ b/java/res/xml/kbd_rows_symbols_shift.xml
@@ -0,0 +1,125 @@
+<?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.
+*/
+-->
+
+<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="~" />
+        <Key
+            latin:keyLabel="`" />
+        <Key
+            latin:keyLabel="|" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="•"
+            latin:popupCharacters="♪,♥,♠,♦,♣" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="√" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="π"
+            latin:popupCharacters="Π" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="÷" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="×" />
+        <Key
+            latin:keyLabel="{" />
+        <Key
+            latin:keyLabel="}"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row
+        latin:keyWidth="10%p"
+    >
+        <Key
+            latin:keyStyle="nonSpecialBackgroundTabKeyStyle" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="£" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="¢" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="€" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="°"
+            latin:popupCharacters="′,″" />
+        <Key
+            latin:keyLabel="^"
+            latin:popupCharacters="↑,↓,←,→" />
+        <Key
+            latin:keyLabel="_" />
+        <Key
+            latin:keyLabel="="
+            latin:popupCharacters="≠,≈,∞" />
+        <Key
+            latin:keyLabel="[" />
+        <Key
+            latin:keyLabel="]"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row
+        latin:keyWidth="10%p"
+    >
+        <Key
+            latin:keyStyle="backFromMoreSymbolKeyStyle"
+            latin:keyWidth="15%p"
+            latin:visualInsetsRight="1%p" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="™" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="®" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="©" />
+        <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="¶"
+            latin:popupCharacters="§" />
+        <Key
+            latin:keyLabel="\\" />
+        <Key
+            latin:keyLabel="&lt;"
+            latin:popupCharacters="≤,«,‹" />
+        <Key
+            latin:keyLabel="&gt;"
+            latin:popupCharacters="≥,»,›" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight"
+            latin:visualInsetsLeft="1%p" />
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/kbd_symbols_shift_row4" />
+</merge>
diff --git a/java/res/xml/kbd_settings_or_tab.xml b/java/res/xml/kbd_settings_or_tab.xml
new file mode 100644
index 0000000..4a8bcc7
--- /dev/null
+++ b/java/res/xml/kbd_settings_or_tab.xml
@@ -0,0 +1,45 @@
+<?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:hasSettingsKey="true"
+        >
+            <Key
+                latin:keyStyle="settingsKeyStyle"
+                latin:keyWidth="9.2%p" />
+        </case>
+        <!-- hasSettingsKey="false" -->
+        <case
+            latin:navigateAction="true"
+        >
+            <Key
+                latin:keyStyle="tabKeyStyle"
+                latin:keyWidth="9.2%p" />
+        </case>
+        <!-- hasSettingsKey="false" and navigateAction="false" -->
+        <default>
+            <!-- No key. -->
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/kbd_symbols.xml b/java/res/xml/kbd_symbols.xml
index 8f682d9..737f684 100644
--- a/java/res/xml/kbd_symbols.xml
+++ b/java/res/xml/kbd_symbols.xml
@@ -1,133 +1,26 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* 
+/*
 **
 ** Copyright 2008, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 -->
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyWidth="10%p"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_key_styles" />
-    <include
-        latin:keyboardLayout="@xml/kbd_currency_key_styles" />
-    <Row
-        latin:rowEdgeFlags="top"
-    >
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_1"
-            latin:popupCharacters="@string/alternates_for_symbols_1"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_2"
-            latin:popupCharacters="@string/alternates_for_symbols_2" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_3"
-            latin:popupCharacters="@string/alternates_for_symbols_3" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_4"
-            latin:popupCharacters="@string/alternates_for_symbols_4" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_5"
-            latin:popupCharacters="@string/alternates_for_symbols_5" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_6"
-            latin:popupCharacters="@string/alternates_for_symbols_6" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_7"
-            latin:popupCharacters="@string/alternates_for_symbols_7" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_8"
-            latin:popupCharacters="@string/alternates_for_symbols_8" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_9"
-            latin:popupCharacters="@string/alternates_for_symbols_9" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_0"
-            latin:popupCharacters="@string/alternates_for_symbols_0"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyLabel="\@"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="\#" />
-        <Key
-            latin:keyStyle="currencyKeyStyle" />
-        <Key
-            latin:keyLabel="%"
-            latin:popupCharacters="‰" />
-        <Key
-            latin:keyLabel="&amp;" />
-        <Key
-            latin:keyLabel="*"
-            latin:popupCharacters="†,‡,★" />
-        <Key
-            latin:keyLabel="-"
-            latin:popupCharacters="_,–,—" />
-        <Key
-            latin:keyLabel="+"
-            latin:popupCharacters="±" />
-        <Key
-            latin:keyLabel="("
-            latin:popupCharacters="[,{,&lt;" />
-        <Key
-            latin:keyLabel=")"
-            latin:popupCharacters="],},&gt;"
-            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="!"
-            latin:popupCharacters="¡" />
-        <!-- Note: DroidSans doesn't have double-high-reversed-quotation '\u201f' glyph. -->
-        <!-- latin:popupCharacters="“,”,„,‟,«,»" -->
-        <Key
-            latin:keyLabel="&quot;"
-            latin:popupCharacters="“,”,«,»"
-            latin:maxPopupKeyboardColumn="6" />
-        <Key
-            latin:keyLabel="\'"
-            latin:popupCharacters="‘,’,‚,‛" />
-        <Key
-            latin:keyLabel=":" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_semicolon"
-            latin:popupCharacters="@string/alternates_for_symbols_semicolon" />
-        <Key
-            latin:keyLabel="/" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_question"
-            latin:popupCharacters="@string/alternates_for_symbols_question" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:visualInsetsLeft="1%p"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/kbd_symbols_row4" />
+        latin:keyboardLayout="@xml/kbd_rows_symbols" />
 </Keyboard>
diff --git a/java/res/xml/kbd_symbols_f1.xml b/java/res/xml/kbd_symbols_f1.xml
index 3c7346c..0dd3d91 100644
--- a/java/res/xml/kbd_symbols_f1.xml
+++ b/java/res/xml/kbd_symbols_f1.xml
@@ -23,41 +23,40 @@
 >
     <switch>
         <case
-            latin:hasSettingsKey="true"
+            latin:hasSettingsKey="false"
+            latin:navigateAction="false"
         >
             <switch>
                 <case
-                    latin:hasVoiceKey="true"
+                    latin:hasShortcutKey="true"
+                >
+                    <Key
+                        latin:keyStyle="shortcutKeyStyle" />
+                </case>
+                <!-- latin:hasShortcutKey="false" -->
+                <default>
+                    <Key
+                        latin:keyLabel="@string/keylabel_for_f1"
+                        latin:keyStyle="f1PopupStyle" />
+                </default>
+            </switch>
+        </case>
+        <!-- hasSettingsKey="true" or navigateAction="true" -->
+        <default>
+            <switch>
+                <case
+                    latin:hasShortcutKey="true"
                 >
                     <Key
                         latin:keyStyle="shortcutKeyStyle"
                         latin:keyWidth="9.2%p" />
                 </case>
-                <!-- latin:hasVoiceKey="false" -->
+                <!-- latin:hasShortcutKey="false" -->
                 <default>
                     <Key
-                        latin:keyLabel="@string/keylabel_for_symbols_comma"
-                        latin:popupCharacters="@string/alternates_for_symbols_comma"
+                        latin:keyLabel="@string/keylabel_for_f1"
                         latin:keyWidth="9.2%p"
-                        latin:keyStyle="settingsPopupStyle" />
-                </default>
-            </switch>
-        </case>
-        <!-- hasSettingsKey="false" -->
-        <default>
-            <switch>
-                <case
-                    latin:hasVoiceKey="true"
-                >
-                    <Key
-                        latin:keyStyle="shortcutKeyStyle" />
-                </case>
-                <!-- latin:hasVoiceKey="false" -->
-                <default>
-                    <Key
-                        latin:keyLabel="@string/keylabel_for_symbols_comma"
-                        latin:popupCharacters="@string/alternates_for_symbols_comma"
-                        latin:keyStyle="settingsPopupStyle" />
+                        latin:keyStyle="f1PopupStyle" />
                 </default>
             </switch>
         </default>
diff --git a/java/res/xml/kbd_symbols_row4.xml b/java/res/xml/kbd_symbols_row4.xml
index 24313ac..864cf2b 100644
--- a/java/res/xml/kbd_symbols_row4.xml
+++ b/java/res/xml/kbd_symbols_row4.xml
@@ -23,57 +23,44 @@
 >
     <Row
         latin:keyWidth="10%p"
-        latin:rowEdgeFlags="bottom"
     >
         <switch>
             <case
-                latin:hasSettingsKey="true"
+                latin:hasSettingsKey="false"
+                latin:navigateAction="false"
             >
                 <Key
                     latin:keyStyle="toAlphaKeyStyle"
-                    latin:keyWidth="13.75%p"
-                    latin:keyEdgeFlags="left" />
-                <Key
-                    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:keyStyle="basePunctuationKeyStyle"
-                    latin:keyWidth="9.2%p" />
-            </case>
-            <!-- latin:hasSettingsKey="false" -->
-            <default>
-                <Key
-                    latin:keyStyle="toAlphaKeyStyle"
-                    latin:keyWidth="15%p"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyWidth="15%p" />
                 <include
                     latin:keyboardLayout="@xml/kbd_symbols_f1" />
                 <Key
                     latin:keyStyle="spaceKeyStyle"
                     latin:keyWidth="50%p" />
                 <Key
-                    latin:keyStyle="basePunctuationKeyStyle" />
-            </default>
-        </switch>
-        <switch>
-            <case
-                latin:mode="im"
-            >
-                <Key
-                    latin:keyStyle="smileyKeyStyle"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
-            </case>
-            <default>
+                    latin:keyStyle="punctuationKeyStyle" />
                 <Key
                     latin:keyStyle="returnKeyStyle"
-                    latin:keyWidth="fillRight"
-                    latin:keyEdgeFlags="right" />
+                    latin:keyWidth="fillRight" />
+            </case>
+            <!-- hasSettingsKey="true" or navigateAction="true" -->
+            <default>
+                <Key
+                    latin:keyStyle="toAlphaKeyStyle"
+                    latin:keyWidth="13.75%p" />
+                <include
+                    latin:keyboardLayout="@xml/kbd_settings_or_tab" />
+                <include
+                    latin:keyboardLayout="@xml/kbd_qwerty_f1" />
+                <Key
+                    latin:keyStyle="spaceKeyStyle"
+                    latin:keyWidth="35.83%p" />
+                <Key
+                    latin:keyStyle="punctuationKeyStyle"
+                    latin:keyWidth="9.2%p" />
+                <Key
+                    latin:keyStyle="returnKeyStyle"
+                    latin:keyWidth="fillRight" />
             </default>
         </switch>
     </Row>
diff --git a/java/res/xml/kbd_symbols_shift.xml b/java/res/xml/kbd_symbols_shift.xml
index f22d45a..9c163d6 100644
--- a/java/res/xml/kbd_symbols_shift.xml
+++ b/java/res/xml/kbd_symbols_shift.xml
@@ -1,127 +1,26 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* 
+/*
 **
 ** Copyright 2008, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 -->
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyWidth="10%p"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_key_styles" />
-    <Row
-        latin:rowEdgeFlags="top"
-    >
-        <Key
-            latin:keyLabel="~"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="`" />
-        <Key
-            latin:keyLabel="|" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="•"
-            latin:popupCharacters="♪,♥,♠,♦,♣" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="√" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="π"
-            latin:popupCharacters="Π" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="÷" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="×" />
-        <Key
-            latin:keyLabel="{" />
-        <Key
-            latin:keyLabel="}"
-            latin:keyWidth="fillRight"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyStyle="nonSpecialBackgroundTabKeyStyle"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="£" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="¢" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="€" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="°"
-            latin:popupCharacters="′,″" />
-        <Key
-            latin:keyLabel="^"
-            latin:popupCharacters="↑,↓,←,→" />
-        <Key
-            latin:keyLabel="_" />
-        <Key
-            latin:keyLabel="="
-            latin:popupCharacters="≠,≈,∞" />
-        <Key
-            latin:keyLabel="[" />
-        <Key
-            latin:keyLabel="]"
-            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"
-            latin:keyLabel="™" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="®" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="©" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="¶"
-            latin:popupCharacters="§" />
-        <Key
-            latin:keyLabel="\\" />
-        <Key
-            latin:keyLabel="&lt;"
-            latin:popupCharacters="≤,«,‹" />
-        <Key
-            latin:keyLabel="&gt;"
-            latin:popupCharacters="≥,»,›" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:visualInsetsLeft="1%p"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <include latin:keyboardLayout="@xml/kbd_symbols_shift_row4" />
+        latin:keyboardLayout="@xml/kbd_rows_symbols_shift" />
 </Keyboard>
diff --git a/java/res/xml/kbd_symbols_shift_row4.xml b/java/res/xml/kbd_symbols_shift_row4.xml
index 90a96e4..9cb453f 100644
--- a/java/res/xml/kbd_symbols_shift_row4.xml
+++ b/java/res/xml/kbd_symbols_shift_row4.xml
@@ -23,19 +23,36 @@
 >
     <Row
         latin:keyWidth="10%p"
-        latin:rowEdgeFlags="bottom"
     >
         <switch>
             <case
-                latin:hasSettingsKey="true"
+                latin:hasSettingsKey="false"
+                latin:navigateAction="false"
             >
                 <Key
                     latin:keyStyle="toAlphaKeyStyle"
-                    latin:keyWidth="13.75%p"
-                    latin:keyEdgeFlags="left" />
+                    latin:keyWidth="15%p" />
                 <Key
-                    latin:keyStyle="settingsKeyStyle"
-                    latin:keyWidth="9.2%p" />
+                    latin:keyLabel="„"
+                    latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛"
+                    latin:keyStyle="nonPasswordFunctionalKeyStyle" />
+                <Key
+                    latin:keyStyle="spaceKeyStyle"
+                    latin:keyWidth="50%p" />
+                <Key
+                    latin:keyLabel="…"
+                    latin:keyStyle="nonPasswordFunctionalKeyStyle" />
+                <Key
+                    latin:keyStyle="returnKeyStyle"
+                    latin:keyWidth="fillRight" />
+            </case>
+            <!-- hasSettingsKey="true" or navigateAction="true" -->
+            <default>
+                <Key
+                    latin:keyStyle="toAlphaKeyStyle"
+                    latin:keyWidth="13.75%p" />
+                <include
+                    latin:keyboardLayout="@xml/kbd_settings_or_tab" />
                 <Key
                     latin:keyLabel="„"
                     latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛"
@@ -48,39 +65,9 @@
                     latin:keyLabel="…"
                     latin:keyWidth="9.2%p"
                     latin:keyStyle="nonPasswordFunctionalKeyStyle" />
-            </case>
-            <!-- latin:hasSettingsKey="false" -->
-            <default>
-                <Key
-                    latin:keyStyle="toAlphaKeyStyle"
-                    latin:keyWidth="15%p"
-                    latin:keyEdgeFlags="left" />
-                <Key
-                    latin:keyLabel="„"
-                    latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛"
-                    latin:keyStyle="nonPasswordFunctionalKeyStyle" />
-                <Key
-                    latin:keyStyle="spaceKeyStyle"
-                    latin:keyWidth="50%p" />
-                <Key
-                    latin:keyLabel="…"
-                    latin:keyStyle="nonPasswordFunctionalKeyStyle" />
-            </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" />
+                    latin:keyWidth="fillRight" />
             </default>
         </switch>
     </Row>
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 7aaf57b..fbbc7fb 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -3,16 +3,16 @@
 /**
  * Copyright (c) 2008, The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); 
- * you may not use this file except in compliance with the License. 
- * You may obtain a copy of the License at 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0 
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software 
- * distributed under the License is distributed on an "AS IS" BASIS, 
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
- * See the License for the specific language governing permissions and 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 -->
@@ -24,237 +24,132 @@
 <!-- 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"
         android:settingsActivity="com.android.inputmethod.latin.Settings"
         android:isDefault="@bool/im_is_default">
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_en_US_keyboard"
+            android:label="@string/subtype_en_US"
             android:imeSubtypeLocale="en_US"
             android:imeSubtypeMode="keyboard"
             android:imeSubtypeExtraValue="TrySuppressingImeSwitcher"
     />
-<!--     <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:label="@string/subtype_en_GB"
             android:imeSubtypeLocale="en_GB"
             android:imeSubtypeMode="keyboard"
             android:imeSubtypeExtraValue="TrySuppressingImeSwitcher"
     />
-    <!-- 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:label="@string/subtype_generic"
             android:imeSubtypeLocale="ar"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_cs_keyboard"
+            android:label="@string/subtype_generic"
             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_keyboard"
-            android:label="@string/subtype_mode_da_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="da"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_de_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="de"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_de_qwerty_keyboard"
+            android:label="@string/subtype_de_qwerty"
             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:label="@string/subtype_generic"
             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_keyboard"
-            android:label="@string/subtype_mode_fi_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="fi"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_fr_keyboard"
+            android:label="@string/subtype_generic"
             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_keyboard"
-            android:label="@string/subtype_mode_fr_CA_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="fr_CA"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_fr_CH_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="fr_CH"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_hr_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="hr"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_hu_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="hu"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_it_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="it"
             android:imeSubtypeMode="keyboard"
     />
     <!-- 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:label="@string/subtype_generic"
             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:label="@string/subtype_generic"
             android:imeSubtypeLocale="nb"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_nl_keyboard"
+            android:label="@string/subtype_generic"
             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_keyboard"
-            android:label="@string/subtype_mode_pl_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="pl"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_pt_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="pt"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_ru_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="ru"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_sr_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="sr"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_sv_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="sv"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_tr_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="tr"
             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" -->
-<!--             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 0bf117b..552e3cf 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -49,13 +49,11 @@
             android:summary="@string/prefs_enable_recorrection_summary"
             android:persistent="true"
             android:defaultValue="@bool/config_default_recorrection_enabled" />
-        <ListPreference
-            android:key="settings_key"
+        <CheckBoxPreference
+            android:key="show_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:defaultValue="false" />
         <ListPreference
             android:key="voice_mode"
             android:title="@string/voice_input"
@@ -73,14 +71,8 @@
            <intent
               android:action="android.intent.action.MAIN"
               android:targetPackage="com.google.android.inputmethod.latin.dictionarypack"
-              android:targetClass="com.google.android.inputmethod.latin.dictionarypack.DictionarySettings" />
+              android:targetClass="com.google.android.inputmethod.latin.dictionarypack.DictionarySettingsActivity" />
         </PreferenceScreen>
-        <CheckBoxPreference
-            android:key="quick_fixes"
-            android:title="@string/quick_fixes"
-            android:summary="@string/quick_fixes_summary"
-            android:persistent="true"
-            android:defaultValue="true" />
         <ListPreference
             android:key="auto_correction_threshold"
             android:title="@string/auto_correction"
diff --git a/java/res/xml/prefs_for_debug.xml b/java/res/xml/prefs_for_debug.xml
index 477461d..2dad171 100644
--- a/java/res/xml/prefs_for_debug.xml
+++ b/java/res/xml/prefs_for_debug.xml
@@ -36,13 +36,6 @@
             />
 
     <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/res/xml/spellchecker.xml b/java/res/xml/spellchecker.xml
new file mode 100644
index 0000000..ce09264
--- /dev/null
+++ b/java/res/xml/spellchecker.xml
@@ -0,0 +1,33 @@
+<?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.
+ */
+-->
+
+<!-- The attributes in this XML file provide the configuration information -->
+<!-- for the spell checker -->
+
+<spell-checker xmlns:android="http://schemas.android.com/apk/res/android"
+        android:label="@string/spell_checker_service_name">
+    <subtype
+            android:label="@string/subtype_en_US"
+            android:subtypeLocale="en_US"
+    />
+    <subtype
+            android:label="@string/subtype_en_GB"
+            android:subtypeLocale="en_GB"
+    />
+</spell-checker>
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
index ae614b7..7e71b5f 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
@@ -16,7 +16,6 @@
 
 package com.android.inputmethod.accessibility;
 
-import android.accessibilityservice.AccessibilityServiceInfo;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.inputmethodservice.InputMethodService;
@@ -82,10 +81,8 @@
      */
     public boolean isTouchExplorationEnabled() {
         return ENABLE_ACCESSIBILITY
-                && AccessibilityEventCompatUtils.supportsTouchExploration()
                 && mAccessibilityManager.isEnabled()
-                && !mCompatManager.getEnabledAccessibilityServiceList(
-                        AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty();
+                && mCompatManager.isTouchExplorationEnabled();
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
index 89adc15..4ab9cb8 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
@@ -47,6 +47,8 @@
      */
     private static final long VIBRATE_KEY_CLICK = 50;
 
+    private static final float FX_VOLUME = -1.0f;
+
     private InputMethodService mInputMethod;
     private Vibrator mVibrator;
     private AudioManager mAudioManager;
@@ -143,7 +145,7 @@
      */
     private void sendDownUpKeyEvents(int keyCode) {
         mVibrator.vibrate(VIBRATE_KEY_CLICK);
-        mAudioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
+        mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, FX_VOLUME);
         mInputMethod.sendDownUpKeyEvents(keyCode);
     }
 
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index 8ca8341..ae9809e 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -105,28 +105,19 @@
     }
 
     /**
-     * Receives hover events when accessibility is turned on in API > 11. In
-     * earlier API levels, events are manually routed from onTouchEvent.
+     * Receives hover events when accessibility is turned on in SDK versions ICS
+     * and higher.
      *
      * @param event The hover event.
      * @return {@code true} if the event is handled
      */
-    public boolean onHoverEvent(MotionEvent event, PointerTracker tracker) {
+    public boolean dispatchHoverEvent(MotionEvent event, PointerTracker tracker) {
         if (mGestureDetector.onHoverEvent(event, this, tracker))
             return true;
 
         return onHoverEventInternal(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.
      *
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index d196c89..ec4287d 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -164,7 +164,7 @@
             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()) {
+        } else if (id.isPhoneShiftKeyboard()) {
             return context.getString(R.string.spoken_description_to_numeric);
         } else if (id.isPhoneKeyboard()) {
             return context.getString(R.string.spoken_description_to_symbol);
diff --git a/java/src/com/android/inputmethod/compat/AccessibilityEventCompatUtils.java b/java/src/com/android/inputmethod/compat/AccessibilityEventCompatUtils.java
index 5005772..2fa9d87 100644
--- a/java/src/com/android/inputmethod/compat/AccessibilityEventCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/AccessibilityEventCompatUtils.java
@@ -16,24 +16,7 @@
 
 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
index 4db1c7a..a30af0f 100644
--- a/java/src/com/android/inputmethod/compat/AccessibilityManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/AccessibilityManagerCompatWrapper.java
@@ -16,16 +16,13 @@
 
 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 static final Method METHOD_isTouchExplorationEnabled = CompatUtils.getMethod(
+            AccessibilityManager.class, "isTouchExplorationEnabled");
 
     private final AccessibilityManager mManager;
 
@@ -33,10 +30,7 @@
         mManager = manager;
     }
 
-    @SuppressWarnings("unchecked")
-    public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
-        return (List<AccessibilityServiceInfo>) CompatUtils.invoke(mManager,
-                Collections.<AccessibilityServiceInfo>emptyList(),
-                METHOD_getEnabledAccessibilityServiceList, feedbackType);
+    public boolean isTouchExplorationEnabled() {
+        return (Boolean) CompatUtils.invoke(mManager, false, METHOD_isTouchExplorationEnabled);
     }
 }
diff --git a/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java
index 8e22bbc..8315598 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.compat;
 
+import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.view.inputmethod.InputMethodInfo;
 
@@ -56,4 +57,21 @@
         return new InputMethodSubtypeCompatWrapper(CompatUtils.invoke(mImi, null,
                 METHOD_getSubtypeAt, index));
     }
+
+    public CharSequence loadLabel(PackageManager pm) {
+        return mImi.loadLabel(pm);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof InputMethodInfoCompatWrapper) {
+            return mImi.equals(((InputMethodInfoCompatWrapper)o).mImi);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mImi.hashCode();
+    }
 }
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
index 1cc13f2..51dc4cd 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -16,21 +16,28 @@
 
 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.app.AlertDialog;
 import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.os.IBinder;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 
+import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.Utils;
+
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -72,27 +79,27 @@
     private static final String VOICE_MODE = "voice";
     private static final String KEYBOARD_MODE = "keyboard";
 
+    private InputMethodServiceCompatWrapper mService;
     private InputMethodManager mImm;
+    private PackageManager mPackageManager;
+    private ApplicationInfo mApplicationInfo;
     private LanguageSwitcherProxy mLanguageSwitcherProxy;
     private String mLatinImePackageName;
 
-    private InputMethodManagerCompatWrapper() {
-    }
-
-    public static InputMethodManagerCompatWrapper getInstance(Context context) {
-        if (sInstance.mImm == null) {
-            sInstance.init(context);
-        }
+    public static InputMethodManagerCompatWrapper getInstance() {
+        if (sInstance.mImm == null)
+            Log.w(TAG, "getInstance() is called before initialization");
         return sInstance;
     }
 
-    private synchronized void init(Context context) {
-        mImm = (InputMethodManager) context.getSystemService(
+    public static void init(InputMethodServiceCompatWrapper service) {
+        sInstance.mService = service;
+        sInstance.mImm = (InputMethodManager) service.getSystemService(
                 Context.INPUT_METHOD_SERVICE);
-        if (context instanceof LatinIME) {
-            mLatinImePackageName = context.getPackageName();
-        }
-        mLanguageSwitcherProxy = LanguageSwitcherProxy.getInstance();
+        sInstance.mLatinImePackageName = service.getPackageName();
+        sInstance.mPackageManager = service.getPackageManager();
+        sInstance.mApplicationInfo = service.getApplicationInfo();
+        sInstance.mLanguageSwitcherProxy = LanguageSwitcherProxy.getInstance();
     }
 
     public InputMethodSubtypeCompatWrapper getCurrentInputMethodSubtype() {
@@ -196,11 +203,15 @@
         return shortcutMap;
     }
 
+    // We don't call this method when we switch between subtypes within this IME.
     public void setInputMethodAndSubtype(
             IBinder token, String id, InputMethodSubtypeCompatWrapper subtype) {
+        // TODO: Support subtype change on non-subtype-supported platform.
         if (subtype != null && subtype.hasOriginalObject()) {
             CompatUtils.invoke(mImm, null, METHOD_setInputMethodAndSubtype,
                     token, id, subtype.getOriginalObject());
+        } else {
+            mImm.setInputMethod(token, id);
         }
     }
 
@@ -222,6 +233,87 @@
 
     public void showInputMethodPicker() {
         if (mImm == null) return;
-        mImm.showInputMethodPicker();
+        if (SUBTYPE_SUPPORTED) {
+            mImm.showInputMethodPicker();
+            return;
+        }
+
+        // The code below are based on {@link InputMethodManager#showInputMethodMenuInternal}.
+
+        final InputMethodInfoCompatWrapper myImi = Utils.getInputMethodInfo(
+                this, mLatinImePackageName);
+        final List<InputMethodSubtypeCompatWrapper> myImsList = getEnabledInputMethodSubtypeList(
+                myImi, true);
+        final InputMethodSubtypeCompatWrapper currentIms = getCurrentInputMethodSubtype();
+        final List<InputMethodInfoCompatWrapper> imiList = getEnabledInputMethodList();
+        imiList.remove(myImi);
+        Collections.sort(imiList, new Comparator<InputMethodInfoCompatWrapper>() {
+            @Override
+            public int compare(InputMethodInfoCompatWrapper imi1,
+                    InputMethodInfoCompatWrapper imi2) {
+                final CharSequence imiId1 = imi1.loadLabel(mPackageManager) + "/" + imi1.getId();
+                final CharSequence imiId2 = imi2.loadLabel(mPackageManager) + "/" + imi2.getId();
+                return imiId1.toString().compareTo(imiId2.toString());
+            }
+        });
+
+        final int myImsCount = myImsList.size();
+        final int imiCount = imiList.size();
+        final CharSequence[] items = new CharSequence[myImsCount + imiCount];
+
+        int checkedItem = 0;
+        int index = 0;
+        final CharSequence myImiLabel = myImi.loadLabel(mPackageManager);
+        for (int i = 0; i < myImsCount; i++) {
+            InputMethodSubtypeCompatWrapper ims = myImsList.get(i);
+            if (currentIms.equals(ims))
+                checkedItem = index;
+            final CharSequence title = TextUtils.concat(
+                    ims.getDisplayName(mService, mLatinImePackageName, mApplicationInfo),
+                    " (" + myImiLabel, ")");
+            items[index] = title;
+            index++;
+        }
+
+        for (int i = 0; i < imiCount; i++) {
+            final InputMethodInfoCompatWrapper imi = imiList.get(i);
+            final CharSequence title = imi.loadLabel(mPackageManager);
+            items[index] = title;
+            index++;
+        }
+
+        final OnClickListener buttonListener = new OnClickListener() {
+            @Override
+            public void onClick(DialogInterface di, int whichButton) {
+                final Intent intent = new Intent("android.settings.INPUT_METHOD_SETTINGS");
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                mService.startActivity(intent);
+            }
+        };
+        final InputMethodServiceCompatWrapper service = mService;
+        final IBinder token = service.getWindow().getWindow().getAttributes().token;
+        final OnClickListener selectionListener = new OnClickListener() {
+            @Override
+            public void onClick(DialogInterface di, int which) {
+                di.dismiss();
+                if (which < myImsCount) {
+                    final int imsIndex = which;
+                    final InputMethodSubtypeCompatWrapper ims = myImsList.get(imsIndex);
+                    service.notifyOnCurrentInputMethodSubtypeChanged(ims);
+                } else {
+                    final int imiIndex = which - myImsCount;
+                    final InputMethodInfoCompatWrapper imi = imiList.get(imiIndex);
+                    setInputMethodAndSubtype(token, imi.getId(), null);
+                }
+            }
+        };
+
+        final AlertDialog.Builder builder = new AlertDialog.Builder(mService)
+                .setTitle(mService.getString(R.string.selectInputMethod))
+                .setNeutralButton(R.string.configure_input_method, buttonListener)
+                .setSingleChoiceItems(items, checkedItem, selectionListener);
+        mService.showOptionDialogInternal(builder.create());
     }
 }
diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
index 7d8c745..7aab66d 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
@@ -16,10 +16,15 @@
 
 package com.android.inputmethod.compat;
 
+import android.app.AlertDialog;
 import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
+import android.view.Window;
+import android.view.WindowManager;
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.latin.SubtypeSwitcher;
 
 public class InputMethodServiceCompatWrapper extends InputMethodService {
@@ -32,10 +37,33 @@
 
     private InputMethodManagerCompatWrapper mImm;
 
+    // For compatibility of {@link InputMethodManager#showInputMethodPicker}.
+    // TODO: Move this variable back to LatinIME when this compatibility wrapper is removed.
+    protected AlertDialog mOptionsDialog;
+
+    public void showOptionDialogInternal(AlertDialog dialog) {
+        final IBinder windowToken = KeyboardSwitcher.getInstance().getKeyboardView()
+                .getWindowToken();
+        if (windowToken == null) return;
+
+        dialog.setCancelable(true);
+        dialog.setCanceledOnTouchOutside(true);
+
+        final Window window = dialog.getWindow();
+        final WindowManager.LayoutParams lp = window.getAttributes();
+        lp.token = windowToken;
+        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+        window.setAttributes(lp);
+        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+
+        mOptionsDialog = dialog;
+        dialog.show();
+    }
+
     @Override
     public void onCreate() {
         super.onCreate();
-        mImm = InputMethodManagerCompatWrapper.getInstance(this);
+        mImm = InputMethodManagerCompatWrapper.getInstance();
     }
 
     // When the API level is 10 or previous, notifyOnCurrentInputMethodSubtypeChanged should
diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
index 667d86c..b6b86a4 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
@@ -16,13 +16,16 @@
 
 package com.android.inputmethod.compat;
 
-import com.android.inputmethod.latin.LatinImeLogger;
-
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.inputmethod.latin.LatinImeLogger;
+
 import java.lang.reflect.Method;
 import java.util.Arrays;
+import java.util.Locale;
 
 // TODO: Override this class with the concrete implementation if we need to take care of the
 // performance.
@@ -50,6 +53,9 @@
             CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValueOf", String.class);
     private static final Method METHOD_isAuxiliary =
             CompatUtils.getMethod(CLASS_InputMethodSubtype, "isAuxiliary");
+    private static final Method METHOD_getDisplayName =
+            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getDisplayName", Context.class,
+                    String.class, ApplicationInfo.class);
 
     private final int mDummyNameResId;
     private final int mDummyIconResId;
@@ -122,6 +128,28 @@
         return (Boolean)CompatUtils.invoke(mObj, false, METHOD_isAuxiliary);
     }
 
+    public CharSequence getDisplayName(Context context, String packageName,
+            ApplicationInfo appInfo) {
+        if (mObj != null) {
+            return (CharSequence)CompatUtils.invoke(
+                    mObj, "", METHOD_getDisplayName, context, packageName, appInfo);
+        }
+
+        // The code below are based on {@link InputMethodSubtype#getDisplayName}.
+
+        final Locale locale = new Locale(getLocale());
+        final String localeStr = locale.getDisplayName();
+        if (getNameResId() == 0) {
+            return localeStr;
+        }
+        final CharSequence subtypeName = context.getText(getNameResId());
+        if (!TextUtils.isEmpty(localeStr)) {
+            return String.format(subtypeName.toString(), localeStr);
+        } else {
+            return localeStr;
+        }
+    }
+
     public boolean isDummy() {
         return !hasOriginalObject();
     }
diff --git a/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
index 8e2a2e0..a6304d8 100644
--- a/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
@@ -23,7 +23,7 @@
 
 public class VibratorCompatWrapper {
     private static final Method METHOD_hasVibrator = CompatUtils.getMethod(Vibrator.class,
-            "hasVibrator", int.class);
+            "hasVibrator");
 
     private static final VibratorCompatWrapper sInstance = new VibratorCompatWrapper();
     private Vibrator mVibrator;
diff --git a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
index 85993ea..9397483 100644
--- a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
+++ b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
@@ -17,11 +17,11 @@
 package com.android.inputmethod.deprecated;
 
 import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
 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;
@@ -71,7 +71,8 @@
 public class VoiceProxy implements VoiceInput.UiListener {
     private static final VoiceProxy sInstance = new VoiceProxy();
 
-    public static final boolean VOICE_INSTALLED = true;
+    public static final boolean VOICE_INSTALLED =
+            !InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED;
     private static final boolean ENABLE_VOICE_BUTTON = true;
     private static final String PREF_VOICE_MODE = "voice_mode";
     // Whether or not the user has used voice input before (and thus, whether to show the
@@ -125,24 +126,23 @@
     }
 
     private void initInternal(LatinIME service, SharedPreferences prefs, UIHandler h) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         mService = service;
         mHandler = h;
         mMinimumVoiceRecognitionViewHeightPixel = Utils.dipToPixel(
                 Utils.getDipScale(service), RECOGNITIONVIEW_MINIMUM_HEIGHT_DIP);
-        mImm = InputMethodManagerCompatWrapper.getInstance(service);
+        mImm = InputMethodManagerCompatWrapper.getInstance();
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
-        if (VOICE_INSTALLED) {
-            mVoiceInput = new VoiceInput(service, this);
-            mHints = new Hints(service, prefs, new Hints.Display() {
-                @Override
-                public void showHint(int viewResource) {
-                    View view = LayoutInflater.from(mService).inflate(viewResource, null);
-//                    mService.setCandidatesView(view);
-//                    mService.setCandidatesViewShown(true);
-                    mIsShowingHint = true;
-                }
-              });
-        }
+        mVoiceInput = new VoiceInput(service, this);
+        mHints = new Hints(service, prefs, new Hints.Display() {
+            @Override
+            public void showHint(int viewResource) {
+                View view = LayoutInflater.from(mService).inflate(viewResource, null);
+                mIsShowingHint = true;
+            }
+        });
     }
 
     private VoiceProxy() {
@@ -158,7 +158,10 @@
     }
 
     public void flushVoiceInputLogs(boolean configurationChanged) {
-        if (VOICE_INSTALLED && !configurationChanged) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
+        if (!configurationChanged) {
             if (mAfterVoiceInput) {
                 mVoiceInput.flushAllTextModificationCounters();
                 mVoiceInput.logInputEnded();
@@ -170,6 +173,9 @@
 
     public void flushAndLogAllTextModificationCounters(int index, CharSequence suggestion,
             String wordSeparators) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         if (mAfterVoiceInput && mShowingVoiceSuggestions) {
             mVoiceInput.flushAllTextModificationCounters();
             // send this intent AFTER logging any prior aggregated edits.
@@ -298,6 +304,9 @@
     }
 
     public void showPunctuationHintIfNecessary() {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         InputConnection ic = mService.getCurrentInputConnection();
         if (!mImmediatelyAfterVoiceInput && mAfterVoiceInput && ic != null) {
             if (mHints.showPunctuationHintIfNecessary(ic)) {
@@ -308,6 +317,9 @@
     }
 
     public void hideVoiceWindow(boolean configurationChanging) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         if (!configurationChanging) {
             if (mAfterVoiceInput)
                 mVoiceInput.logInputEnded();
@@ -324,6 +336,9 @@
     }
 
     public void setCursorAndSelection(int newSelEnd, int newSelStart) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         if (mAfterVoiceInput) {
             mVoiceInput.setCursorPos(newSelEnd);
             mVoiceInput.setSelectionSpan(newSelEnd - newSelStart);
@@ -382,7 +397,10 @@
     }
 
     public boolean logAndRevertVoiceInput() {
-        if (VOICE_INSTALLED && mVoiceInputHighlighted) {
+        if (!VOICE_INSTALLED) {
+            return false;
+        }
+        if (mVoiceInputHighlighted) {
             mVoiceInput.incrementTextModificationDeleteCount(
                     mVoiceResults.candidates.get(0).toString().length());
             revertVoiceInput();
@@ -393,6 +411,9 @@
     }
 
     public void rememberReplacedWord(CharSequence suggestion,String wordSeparators) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         if (mShowingVoiceSuggestions) {
             // Retain the replaced word in the alternatives array.
             String wordToBeReplaced = EditingUtils.getWordAtCursor(
@@ -419,6 +440,9 @@
      * @return true if an alternative was found, false otherwise.
      */
     public boolean applyVoiceAlternatives(EditingUtils.SelectedWord touching) {
+        if (!VOICE_INSTALLED) {
+            return false;
+        }
         // Search for result in spoken word alternatives
         String selectedWord = touching.mWord.toString().trim();
         if (!mWordToSuggestions.containsKey(selectedWord)) {
@@ -448,6 +472,9 @@
     }
 
     public void handleBackspace() {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         if (mAfterVoiceInput) {
             // Don't log delete if the user is pressing delete at
             // the beginning of the text box (hence not deleting anything)
@@ -462,6 +489,9 @@
     }
 
     public void handleCharacter() {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         commitVoiceInput();
         if (mAfterVoiceInput) {
             // Assume input length is 1. This assumption fails for smiley face insertions.
@@ -470,6 +500,9 @@
     }
 
     public void handleSeparator() {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         commitVoiceInput();
         if (mAfterVoiceInput){
             // Assume input length is 1. This assumption fails for smiley face insertions.
@@ -478,13 +511,19 @@
     }
 
     public void handleClose() {
-        if (VOICE_INSTALLED & mRecognizing) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
+        if (mRecognizing) {
             mVoiceInput.cancel();
         }
     }
 
 
     public void handleVoiceResults(boolean capitalizeFirstWord) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         mAfterVoiceInput = true;
         mImmediatelyAfterVoiceInput = true;
 
@@ -523,6 +562,9 @@
     }
 
     public void switchToRecognitionStatusView(final Configuration configuration) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -567,6 +609,9 @@
     }
 
     private void switchToLastInputMethod() {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         final IBinder token = mService.getWindow().getWindow().getAttributes().token;
         new AsyncTask<Void, Void, Boolean>() {
             @Override
@@ -632,14 +677,15 @@
     }
 
     public void startListening(final boolean swipe, IBinder token) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         // TODO: remove swipe which is no longer used.
-        if (VOICE_INSTALLED) {
-            if (needsToShowWarningDialog()) {
-                // Calls reallyStartListening if user clicks OK, does nothing if user clicks Cancel.
-                showVoiceWarningDialog(swipe, token);
-            } else {
-                reallyStartListening(swipe);
-            }
+        if (needsToShowWarningDialog()) {
+            // Calls reallyStartListening if user clicks OK, does nothing if user clicks Cancel.
+            showVoiceWarningDialog(swipe, token);
+        } else {
+            reallyStartListening(swipe);
         }
     }
 
@@ -659,7 +705,14 @@
                 && SpeechRecognizer.isRecognitionAvailable(mService);
     }
 
+    public static boolean isRecognitionAvailable(Context context) {
+        return SpeechRecognizer.isRecognitionAvailable(context);
+    }
+
     public void loadSettings(EditorInfo attribute, SharedPreferences sp) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         mHasUsedVoiceInput = sp.getBoolean(PREF_HAS_USED_VOICE_INPUT, false);
         mHasUsedVoiceInputUnsupportedLocale =
                 sp.getBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, false);
@@ -667,22 +720,26 @@
         mLocaleSupportedForVoiceInput = SubtypeSwitcher.getInstance().isVoiceSupported(
                 SubtypeSwitcher.getInstance().getInputLocaleStr());
 
-        if (VOICE_INSTALLED) {
-            final String voiceMode = sp.getString(PREF_VOICE_MODE,
-                    mService.getString(R.string.voice_mode_main));
-            mVoiceButtonEnabled = !voiceMode.equals(mService.getString(R.string.voice_mode_off))
-                    && shouldShowVoiceButton(makeFieldContext(), attribute);
-            mVoiceButtonOnPrimary = voiceMode.equals(mService.getString(R.string.voice_mode_main));
-        }
+        final String voiceMode = sp.getString(PREF_VOICE_MODE,
+                mService.getString(R.string.voice_mode_main));
+        mVoiceButtonEnabled = !voiceMode.equals(mService.getString(R.string.voice_mode_off))
+                && shouldShowVoiceButton(makeFieldContext(), attribute);
+        mVoiceButtonOnPrimary = voiceMode.equals(mService.getString(R.string.voice_mode_main));
     }
 
     public void destroy() {
-        if (VOICE_INSTALLED && mVoiceInput != null) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
+        if (mVoiceInput != null) {
             mVoiceInput.destroy();
         }
     }
 
     public void onStartInputView(IBinder keyboardViewToken) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         // If keyboardViewToken is null, keyboardView is not attached but voiceView is attached.
         IBinder windowToken = keyboardViewToken != null ? keyboardViewToken
                 : mVoiceInput.getView().getWindowToken();
@@ -699,12 +756,18 @@
     }
 
     public void onAttachedToWindow() {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         // After onAttachedToWindow, we can show the voice warning dialog. See startListening()
         // above.
         VoiceInputWrapper.getInstance().setVoiceInput(mVoiceInput, mSubtypeSwitcher);
     }
 
     public void onConfigurationChanged(Configuration configuration) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         if (mRecognizing) {
             switchToRecognitionStatusView(configuration);
         }
@@ -712,6 +775,9 @@
 
     @Override
     public void onCancelVoice() {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         if (mRecognizing) {
             if (mSubtypeSwitcher.isVoiceMode()) {
                 // If voice mode is being canceled within LatinIME (i.e. time-out or user
@@ -733,6 +799,9 @@
     @Override
     public void onVoiceResults(List<String> candidates,
             Map<String, List<CharSequence>> alternatives) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         if (!mRecognizing) {
             return;
         }
@@ -748,59 +817,22 @@
                 switcher.getEnabledLanguages());
     }
 
-    private class VoiceResults {
+    // TODO: make this private (proguard issue)
+    public static class VoiceResults {
         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) {
+        private void setVoiceInput(VoiceInput voiceInput, SubtypeSwitcher switcher) {
+            if (!VOICE_INSTALLED) {
+                return;
+            }
             if (mVoiceInput == null && voiceInput != null) {
                 mVoiceInput = voiceInput;
             }
@@ -811,10 +843,16 @@
         }
 
         public void cancel() {
+            if (!VOICE_INSTALLED) {
+                return;
+            }
             if (mVoiceInput != null) mVoiceInput.cancel();
         }
 
         public void reset() {
+            if (!VOICE_INSTALLED) {
+                return;
+            }
             if (mVoiceInput != null) mVoiceInput.reset();
         }
     }
diff --git a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java
index cf6cd0f..e75559e 100644
--- a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java
+++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.deprecated.languageswitcher;
 
-import com.android.inputmethod.keyboard.internal.KeyboardParser;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
 import com.android.inputmethod.latin.DictionaryFactory;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.Settings;
@@ -162,7 +162,7 @@
 
         try {
             final String localeStr = locale.toString();
-            final String[] layoutCountryCodes = KeyboardParser.parseKeyboardLocale(
+            final String[] layoutCountryCodes = KeyboardBuilder.parseKeyboardLocale(
                     this, R.xml.kbd_qwerty).split(",", -1);
             if (!TextUtils.isEmpty(localeStr) && layoutCountryCodes.length > 0) {
                 for (String s : layoutCountryCodes) {
diff --git a/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java
index 5e6c870..f33a462 100644
--- a/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java
+++ b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java
@@ -57,6 +57,7 @@
 
     private static SuggestedWords.Builder getTypedSuggestions(
             Suggest suggest, KeyboardSwitcher keyboardSwitcher, WordComposer word) {
-        return suggest.getSuggestedWordBuilder(keyboardSwitcher.getKeyboardView(), word, null);
+        return suggest.getSuggestedWordBuilder(keyboardSwitcher.getKeyboardView(), word, null,
+                keyboardSwitcher.getLatinKeyboard().getProximityInfo());
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 45bf68c..397b7b1 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -27,13 +27,15 @@
 import com.android.inputmethod.keyboard.internal.KeyStyles;
 import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle;
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.keyboard.internal.KeyboardParser;
-import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder.ParseException;
 import com.android.inputmethod.keyboard.internal.PopupCharactersParser;
 import com.android.inputmethod.keyboard.internal.Row;
 import com.android.inputmethod.latin.R;
 
-import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Class for describing the position and characteristics of a single key in the keyboard.
@@ -49,10 +51,10 @@
     /** Hint label to display on the key in conjunction with the label */
     public final CharSequence mHintLabel;
     /** 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_LEFT_OF_CENTER = 0x08;
+    private final int mLabelOption;
+    private static final int LABEL_OPTION_ALIGN_LEFT = 0x01;
+    private static final int LABEL_OPTION_ALIGN_RIGHT = 0x02;
+    private static final int LABEL_OPTION_ALIGN_LEFT_OF_CENTER = 0x08;
     private static final int LABEL_OPTION_LARGE_LETTER = 0x10;
     private static final int LABEL_OPTION_FONT_NORMAL = 0x20;
     private static final int LABEL_OPTION_FONT_MONO_SPACE = 0x40;
@@ -61,6 +63,8 @@
     private static final int LABEL_OPTION_HAS_POPUP_HINT = 0x200;
     private static final int LABEL_OPTION_HAS_UPPERCASE_LETTER = 0x400;
     private static final int LABEL_OPTION_HAS_HINT_LABEL = 0x800;
+    private static final int LABEL_OPTION_WITH_ICON_LEFT = 0x1000;
+    private static final int LABEL_OPTION_WITH_ICON_RIGHT = 0x2000;
 
     /** Icon to display instead of a label. Icon takes precedence over a label */
     private Drawable mIcon;
@@ -72,7 +76,9 @@
     /** Height of the key, not including the gap */
     public final int mHeight;
     /** The horizontal gap around this key */
-    public final int mGap;
+    public final int mHorizontalGap;
+    /** The vertical gap below this key */
+    public final int mVerticalGap;
     /** The visual insets */
     public final int mVisualInsetsLeft;
     public final int mVisualInsetsRight;
@@ -95,21 +101,20 @@
      * {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT},
      * {@link Keyboard#EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM}.
      */
-    public final int mEdgeFlags;
+    private int mEdgeFlags;
     /** 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;
 
-    /** The Keyboard that this key belongs to */
-    private final Keyboard mKeyboard;
-
     /** The current pressed state of this key */
     private boolean mPressed;
     /** If this is a sticky key, is its highlight on? */
     private boolean mHighlightOn;
     /** Key is enabled and responds on press */
     private boolean mEnabled = true;
+    /** Whether this key needs to show the "..." popup hint for special purposes */
+    private boolean mNeedsSpecialPopupHint;
 
     // keyWidth constants
     private static final int KEYWIDTH_FILL_RIGHT = 0;
@@ -153,16 +158,50 @@
             android.R.attr.state_pressed
     };
 
+    // RTL parenthesis character swapping map.
+    private static final Map<Integer, Integer> sRtlParenthesisMap = new HashMap<Integer, Integer>();
+
+    static {
+        // The all letters need to be mirrored are found at
+        // http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt
+        addRtlParenthesisPair('(', ')');
+        addRtlParenthesisPair('[', ']');
+        addRtlParenthesisPair('{', '}');
+        addRtlParenthesisPair('<', '>');
+        // \u00ab: LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+        // \u00bb: RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+        addRtlParenthesisPair('\u00ab', '\u00bb');
+        // \u2039: SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+        // \u203a: SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+        addRtlParenthesisPair('\u2039', '\u203a');
+        // \u2264: LESS-THAN OR EQUAL TO
+        // \u2265: GREATER-THAN OR EQUAL TO
+        addRtlParenthesisPair('\u2264', '\u2265');
+    }
+
+    private static void addRtlParenthesisPair(int left, int right) {
+        sRtlParenthesisMap.put(left, right);
+        sRtlParenthesisMap.put(right, left);
+    }
+
+    public static int getRtlParenthesisCode(int code) {
+        if (sRtlParenthesisMap.containsKey(code)) {
+            return sRtlParenthesisMap.get(code);
+        } else {
+            return code;
+        }
+    }
+
     /**
      * This constructor is being used only for key in popup mini keyboard.
      */
-    public Key(Resources res, Keyboard keyboard, CharSequence popupCharacter, int x, int y,
+    public Key(Resources res, KeyboardParams params, CharSequence popupCharacter, int x, int y,
             int width, int height, int edgeFlags) {
-        mKeyboard = keyboard;
-        mHeight = height - keyboard.getVerticalGap();
-        mGap = keyboard.getHorizontalGap();
+        mHeight = height - params.mVerticalGap;
+        mHorizontalGap = params.mHorizontalGap;
+        mVerticalGap = params.mVerticalGap;
         mVisualInsetsLeft = mVisualInsetsRight = 0;
-        mWidth = width - mGap;
+        mWidth = width - mHorizontalGap;
         mEdgeFlags = edgeFlags;
         mHintLabel = null;
         mLabelOption = 0;
@@ -174,10 +213,11 @@
         final String popupSpecification = popupCharacter.toString();
         mLabel = PopupCharactersParser.getLabel(popupSpecification);
         mOutputText = PopupCharactersParser.getOutputText(popupSpecification);
-        mCode = PopupCharactersParser.getCode(res, popupSpecification);
-        mIcon = keyboard.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification));
+        final int code = PopupCharactersParser.getCode(res, popupSpecification);
+        mCode = params.mIsRtlKeyboard ? getRtlParenthesisCode(code) : code;
+        mIcon = params.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification));
         // Horizontal gap is divided equally to both sides of the key.
-        mX = x + mGap / 2;
+        mX = x + mHorizontalGap / 2;
         mY = y;
     }
 
@@ -185,30 +225,30 @@
      * Create a key with the given top-left coordinate and extract its attributes from the XML
      * parser.
      * @param res resources associated with the caller's context
-     * @param row the row that this key belongs to. The row must already be attached to
-     * a {@link Keyboard}.
+     * @param params the keyboard building parameters.
+     * @param row the row that this key belongs to.
      * @param x the x coordinate of the top-left
      * @param y the y coordinate of the top-left
      * @param parser the XML parser containing the attributes for this key
      * @param keyStyles active key styles set
      */
-    public Key(Resources res, Row row, int x, int y, XmlResourceParser parser,
-            KeyStyles keyStyles) {
-        mKeyboard = row.getKeyboard();
+    public Key(Resources res, KeyboardParams params, Row row, int x, int y,
+            XmlResourceParser parser, KeyStyles keyStyles) {
 
         final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard);
         int keyWidth;
         try {
-            mHeight = KeyboardParser.getDimensionOrFraction(keyboardAttr,
+            mHeight = KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
                     R.styleable.Keyboard_rowHeight,
-                    mKeyboard.getKeyboardHeight(), row.mDefaultHeight) - row.mVerticalGap;
-            mGap = KeyboardParser.getDimensionOrFraction(keyboardAttr,
+                    params.mHeight, row.mRowHeight) - params.mVerticalGap;
+            mHorizontalGap = KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
                     R.styleable.Keyboard_horizontalGap,
-                    mKeyboard.getDisplayWidth(), row.mDefaultHorizontalGap);
-            keyWidth = KeyboardParser.getDimensionOrFraction(keyboardAttr,
+                    params.mWidth, params.mHorizontalGap);
+            mVerticalGap = params.mVerticalGap;
+            keyWidth = KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
                     R.styleable.Keyboard_keyWidth,
-                    mKeyboard.getDisplayWidth(), row.mDefaultWidth);
+                    params.mWidth, row.mDefaultKeyWidth);
         } finally {
             keyboardAttr.recycle();
         }
@@ -226,8 +266,8 @@
                 style = keyStyles.getEmptyKeyStyle();
             }
 
-            final int keyboardWidth = mKeyboard.getDisplayWidth();
-            int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr,
+            final int keyboardWidth = params.mOccupiedWidth;
+            int keyXPos = KeyboardBuilder.getDimensionOrFraction(keyAttr,
                     R.styleable.Keyboard_Key_keyXPos, keyboardWidth, x);
             if (keyXPos < 0) {
                 // If keyXPos is negative, the actual x-coordinate will be k + keyXPos.
@@ -251,44 +291,52 @@
             }
 
             // Horizontal gap is divided equally to both sides of the key.
-            mX = keyXPos + mGap / 2;
+            mX = keyXPos + mHorizontalGap / 2;
             mY = y;
-            mWidth = keyWidth - mGap;
+            mWidth = keyWidth - mHorizontalGap;
 
-            final CharSequence[] popupCharacters = style.getTextArray(keyAttr,
-                    R.styleable.Keyboard_Key_popupCharacters);
+            CharSequence[] popupCharacters = style.getTextArray(
+                    keyAttr, R.styleable.Keyboard_Key_popupCharacters);
+            if (params.mId.mPasswordInput) {
+                popupCharacters = PopupCharactersParser.filterOut(
+                        res, popupCharacters, PopupCharactersParser.NON_ASCII_FILTER);
+            }
             // In Arabic symbol layouts, we'd like to keep digits in popup characters regardless of
             // config_digit_popup_characters_enabled.
-            if (mKeyboard.mId.isAlphabetKeyboard() && !res.getBoolean(
+            if (params.mId.isAlphabetKeyboard() && !res.getBoolean(
                     R.bool.config_digit_popup_characters_enabled)) {
-                mPopupCharacters = filterOutDigitPopupCharacters(popupCharacters);
+                mPopupCharacters = PopupCharactersParser.filterOut(
+                        res, popupCharacters, PopupCharactersParser.DIGIT_FILTER);
             } else {
                 mPopupCharacters = popupCharacters;
             }
             mMaxPopupColumn = style.getInt(keyboardAttr,
                     R.styleable.Keyboard_Key_maxPopupKeyboardColumn,
-                    mKeyboard.getMaxPopupKeyboardColumn());
+                    params.mMaxPopupColumn);
 
             mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, 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;
+            mEdgeFlags = 0;
 
-            final KeyboardIconsSet iconsSet = mKeyboard.mIconsSet;
-            mVisualInsetsLeft = KeyboardParser.getDimensionOrFraction(keyAttr,
+            final KeyboardIconsSet iconsSet = params.mIconsSet;
+            mVisualInsetsLeft = KeyboardBuilder.getDimensionOrFraction(keyAttr,
                     R.styleable.Keyboard_Key_visualInsetsLeft, keyboardWidth, 0);
-            mVisualInsetsRight = KeyboardParser.getDimensionOrFraction(keyAttr,
+            mVisualInsetsRight = KeyboardBuilder.getDimensionOrFraction(keyAttr,
                     R.styleable.Keyboard_Key_visualInsetsRight, keyboardWidth, 0);
             mPreviewIcon = iconsSet.getIcon(style.getInt(
                     keyAttr, R.styleable.Keyboard_Key_keyIconPreview,
                     KeyboardIconsSet.ICON_UNDEFINED));
-            Keyboard.setDefaultBounds(mPreviewIcon);
             mIcon = iconsSet.getIcon(style.getInt(
                     keyAttr, R.styleable.Keyboard_Key_keyIcon,
                     KeyboardIconsSet.ICON_UNDEFINED));
-            Keyboard.setDefaultBounds(mIcon);
+            final int shiftedIconId = style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIconShifted,
+                    KeyboardIconsSet.ICON_UNDEFINED);
+            if (shiftedIconId != KeyboardIconsSet.ICON_UNDEFINED) {
+                final Drawable shiftedIcon = iconsSet.getIcon(shiftedIconId);
+                params.addShiftedIcon(this, shiftedIcon);
+            }
             mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
 
             mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel);
@@ -299,25 +347,20 @@
             final int code = style.getInt(keyAttr, R.styleable.Keyboard_Key_code,
                     Keyboard.CODE_UNSPECIFIED);
             if (code == Keyboard.CODE_UNSPECIFIED && !TextUtils.isEmpty(mLabel)) {
-                mCode = mLabel.charAt(0);
+                final int firstChar = mLabel.charAt(0);
+                mCode = params.mIsRtlKeyboard ? getRtlParenthesisCode(firstChar) : firstChar;
             } else if (code != Keyboard.CODE_UNSPECIFIED) {
                 mCode = code;
             } else {
                 mCode = Keyboard.CODE_DUMMY;
             }
-
-            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 {
             keyAttr.recycle();
         }
     }
 
-    public CharSequence getCaseAdjustedLabel() {
-        return mKeyboard.adjustLabelCase(mLabel);
+    public void addEdgeFlags(int flags) {
+        mEdgeFlags |= flags;
     }
 
     public Typeface selectTypeface(Typeface defaultTypeface) {
@@ -345,10 +388,30 @@
         }
     }
 
+    public boolean isAlignLeft() {
+        return (mLabelOption & LABEL_OPTION_ALIGN_LEFT) != 0;
+    }
+
+    public boolean isAlignRight() {
+        return (mLabelOption & LABEL_OPTION_ALIGN_RIGHT) != 0;
+    }
+
+    public boolean isAlignLeftOfCenter() {
+        return (mLabelOption & LABEL_OPTION_ALIGN_LEFT_OF_CENTER) != 0;
+    }
+
     public boolean hasPopupHint() {
         return (mLabelOption & LABEL_OPTION_HAS_POPUP_HINT) != 0;
     }
 
+    public void setNeedsSpecialPopupHint(boolean needsSpecialPopupHint) {
+        mNeedsSpecialPopupHint = needsSpecialPopupHint;
+    }
+
+    public boolean needsSpecialPopupHint() {
+        return mNeedsSpecialPopupHint;
+    }
+
     public boolean hasUppercaseLetter() {
         return (mLabelOption & LABEL_OPTION_HAS_UPPERCASE_LETTER) != 0;
     }
@@ -357,34 +420,12 @@
         return (mLabelOption & LABEL_OPTION_HAS_HINT_LABEL) != 0;
     }
 
-    private static boolean isDigitPopupCharacter(CharSequence label) {
-        return label != null && label.length() == 1 && Character.isDigit(label.charAt(0));
+    public boolean hasLabelWithIconLeft() {
+        return (mLabelOption & LABEL_OPTION_WITH_ICON_LEFT) != 0;
     }
 
-    private static CharSequence[] filterOutDigitPopupCharacters(CharSequence[] popupCharacters) {
-        if (popupCharacters == null || popupCharacters.length < 1)
-            return null;
-        if (popupCharacters.length == 1 && isDigitPopupCharacter(
-                PopupCharactersParser.getLabel(popupCharacters[0].toString())))
-            return null;
-        ArrayList<CharSequence> filtered = null;
-        for (int i = 0; i < popupCharacters.length; i++) {
-            final CharSequence popupSpec = popupCharacters[i];
-            if (isDigitPopupCharacter(PopupCharactersParser.getLabel(popupSpec.toString()))) {
-                if (filtered == null) {
-                    filtered = new ArrayList<CharSequence>();
-                    for (int j = 0; j < i; j++)
-                        filtered.add(popupCharacters[j]);
-                }
-            } else if (filtered != null) {
-                filtered.add(popupSpec);
-            }
-        }
-        if (filtered == null)
-            return popupCharacters;
-        if (filtered.size() == 0)
-            return null;
-        return filtered.toArray(new CharSequence[filtered.size()]);
+    public boolean hasLabelWithIconRight() {
+        return (mLabelOption & LABEL_OPTION_WITH_ICON_RIGHT) != 0;
     }
 
     public Drawable getIcon() {
@@ -441,15 +482,18 @@
      * assume that all points between the key and the edge are considered to be on the key.
      */
     public boolean isOnKey(int x, int y) {
+        final int left = mX - mHorizontalGap / 2;
+        final int right = left + mWidth + mHorizontalGap;
+        final int top = mY;
+        final int bottom = top + mHeight + mVerticalGap;
         final int flags = mEdgeFlags;
+        if (flags == 0) {
+            return x >= left && x <= right && y >= top && y <= bottom;
+        }
         final boolean leftEdge = (flags & Keyboard.EDGE_LEFT) != 0;
         final boolean rightEdge = (flags & Keyboard.EDGE_RIGHT) != 0;
         final boolean topEdge = (flags & Keyboard.EDGE_TOP) != 0;
         final boolean bottomEdge = (flags & Keyboard.EDGE_BOTTOM) != 0;
-        final int left = mX - mGap / 2;
-        final int right = left + mWidth + mGap;
-        final int top = mY;
-        final int bottom = top + mHeight + mKeyboard.getVerticalGap();
         // In order to mitigate rounding errors, we use (left <= x <= right) here.
         return (x >= left || leftEdge) && (x <= right || rightEdge)
                 && (y >= top || topEdge) && (y <= bottom || bottomEdge);
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 6d25025..0a3acb4 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -57,7 +57,7 @@
         mCorrectionX = (int)correctionX;
         mCorrectionY = (int)correctionY;
         mKeyboard = keyboard;
-        final int threshold = keyboard.getMostCommonKeyWidth();
+        final int threshold = keyboard.mMostCommonKeyWidth;
         mProximityThresholdSquare = threshold * threshold;
     }
 
@@ -153,7 +153,7 @@
     }
 
     private void getNearbyKeyCodes(final int[] allCodes) {
-        final List<Key> keys = getKeyboard().getKeys();
+        final List<Key> keys = getKeyboard().mKeys;
         final int[] indices = mIndices;
 
         // allCodes[0] should always have the key code even if it is a non-letter key.
@@ -187,7 +187,7 @@
      * @return The nearest key index
      */
     public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
-        final List<Key> keys = getKeyboard().getKeys();
+        final List<Key> keys = getKeyboard().mKeys;
         final int touchX = getTouchX(x);
         final int touchY = getTouchY(y);
 
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 3e45793..f8e08b0 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -16,25 +16,17 @@
 
 package com.android.inputmethod.keyboard;
 
-import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.keyboard.internal.KeyboardParser;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
 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;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
@@ -55,8 +47,6 @@
  * </pre>
  */
 public class Keyboard {
-    private static final String TAG = Keyboard.class.getSimpleName();
-
     public static final int EDGE_LEFT = 0x01;
     public static final int EDGE_RIGHT = 0x02;
     public static final int EDGE_TOP = 0x04;
@@ -77,6 +67,8 @@
     public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
     public static final int CODE_CLOSING_CURLY_BRACKET = '}';
     public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
+    public static final int CODE_DIGIT0 = '0';
+    public static final int CODE_PLUS = '+';
 
 
     /** Special keys code.  These should be aligned with values/keycodes.xml */
@@ -87,216 +79,94 @@
     public static final int CODE_CANCEL = -4;
     public static final int CODE_DELETE = -5;
     public static final int CODE_SETTINGS = -6;
-    public static final int CODE_SETTINGS_LONGPRESS = -7;
-    public static final int CODE_SHORTCUT = -8;
+    public static final int CODE_SHORTCUT = -7;
     // Code value representing the code is not specified.
     public static final int CODE_UNSPECIFIED = -99;
 
-    /** Horizontal gap default for all rows */
-    private int mDefaultHorizontalGap;
-
-    /** Default key width */
-    private int mDefaultWidth;
-
-    /** Default key height */
-    private int mDefaultHeight;
-
-    /** Default gap between rows */
-    private int mDefaultVerticalGap;
-
-    /** Popup keyboard template */
-    private int mPopupKeyboardResId;
-
-    /** Maximum column for popup keyboard */
-    private int mMaxPopupColumn;
-
-    /** List of shift keys in this keyboard and its icons and state */
-    private final List<Key> mShiftKeys = new ArrayList<Key>();
-    private final HashMap<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>();
-    private final HashMap<Key, Drawable> mNormalShiftIcons = new HashMap<Key, Drawable>();
-    private final HashSet<Key> mShiftLockEnabled = new HashSet<Key>();
-    private final KeyboardShiftState mShiftState = new KeyboardShiftState();
-
-    /** Total height of the keyboard, including the padding and keys */
-    private int mTotalHeight;
-
-    /**
-     * Total width (minimum width) of the keyboard, including left side gaps and keys, but not any
-     * gaps on the right side.
-     */
-    private int mMinWidth;
-
-    /** List of keys in this keyboard */
-    private final List<Key> mKeys = new ArrayList<Key>();
-
-    /** Width of the screen available to fit the keyboard */
-    private final int mDisplayWidth;
-
-    /** Height of the screen */
-    private final int mDisplayHeight;
-
-    /** Height of keyboard */
-    private int mKeyboardHeight;
-
-    private int mMostCommonKeyWidth = 0;
-
     public final KeyboardId mId;
 
-    public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
+    /** Total height of the keyboard, including the padding and keys */
+    public final int mOccupiedHeight;
+    /** Total width of the keyboard, including the padding and keys */
+    public final int mOccupiedWidth;
 
-    // Variables for pre-computing nearest keys.
+    public final int mHeight;
+    public final int mWidth;
 
-    // TODO: Change GRID_WIDTH and GRID_HEIGHT to private.
-    public final int GRID_WIDTH;
-    public final int GRID_HEIGHT;
+    /** Default row height */
+    public final int mDefaultRowHeight;
+
+    /** Default gap between rows */
+    public final int mVerticalGap;
+
+    public final int mMostCommonKeyWidth;
+
+    /** Popup keyboard template */
+    public final int mPopupKeyboardResId;
+
+    /** Maximum column for popup keyboard */
+    public final int mMaxPopupColumn;
+
+    /** True if Right-To-Left keyboard */
+    public final boolean mIsRtlKeyboard;
+
+    /** List of keys and icons in this keyboard */
+    public final List<Key> mKeys;
+    public final List<Key> mShiftKeys;
+    public final Set<Key> mShiftLockKeys;
+    public final Map<Key, Drawable> mShiftedIcons;
+    public final Map<Key, Drawable> mUnshiftedIcons;
+    public final KeyboardIconsSet mIconsSet;
+
+    private final KeyboardShiftState mShiftState = new KeyboardShiftState();
 
     private final ProximityInfo mProximityInfo;
 
-    /**
-     * Creates a keyboard from the given xml key layout file.
-     * @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(KeyboardParams params) {
+        mId = params.mId;
+        mOccupiedHeight = params.mOccupiedHeight;
+        mOccupiedWidth = params.mOccupiedWidth;
+        mHeight = params.mHeight;
+        mWidth = params.mWidth;
+        mMostCommonKeyWidth = params.mMostCommonKeyWidth;
+        mIsRtlKeyboard = params.mIsRtlKeyboard;
+        mPopupKeyboardResId = params.mPopupKeyboardResId;
+        mMaxPopupColumn = params.mMaxPopupColumn;
 
-    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);
+        mDefaultRowHeight = params.mDefaultRowHeight;
+        mVerticalGap = params.mVerticalGap;
 
-        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;
+        mKeys = Collections.unmodifiableList(params.mKeys);
+        mShiftKeys = Collections.unmodifiableList(params.mShiftKeys);
+        mShiftLockKeys = Collections.unmodifiableSet(params.mShiftLockKeys);
+        mShiftedIcons = Collections.unmodifiableMap(params.mShiftedIcons);
+        mUnshiftedIcons = Collections.unmodifiableMap(params.mUnshiftedIcons);
+        mIconsSet = params.mIconsSet;
 
-        mDefaultHorizontalGap = 0;
-        setKeyWidth(mDisplayWidth / 10);
-        mDefaultVerticalGap = 0;
-        mDefaultHeight = mDefaultWidth;
-        mId = id;
-        loadKeyboard(context, xmlLayoutResId);
         mProximityInfo = new ProximityInfo(
-                GRID_WIDTH, GRID_HEIGHT, getMinWidth(), getHeight(), getKeyWidth(), mKeys);
+                params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
+                mMostCommonKeyWidth, mKeys);
     }
 
-    public int getProximityInfo() {
-        return mProximityInfo.getNativeProximityInfo();
+    public ProximityInfo getProximityInfo() {
+        return mProximityInfo;
     }
 
-    public List<Key> getKeys() {
-        return mKeys;
-    }
-
-    public int getHorizontalGap() {
-        return mDefaultHorizontalGap;
-    }
-
-    public void setHorizontalGap(int gap) {
-        mDefaultHorizontalGap = gap;
-    }
-
-    public int getVerticalGap() {
-        return mDefaultVerticalGap;
-    }
-
-    public void setVerticalGap(int gap) {
-        mDefaultVerticalGap = gap;
-    }
-
-    public int getRowHeight() {
-        return mDefaultHeight;
-    }
-
-    public void setRowHeight(int height) {
-        mDefaultHeight = height;
-    }
-
-    public int getKeyWidth() {
-        return mDefaultWidth;
-    }
-
-    public void setKeyWidth(int width) {
-        mDefaultWidth = width;
-    }
-
-    /**
-     * Returns the total height of the keyboard
-     * @return the total height of the keyboard
-     */
-    public int getHeight() {
-        return mTotalHeight;
-    }
-
-    public void setHeight(int height) {
-        mTotalHeight = height;
-    }
-
-    public int getMinWidth() {
-        return mMinWidth;
-    }
-
-    public void setMinWidth(int minWidth) {
-        mMinWidth = minWidth;
-    }
-
-    public int getDisplayHeight() {
-        return mDisplayHeight;
-    }
-
-    public int getDisplayWidth() {
-        return mDisplayWidth;
-    }
-
-    public int getKeyboardHeight() {
-        return mKeyboardHeight;
-    }
-
-    public void setKeyboardHeight(int height) {
-        mKeyboardHeight = height;
-    }
-
-    public int getPopupKeyboardResId() {
-        return mPopupKeyboardResId;
-    }
-
-    public void setPopupKeyboardResId(int resId) {
-        mPopupKeyboardResId = resId;
-    }
-
-    public int getMaxPopupKeyboardColumn() {
-        return mMaxPopupColumn;
-    }
-
-    public void setMaxPopupKeyboardColumn(int column) {
-        mMaxPopupColumn = column;
-    }
-
-    public List<Key> getShiftKeys() {
-        return mShiftKeys;
-    }
-
-    public Map<Key, Drawable> getShiftedIcons() {
-        return mShiftedIcons;
-    }
-
-    public void enableShiftLock() {
-        for (final Key key : getShiftKeys()) {
-            mShiftLockEnabled.add(key);
-            mNormalShiftIcons.put(key, key.getIcon());
-        }
-    }
-
-    public boolean isShiftLockEnabled(Key key) {
-        return mShiftLockEnabled.contains(key);
+    public boolean hasShiftLockKey() {
+        return !mShiftLockKeys.isEmpty();
     }
 
     public boolean setShiftLocked(boolean newShiftLockState) {
-        final Map<Key, Drawable> shiftedIcons = getShiftedIcons();
-        for (final Key key : getShiftKeys()) {
+        for (final Key key : mShiftLockKeys) {
+            // To represent "shift locked" state. The highlight is handled by background image that
+            // might be a StateListDrawable.
             key.setHighlightOn(newShiftLockState);
-            key.setIcon(newShiftLockState ? shiftedIcons.get(key) : mNormalShiftIcons.get(key));
+            // To represent "shifted" state. The key might have a shifted icon.
+            if (newShiftLockState && mShiftedIcons.containsKey(key)) {
+                key.setIcon(mShiftedIcons.get(key));
+            } else {
+                key.setIcon(mUnshiftedIcons.get(key));
+            }
         }
         mShiftState.setShiftLocked(newShiftLockState);
         return true;
@@ -307,12 +177,11 @@
     }
 
     public boolean setShifted(boolean newShiftState) {
-        final Map<Key, Drawable> shiftedIcons = getShiftedIcons();
-        for (final Key key : getShiftKeys()) {
+        for (final Key key : mShiftKeys) {
             if (!newShiftState && !mShiftState.isShiftLocked()) {
-                key.setIcon(mNormalShiftIcons.get(key));
+                key.setIcon(mUnshiftedIcons.get(key));
             } else if (newShiftState && !mShiftState.isShiftedOrShiftLocked()) {
-                key.setIcon(shiftedIcons.get(key));
+                key.setIcon(mShiftedIcons.get(key));
             }
         }
         return mShiftState.setShifted(newShiftState);
@@ -373,61 +242,4 @@
     public int[] getNearestKeys(int x, int y) {
         return mProximityInfo.getNearestKeys(x, y);
     }
-
-    /**
-     * 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);
-            parser.parseKeyboard(xmlLayoutResId);
-            // mMinWidth is the width of this keyboard which is maximum width of row.
-            mMinWidth = parser.getMaxRowWidth();
-            mTotalHeight = parser.getTotalHeight();
-        } catch (XmlPullParserException e) {
-            Log.w(TAG, "keyboard XML parse error: " + e);
-            throw new IllegalArgumentException(e);
-        } catch (IOException e) {
-            Log.w(TAG, "keyboard XML parse error: " + e);
-            throw new RuntimeException(e);
-        }
-    }
-
-    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/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
index 905f779..8640912 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
@@ -70,4 +70,10 @@
      * Called when user released a finger outside any key.
      */
     public void onCancelInput();
+
+    /**
+     * Send a non-"code input" custom request to the listener.
+     * @return true if the request has been consumed, false otherwise.
+     */
+    public boolean onCustomRequest(int requestCode);
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index b2600dd..d0a2f86 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -42,8 +42,6 @@
     public static final int F2KEY_MODE_SHORTCUT_IME = 2;
     public static final int F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS = 3;
 
-    private static final int MINI_KEYBOARD_ID_MARKER = -1;
-
     public final Locale mLocale;
     public final int mOrientation;
     public final int mWidth;
@@ -55,10 +53,9 @@
     public final boolean mHasSettingsKey;
     public final int mF2KeyMode;
     public final boolean mClobberSettingsKey;
-    public final boolean mVoiceKeyEnabled;
-    public final boolean mHasVoiceKey;
+    public final boolean mShortcutKeyEnabled;
+    public final boolean mHasShortcutKey;
     public final int mImeAction;
-    public final boolean mEnableShiftLock;
 
     public final String mXmlName;
     public final EditorInfo mAttribute;
@@ -67,8 +64,7 @@
 
     public KeyboardId(String xmlName, int xmlId, Locale locale, int orientation, int width,
             int mode, EditorInfo attribute, boolean hasSettingsKey, int f2KeyMode,
-            boolean clobberSettingsKey, boolean voiceKeyEnabled, boolean hasVoiceKey,
-            boolean enableShiftLock) {
+            boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) {
         final int inputType = (attribute != null) ? attribute.inputType : 0;
         final int imeOptions = (attribute != null) ? attribute.imeOptions : 0;
         this.mLocale = locale;
@@ -85,13 +81,12 @@
         this.mHasSettingsKey = hasSettingsKey;
         this.mF2KeyMode = f2KeyMode;
         this.mClobberSettingsKey = clobberSettingsKey;
-        this.mVoiceKeyEnabled = voiceKeyEnabled;
-        this.mHasVoiceKey = hasVoiceKey;
+        this.mShortcutKeyEnabled = shortcutKeyEnabled;
+        this.mHasShortcutKey = hasShortcutKey;
         // We are interested only in {@link EditorInfo#IME_MASK_ACTION} enum value and
         // {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION}.
         this.mImeAction = imeOptions & (
                 EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION);
-        this.mEnableShiftLock = enableShiftLock;
 
         this.mXmlName = xmlName;
         this.mAttribute = attribute;
@@ -107,40 +102,29 @@
                 hasSettingsKey,
                 f2KeyMode,
                 clobberSettingsKey,
-                voiceKeyEnabled,
-                hasVoiceKey,
+                shortcutKeyEnabled,
+                hasShortcutKey,
                 mImeAction,
-                enableShiftLock,
         });
     }
 
-    public KeyboardId cloneAsMiniKeyboard() {
-        return new KeyboardId("mini popup keyboard", MINI_KEYBOARD_ID_MARKER, mLocale, mOrientation,
-                mWidth, mMode, mAttribute, false, F2KEY_MODE_NONE, false, false, false, false);
-    }
-
-    public KeyboardId cloneWithNewLayout(String xmlName, int xmlId) {
+    public KeyboardId cloneWithNewXml(String xmlName, int xmlId) {
         return new KeyboardId(xmlName, xmlId, mLocale, mOrientation, mWidth, mMode, mAttribute,
-                mHasSettingsKey, mF2KeyMode, mClobberSettingsKey, mVoiceKeyEnabled, mHasVoiceKey,
-                mEnableShiftLock);
+                false, F2KEY_MODE_NONE, false, false, false);
     }
 
-    public KeyboardId cloneWithNewGeometry(int width) {
+    public KeyboardId cloneWithNewGeometry(int orientation, int width) {
         if (mWidth == width)
             return this;
-        return new KeyboardId(mXmlName, mXmlId, mLocale, mOrientation, width, mMode, mAttribute,
-                mHasSettingsKey, mF2KeyMode, mClobberSettingsKey, mVoiceKeyEnabled, mHasVoiceKey,
-                mEnableShiftLock);
+        return new KeyboardId(mXmlName, mXmlId, mLocale, orientation, width, mMode, mAttribute,
+                mHasSettingsKey, mF2KeyMode, mClobberSettingsKey, mShortcutKeyEnabled,
+                mHasShortcutKey);
     }
 
     public int getXmlId() {
         return mXmlId;
     }
 
-    public boolean isMiniKeyboard() {
-        return mXmlId == MINI_KEYBOARD_ID_MARKER;
-    }
-
     public boolean isAlphabetKeyboard() {
         return mXmlId == R.xml.kbd_qwerty;
     }
@@ -153,8 +137,8 @@
         return mMode == MODE_PHONE;
     }
 
-    public boolean isPhoneSymbolsKeyboard() {
-        return mXmlId == R.xml.kbd_phone_symbols;
+    public boolean isPhoneShiftKeyboard() {
+        return mXmlId == R.xml.kbd_phone_shift;
     }
 
     public boolean isNumberKeyboard() {
@@ -166,7 +150,7 @@
         return other instanceof KeyboardId && equals((KeyboardId) other);
     }
 
-    boolean equals(KeyboardId other) {
+    private boolean equals(KeyboardId other) {
         return other.mLocale.equals(this.mLocale)
             && other.mOrientation == this.mOrientation
             && other.mWidth == this.mWidth
@@ -177,10 +161,9 @@
             && other.mHasSettingsKey == this.mHasSettingsKey
             && other.mF2KeyMode == this.mF2KeyMode
             && other.mClobberSettingsKey == this.mClobberSettingsKey
-            && other.mVoiceKeyEnabled == this.mVoiceKeyEnabled
-            && other.mHasVoiceKey == this.mHasVoiceKey
-            && other.mImeAction == this.mImeAction
-            && other.mEnableShiftLock == this.mEnableShiftLock;
+            && other.mShortcutKeyEnabled == this.mShortcutKeyEnabled
+            && other.mHasShortcutKey == this.mHasShortcutKey
+            && other.mImeAction == this.mImeAction;
     }
 
     @Override
@@ -190,7 +173,7 @@
 
     @Override
     public String toString() {
-        return String.format("[%s.xml %s %s%d %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%s]",
                 mXmlName,
                 mLocale,
                 (mOrientation == 1 ? "port" : "land"), mWidth,
@@ -201,9 +184,8 @@
                 (mNavigateAction ? " navigateAction" : ""),
                 (mPasswordInput ? " passwordInput" : ""),
                 (mHasSettingsKey ? " hasSettingsKey" : ""),
-                (mVoiceKeyEnabled ? " voiceKeyEnabled" : ""),
-                (mHasVoiceKey ? " hasVoiceKey" : ""),
-                (mEnableShiftLock ? " enableShiftLock" : "")
+                (mShortcutKeyEnabled ? " shortcutKeyEnabled" : ""),
+                (mHasShortcutKey ? " hasShortcutKey" : "")
         );
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index bb21d7a..811470c 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -18,7 +18,9 @@
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.inputmethodservice.InputMethodService;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.InflateException;
@@ -27,7 +29,6 @@
 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;
@@ -38,6 +39,7 @@
 import com.android.inputmethod.latin.Utils;
 
 import java.lang.ref.SoftReference;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Locale;
 
@@ -62,25 +64,29 @@
     private View mCurrentInputView;
     private LatinKeyboardView mKeyboardView;
     private LatinIME mInputMethodService;
+    private String mPackageName;
+    private Resources mResources;
 
     // TODO: Combine these key state objects with auto mode switch state.
     private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
     private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol");
 
-    private KeyboardId mSymbolsId;
-    private KeyboardId mSymbolsShiftedId;
+    private KeyboardId mMainKeyboardId;
+    private KeyboardId mSymbolsKeyboardId;
+    private KeyboardId mSymbolsShiftedKeyboardId;
 
     private KeyboardId mCurrentId;
     private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache =
             new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
+    // TODO: Remove this cache object when {@link DisplayMetrics} has actual window width excluding
+    // system navigation bar.
+    private WindowWidthCache mWindowWidthCache;
 
-    private EditorInfo mAttribute;
-    private boolean mIsSymbols;
+    private KeyboardLayoutState mSavedKeyboardState = new KeyboardLayoutState();
+
     /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
      * what user actually typed. */
     private boolean mIsAutoCorrectionActive;
-    private boolean mVoiceKeyEnabled;
-    private boolean mVoiceButtonOnPrimary;
 
     // TODO: Encapsulate these state handling to separate class and combine with ShiftKeyState
     // and ModifierKeyState.
@@ -94,22 +100,135 @@
     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;
-    private static final int SETTINGS_KEY_MODE_AUTO = R.string.settings_key_mode_auto;
-    private static final int SETTINGS_KEY_MODE_ALWAYS_SHOW =
-            R.string.settings_key_mode_always_show;
-    // NOTE: No need to have SETTINGS_KEY_MODE_ALWAYS_HIDE here because it's not being referred to
-    // in the source code now.
-    // Default is SETTINGS_KEY_MODE_AUTO.
-    private static final int DEFAULT_SETTINGS_KEY_MODE = SETTINGS_KEY_MODE_AUTO;
-
     private int mThemeIndex = -1;
     private Context mThemeContext;
-    private int mKeyboardWidth;
 
     private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
 
+    private static class WindowWidthCache {
+        private final InputMethodService mService;
+        private final Resources mResources;
+        private final boolean mIsRegistered[] = new boolean[Configuration.ORIENTATION_SQUARE + 1];
+        private final int mWidth[] = new int[Configuration.ORIENTATION_SQUARE + 1];
+
+        public WindowWidthCache(InputMethodService service) {
+            mService = service;
+            mResources = service.getResources();
+
+            Arrays.fill(mIsRegistered, false);
+            Arrays.fill(mWidth, 0);
+        }
+
+        private int getCurrentWindowWidth() {
+            return mService.getWindow().getWindow().getDecorView().getWidth();
+        }
+
+        public int getWidth(Configuration conf) {
+            final int orientation = conf.orientation;
+            try {
+                final int width = mWidth[orientation];
+                if (mIsRegistered[orientation] || width > 0) {
+                    // Return registered or cached window width for this orientation.
+                    return width;
+                }
+                // Fall through
+            } catch (IndexOutOfBoundsException e) {
+                Log.w(TAG, "unknwon orientation value " + orientation);
+                // Fall through
+            }
+
+            // Return screen width as default window width.
+            return mResources.getDisplayMetrics().widthPixels;
+        }
+
+        public int getWidthOnSizeChanged(Configuration conf) {
+            final int orientation = conf.orientation;
+            try {
+                if (mIsRegistered[orientation]) {
+                    // Return registered window width for this orientation.
+                    return mWidth[orientation];
+                }
+
+                // Cache the current window width without registering.
+                final int width = getCurrentWindowWidth();
+                mWidth[orientation] = width;
+                return width;
+            } catch (IndexOutOfBoundsException e) {
+                Log.w(TAG, "unknwon orientation value " + orientation);
+                return 0;
+            }
+        }
+
+        public void registerWidth() {
+            final int orientation = mResources.getConfiguration().orientation;
+            try {
+                if (!mIsRegistered[orientation]) {
+                    final int width = getCurrentWindowWidth();
+                    if (width > 0) {
+                        // Register current window width.
+                        mWidth[orientation] = width;
+                        mIsRegistered[orientation] = true;
+                    }
+                }
+            } catch (IndexOutOfBoundsException e) {
+                Log.w(TAG, "unknwon orientation value " + orientation);
+            }
+        }
+    }
+
+    public class KeyboardLayoutState {
+        private boolean mIsValid;
+        private boolean mIsAlphabetMode;
+        private boolean mIsShiftLocked;
+        private boolean mIsShifted;
+
+        public boolean isValid() {
+            return mIsValid;
+        }
+
+        public void save() {
+            if (mCurrentId == null) {
+                return;
+            }
+            mIsAlphabetMode = isAlphabetMode();
+            if (mIsAlphabetMode) {
+                mIsShiftLocked = isShiftLocked();
+                mIsShifted = !mIsShiftLocked && isShiftedOrShiftLocked();
+            } else {
+                mIsShiftLocked = false;
+                mIsShifted = mCurrentId.equals(mSymbolsShiftedKeyboardId);
+            }
+            mIsValid = true;
+        }
+
+        public KeyboardId getKeyboardId() {
+            if (!mIsValid) return mMainKeyboardId;
+
+            if (mIsAlphabetMode) {
+                return mMainKeyboardId;
+            } else {
+                return mIsShifted ? mSymbolsShiftedKeyboardId : mSymbolsKeyboardId;
+            }
+        }
+
+        public void restore() {
+            if (!mIsValid) return;
+            mIsValid = false;
+
+            if (mIsAlphabetMode) {
+                final boolean isAlphabetMode = isAlphabetMode();
+                final boolean isShiftLocked = isAlphabetMode && isShiftLocked();
+                final boolean isShifted = !isShiftLocked && isShiftedOrShiftLocked();
+                if (mIsShiftLocked != isShiftLocked) {
+                    toggleCapsLock();
+                } else if (mIsShifted != isShifted) {
+                    onPressShift(false);
+                    onReleaseShift(false);
+                }
+            }
+        }
+    }
+
     public static KeyboardSwitcher getInstance() {
         return sInstance;
     }
@@ -119,11 +238,18 @@
     }
 
     public static void init(LatinIME ims, SharedPreferences prefs) {
-        sInstance.mInputMethodService = ims;
-        sInstance.mPrefs = prefs;
-        sInstance.mSubtypeSwitcher = SubtypeSwitcher.getInstance();
-        sInstance.setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs));
-        prefs.registerOnSharedPreferenceChangeListener(sInstance);
+        sInstance.initInternal(ims, prefs);
+    }
+
+    private void initInternal(LatinIME ims, SharedPreferences prefs) {
+        mInputMethodService = ims;
+        mPackageName = ims.getPackageName();
+        mResources = ims.getResources();
+        mPrefs = prefs;
+        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
+        mWindowWidthCache = new WindowWidthCache(ims);
+        setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs));
+        prefs.registerOnSharedPreferenceChangeListener(this);
     }
 
     private static int getKeyboardThemeIndex(Context context, SharedPreferences prefs) {
@@ -148,162 +274,153 @@
         }
     }
 
-    public void loadKeyboard(EditorInfo attribute, boolean voiceKeyEnabled,
-            boolean voiceButtonOnPrimary) {
-        mSwitchState = SWITCH_STATE_ALPHA;
+    public void loadKeyboard(EditorInfo editorInfo, Settings.Values settingsValues) {
         try {
-            final boolean isSymbols = (mCurrentId != null) ? mCurrentId.isSymbolsKeyboard() : false;
-            loadKeyboardInternal(attribute, voiceKeyEnabled, voiceButtonOnPrimary, isSymbols);
+            mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues);
+            mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues);
+            mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues);
+            setKeyboard(getKeyboard(mSavedKeyboardState.getKeyboardId()));
+            updateShiftState();
         } catch (RuntimeException e) {
-            // Get KeyboardId to record which keyboard has been failed to load.
-            final KeyboardId id = getKeyboardId(attribute, false);
-            Log.w(TAG, "loading keyboard failed: " + id, e);
-            LatinImeLogger.logOnException(id.toString(), e);
+            Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e);
+            LatinImeLogger.logOnException(mMainKeyboardId.toString(), e);
         }
     }
 
-    private void loadKeyboardInternal(EditorInfo attribute, boolean voiceButtonEnabled,
-            boolean voiceButtonOnPrimary, boolean isSymbols) {
-        if (mKeyboardView == null) return;
-
-        mAttribute = attribute;
-        mVoiceKeyEnabled = voiceButtonEnabled;
-        mVoiceButtonOnPrimary = voiceButtonOnPrimary;
-        mIsSymbols = isSymbols;
-        // Update the settings key state because number of enabled IMEs could have been changed
-        mSettingsKeyEnabledInSettings = getSettingsKeyMode(mPrefs, mInputMethodService);
-        final KeyboardId id = getKeyboardId(attribute, isSymbols);
-
-        // 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);
-
-        setKeyboard(getKeyboard(id));
+    public KeyboardLayoutState getKeyboardState() {
+        return mSavedKeyboardState;
     }
 
-    public void onSizeChanged() {
-        final int width = mInputMethodService.getWindow().getWindow().getDecorView().getWidth();
+    public void onFinishInputView() {
+        mIsAutoCorrectionActive = false;
+    }
+
+    public void onHideWindow() {
+        mIsAutoCorrectionActive = false;
+    }
+
+    public void registerWindowWidth() {
+        mWindowWidthCache.registerWidth();
+    }
+
+    @SuppressWarnings("unused")
+    public void onSizeChanged(int w, int h, int oldw, int oldh) {
+        // TODO: This hack should be removed when display metric returns a proper width.
+        // Until then, the behavior of KeyboardSwitcher is suboptimal on a device that has a
+        // vertical system navigation bar in landscape screen orientation, for instance.
+        final Configuration conf = mResources.getConfiguration();
+        final int width = mWindowWidthCache.getWidthOnSizeChanged(conf);
+        // If the window width hasn't fixed yet or keyboard doesn't exist, nothing to do with.
         if (width == 0 || mCurrentId == null)
             return;
-        mKeyboardWidth = width;
-        // Set keyboard with new width.
-        final KeyboardId newId = mCurrentId.cloneWithNewGeometry(width);
+        // Reload keyboard with new width.
+        final KeyboardId newId = mCurrentId.cloneWithNewGeometry(conf.orientation, width);
+        mInputMethodService.mHandler.postRestoreKeyboardLayout();
         setKeyboard(getKeyboard(newId));
     }
 
-    private void setKeyboard(final Keyboard newKeyboard) {
+    private void setKeyboard(final Keyboard keyboard) {
         final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
-        mKeyboardView.setKeyboard(newKeyboard);
-        mCurrentId = newKeyboard.mId;
-        final Resources res = mInputMethodService.getResources();
+        mKeyboardView.setKeyboard(keyboard);
+        mCurrentId = keyboard.mId;
+        mSwitchState = getSwitchState(mCurrentId);
+        updateShiftLockState(keyboard);
         mKeyboardView.setKeyPreviewPopupEnabled(
-                Settings.Values.isKeyPreviewPopupEnabled(mPrefs, res),
-                Settings.Values.getKeyPreviewPopupDismissDelay(mPrefs, res));
+                Settings.Values.isKeyPreviewPopupEnabled(mPrefs, mResources),
+                Settings.Values.getKeyPreviewPopupDismissDelay(mPrefs, mResources));
         final boolean localeChanged = (oldKeyboard == null)
-                || !newKeyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
+                || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
         mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged);
     }
 
+    private int getSwitchState(KeyboardId id) {
+        return id.equals(mMainKeyboardId) ? SWITCH_STATE_ALPHA : SWITCH_STATE_SYMBOL_BEGIN;
+    }
+
+    private void updateShiftLockState(Keyboard keyboard) {
+        if (mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
+            // Symbol keyboard may have an ALT key that has a caps lock style indicator (a.k.a.
+            // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked()
+            // that takes care of the current keyboard having such ALT key or not.
+            keyboard.setShiftLocked(keyboard.hasShiftLockKey());
+        } else if (mCurrentId.equals(mSymbolsKeyboardId)) {
+            // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
+            // indicator, we need to call setShiftLocked(false).
+            keyboard.setShiftLocked(false);
+        }
+    }
+
     private LatinKeyboard getKeyboard(KeyboardId id) {
         final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id);
         LatinKeyboard keyboard = (ref == null) ? null : ref.get();
         if (keyboard == null) {
-            final Resources res = mInputMethodService.getResources();
-            final Locale savedLocale = Utils.setSystemLocale(res,
-                    mSubtypeSwitcher.getInputLocale());
-
-            keyboard = new LatinKeyboard(mThemeContext, id, id.mWidth);
-
-            if (id.mEnableShiftLock) {
-                keyboard.enableShiftLock();
+            final Locale savedLocale = Utils.setSystemLocale(
+                    mResources, mSubtypeSwitcher.getInputLocale());
+            try {
+                keyboard = new LatinKeyboard.Builder(mThemeContext).load(id).build();
+            } finally {
+                Utils.setSystemLocale(mResources, savedLocale);
             }
-
             mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard));
-            if (DEBUG_CACHE)
+
+            if (DEBUG_CACHE) {
                 Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
                         + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
-
-            Utils.setSystemLocale(res, savedLocale);
+            }
         } else if (DEBUG_CACHE) {
             Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id);
         }
 
         keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive);
+        keyboard.setShiftLocked(false);
         keyboard.setShifted(false);
         // If the cached keyboard had been switched to another keyboard while the language was
         // displayed on its spacebar, it might have had arbitrary text fade factor. In such case,
         // 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;
     }
 
-    private boolean hasVoiceKey(boolean isSymbols) {
-        return mVoiceKeyEnabled && (isSymbols != mVoiceButtonOnPrimary);
-    }
-
-    private boolean hasSettingsKey(EditorInfo attribute) {
-        return mSettingsKeyEnabledInSettings
-            && !Utils.inPrivateImeOptions(mInputMethodService.getPackageName(),
-                    LatinIME.IME_OPTION_NO_SETTINGS_KEY, attribute);
-    }
-
-    private KeyboardId getKeyboardId(EditorInfo attribute, boolean isSymbols) {
-        final int mode = Utils.getKeyboardMode(attribute);
-        final boolean hasVoiceKey = hasVoiceKey(isSymbols);
+    private KeyboardId getKeyboardId(EditorInfo editorInfo, final boolean isSymbols,
+            final boolean isShift, Settings.Values settingsValues) {
+        final int mode = Utils.getKeyboardMode(editorInfo);
         final int xmlId;
-        final boolean enableShiftLock;
-
-        if (isSymbols) {
-            if (mode == KeyboardId.MODE_PHONE) {
-                xmlId = R.xml.kbd_phone_symbols;
-            } else if (mode == KeyboardId.MODE_NUMBER) {
-                // Note: MODE_NUMBER keyboard layout has no "switch alpha symbol" key.
-                xmlId = R.xml.kbd_number;
-            } else {
-                xmlId = R.xml.kbd_symbols;
-            }
-            enableShiftLock = false;
-        } else {
-            if (mode == KeyboardId.MODE_PHONE) {
-                xmlId = R.xml.kbd_phone;
-                enableShiftLock = false;
-            } else if (mode == KeyboardId.MODE_NUMBER) {
-                xmlId = R.xml.kbd_number;
-                enableShiftLock = false;
+        switch (mode) {
+        case KeyboardId.MODE_PHONE:
+            xmlId = (isSymbols && isShift) ? R.xml.kbd_phone_shift : R.xml.kbd_phone;
+            break;
+        case KeyboardId.MODE_NUMBER:
+            xmlId = R.xml.kbd_number;
+            break;
+        default:
+            if (isSymbols) {
+                xmlId = isShift ? R.xml.kbd_symbols_shift : R.xml.kbd_symbols;
             } else {
                 xmlId = R.xml.kbd_qwerty;
-                enableShiftLock = true;
             }
+            break;
         }
-        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, locale, orientation, mKeyboardWidth,
-                mode, attribute, hasSettingsKey, f2KeyMode, clobberSettingsKey, mVoiceKeyEnabled,
-                hasVoiceKey, enableShiftLock);
-    }
 
-    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);
+        final boolean settingsKeyEnabled = settingsValues.isSettingsKeyEnabled(editorInfo);
+        final boolean noMicrophone = Utils.inPrivateImeOptions(
+                mPackageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo)
+                || Utils.inPrivateImeOptions(
+                        null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo);
+        final boolean voiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo)
+                && !noMicrophone;
+        final boolean voiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
+        final boolean noSettingsKey = Utils.inPrivateImeOptions(
+                mPackageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo);
+        final boolean hasSettingsKey = settingsKeyEnabled && !noSettingsKey;
+        final int f2KeyMode = getF2KeyMode(settingsKeyEnabled, noSettingsKey);
+        final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != voiceKeyOnMain);
+        final Configuration conf = mResources.getConfiguration();
+
+        return new KeyboardId(
+                mResources.getResourceEntryName(xmlId), xmlId, mSubtypeSwitcher.getInputLocale(),
+                conf.orientation, mWindowWidthCache.getWidth(conf), mode, editorInfo,
+                hasSettingsKey, f2KeyMode, noSettingsKey, voiceKeyEnabled, hasShortcutKey);
     }
 
     public int getKeyboardMode() {
@@ -593,16 +710,11 @@
         if (isAlphabetMode())
             return;
         final LatinKeyboard keyboard;
-        if (mCurrentId.equals(mSymbolsId) || !mCurrentId.equals(mSymbolsShiftedId)) {
-            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 setShiftLocked(true).
-            keyboard.setShiftLocked(true);
+        if (mCurrentId.equals(mSymbolsKeyboardId)
+                || !mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
+            keyboard = getKeyboard(mSymbolsShiftedKeyboardId);
         } else {
-            keyboard = getKeyboard(mSymbolsId);
-            // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
-            // indicator, we need to call setShiftLocked(false).
-            keyboard.setShiftLocked(false);
+            keyboard = getKeyboard(mSymbolsKeyboardId);
         }
         setKeyboard(keyboard);
     }
@@ -621,11 +733,10 @@
     }
 
     private void toggleKeyboardMode() {
-        loadKeyboardInternal(mAttribute, mVoiceKeyEnabled, mVoiceButtonOnPrimary, !mIsSymbols);
-        if (mIsSymbols) {
-            mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
+        if (mCurrentId.equals(mMainKeyboardId)) {
+            setKeyboard(getKeyboard(mSymbolsKeyboardId));
         } else {
-            mSwitchState = SWITCH_STATE_ALPHA;
+            setKeyboard(getKeyboard(mMainKeyboardId));
         }
     }
 
@@ -675,10 +786,10 @@
             // {@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) {
-                    mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
-                } else {
+                if (mCurrentId.equals(mMainKeyboardId)) {
                     mSwitchState = SWITCH_STATE_ALPHA;
+                } else {
+                    mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
                 }
             } else if (getPointerCount() == 1) {
                 // Snap back to the previous keyboard mode if the user pressed the mode change key
@@ -788,11 +899,9 @@
     @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
         if (PREF_KEYBOARD_LAYOUT.equals(key)) {
-            final int layoutId = getKeyboardThemeIndex(mInputMethodService, sharedPreferences);
-            postSetInputView(createInputView(layoutId, false));
-        } else if (Settings.PREF_SETTINGS_KEY.equals(key)) {
-            mSettingsKeyEnabledInSettings = getSettingsKeyMode(sharedPreferences,
-                    mInputMethodService);
+            final int themeIndex = getKeyboardThemeIndex(mInputMethodService, sharedPreferences);
+            postSetInputView(createInputView(themeIndex, false));
+        } else if (Settings.PREF_SHOW_SETTINGS_KEY.equals(key)) {
             postSetInputView(createInputView(mThemeIndex, true));
         }
     }
@@ -810,41 +919,18 @@
         }
     }
 
-    private static boolean getSettingsKeyMode(SharedPreferences prefs, Context context) {
-        final Resources res = context.getResources();
-        final boolean showSettingsKeyOption = res.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));
-            // 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))
-                            && Utils.hasMultipleEnabledIMEsOrSubtypes(
-                                    (InputMethodManagerCompatWrapper.getInstance(context))))) {
-                return true;
-            }
-            return false;
-        }
-        // 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
+    private static int getF2KeyMode(boolean settingsKeyEnabled, boolean noSettingsKey) {
+        if (noSettingsKey) {
+            // Never shows the Settings key
             return KeyboardId.F2KEY_MODE_SHORTCUT_IME;
         }
+
+        if (settingsKeyEnabled) {
+            return KeyboardId.F2KEY_MODE_SETTINGS;
+        } else {
+            // It should be alright to fall back to the Settings key on 7-inch layouts
+            // even when the Settings key is not explicitly enabled.
+            return KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS;
+        }
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 2e06831..2df2994 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -35,6 +35,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import com.android.inputmethod.compat.FrameLayoutCompatUtils;
@@ -73,8 +74,6 @@
  * @attr ref R.styleable#KeyboardView_shadowRadius
  */
 public class KeyboardView extends View implements PointerTracker.DrawingProxy {
-    private static final boolean DEBUG_KEYBOARD_GRID = false;
-
     // Miscellaneous constants
     private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
 
@@ -89,25 +88,23 @@
     private final KeyDrawParams mKeyDrawParams;
 
     // Key preview
+    private final int mKeyPreviewLayoutId;
     private final KeyPreviewDrawParams mKeyPreviewDrawParams;
-    private final TextView mPreviewText;
     private boolean mShowKeyPreviewPopup = true;
     private final int mDelayBeforePreview;
     private int mDelayAfterPreview;
     private ViewGroup mPreviewPlacer;
 
     // Drawing
-    /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/
-    private boolean mDrawPending;
-    /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */
-    private boolean mKeyboardChanged;
+    /** Whether the keyboard bitmap buffer needs to be redrawn before it's blitted. **/
+    private boolean mBufferNeedsUpdate;
     /** The dirty region in the keyboard bitmap */
     private final Rect mDirtyRect = new Rect();
     /** The key to invalidate. */
     private Key mInvalidatedKey;
     /** The dirty region for single key drawing */
     private final Rect mInvalidatedKeyRect = new Rect();
-    /** The keyboard bitmap for faster updates */
+    /** The keyboard bitmap buffer for faster updates */
     private Bitmap mBuffer;
     /** The canvas for the above mutable keyboard bitmap */
     private Canvas mCanvas;
@@ -136,23 +133,23 @@
         @Override
         public void handleMessage(Message msg) {
             final KeyboardView keyboardView = getOuterInstance();
+            if (keyboardView == null) return;
             final PointerTracker tracker = (PointerTracker) msg.obj;
             switch (msg.what) {
             case MSG_SHOW_KEY_PREVIEW:
                 keyboardView.showKey(msg.arg1, tracker);
                 break;
             case MSG_DISMISS_KEY_PREVIEW:
-                if (keyboardView.mPreviewText != null) {
-                    keyboardView.mPreviewText.setVisibility(View.INVISIBLE);
-                }
+                tracker.getKeyPreviewText().setVisibility(View.INVISIBLE);
                 break;
             }
         }
 
         public void showKeyPreview(long delay, int keyIndex, PointerTracker tracker) {
-            final KeyboardView keyboardView = getOuterInstance();
             removeMessages(MSG_SHOW_KEY_PREVIEW);
-            if (keyboardView.mPreviewText.getVisibility() == VISIBLE || delay == 0) {
+            final KeyboardView keyboardView = getOuterInstance();
+            if (keyboardView == null) return;
+            if (tracker.getKeyPreviewText().getVisibility() == VISIBLE || delay == 0) {
                 // Show right away, if it's already visible and finger is moving around
                 keyboardView.showKey(keyIndex, tracker);
             } else {
@@ -265,7 +262,6 @@
         public final Drawable mPreviewBackground;
         public final Drawable mPreviewLeftBackground;
         public final Drawable mPreviewRightBackground;
-        public final Drawable mPreviewSpacebarBackground;
         public final int mPreviewTextColor;
         public final int mPreviewOffset;
         public final int mPreviewHeight;
@@ -286,8 +282,6 @@
                     R.styleable.KeyboardView_keyPreviewLeftBackground);
             mPreviewRightBackground = a.getDrawable(
                     R.styleable.KeyboardView_keyPreviewRightBackground);
-            mPreviewSpacebarBackground = a.getDrawable(
-                    R.styleable.KeyboardView_keyPreviewSpacebarBackground);
             setAlpha(mPreviewBackground, PREVIEW_ALPHA);
             setAlpha(mPreviewLeftBackground, PREVIEW_ALPHA);
             setAlpha(mPreviewRightBackground, PREVIEW_ALPHA);
@@ -326,11 +320,8 @@
 
         mKeyDrawParams = new KeyDrawParams(a);
         mKeyPreviewDrawParams = new KeyPreviewDrawParams(a, mKeyDrawParams);
-        final int previewLayout = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0);
-        if (previewLayout != 0) {
-            mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null);
-        } else {
-            mPreviewText = null;
+        mKeyPreviewLayoutId = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0);
+        if (mKeyPreviewLayoutId == 0) {
             mShowKeyPreviewPopup = false;
         }
         mBackgroundDimAmount = a.getFloat(R.styleable.KeyboardView_backgroundDimAmount, 0.5f);
@@ -359,14 +350,18 @@
      * @param keyboard the keyboard to display in this view
      */
     public void setKeyboard(Keyboard keyboard) {
-        // Remove any pending messages, except dismissing preview
+        // Remove any pending dismissing preview
         mDrawingHandler.cancelAllShowKeyPreviews();
+        if (mKeyboard != null) {
+            PointerTracker.dismissAllKeyPreviews();
+        }
         mKeyboard = keyboard;
         LatinImeLogger.onSetKeyboard(keyboard);
         requestLayout();
-        mKeyboardChanged = true;
+        mDirtyRect.set(0, 0, getWidth(), getHeight());
+        mBufferNeedsUpdate = true;
         invalidateAllKeys();
-        final int keyHeight = keyboard.getRowHeight() - keyboard.getVerticalGap();
+        final int keyHeight = keyboard.mDefaultRowHeight - keyboard.mVerticalGap;
         mKeyDrawParams.updateKeyHeight(keyHeight);
         mKeyPreviewDrawParams.updateKeyHeight(keyHeight);
     }
@@ -402,25 +397,21 @@
     }
 
     @Override
-    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // Round up a little
-        if (mKeyboard == null) {
-            setMeasuredDimension(
-                    getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom());
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mKeyboard != null) {
+            // The main keyboard expands to the display width.
+            final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
+            setMeasuredDimension(widthMeasureSpec, height);
         } else {
-            int width = mKeyboard.getMinWidth() + getPaddingLeft() + getPaddingRight();
-            if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) {
-                width = MeasureSpec.getSize(widthMeasureSpec);
-            }
-            setMeasuredDimension(
-                    width, mKeyboard.getHeight() + getPaddingTop() + getPaddingBottom());
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
     }
 
     @Override
     public void onDraw(Canvas canvas) {
         super.onDraw(canvas);
-        if (mDrawPending || mBuffer == null || mKeyboardChanged) {
+        if (mBufferNeedsUpdate || mBuffer == null) {
+            mBufferNeedsUpdate = false;
             onBufferDraw();
         }
         canvas.drawBitmap(mBuffer, 0, 0, null);
@@ -431,14 +422,11 @@
         final int height = getHeight();
         if (width == 0 || height == 0)
             return;
-        if (mBuffer == null || mKeyboardChanged) {
-            mKeyboardChanged = false;
-            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);
+            mDirtyRect.union(0, 0, width, height);
             if (mCanvas != null) {
                 mCanvas.setBitmap(mBuffer);
             } else {
@@ -459,37 +447,20 @@
                     + getPaddingLeft();
             final int keyDrawY = mInvalidatedKey.mY + getPaddingTop();
             canvas.translate(keyDrawX, keyDrawY);
-            onBufferDrawKey(mInvalidatedKey, canvas, mPaint, params, isManualTemporaryUpperCase);
+            onBufferDrawKey(mInvalidatedKey, mKeyboard, canvas, mPaint, params,
+                    isManualTemporaryUpperCase);
             canvas.translate(-keyDrawX, -keyDrawY);
         } else {
             // Draw all keys.
-            for (final Key key : mKeyboard.getKeys()) {
+            for (final Key key : mKeyboard.mKeys) {
                 final int keyDrawX = key.mX + key.mVisualInsetsLeft + getPaddingLeft();
                 final int keyDrawY = key.mY + getPaddingTop();
                 canvas.translate(keyDrawX, keyDrawY);
-                onBufferDrawKey(key, canvas, mPaint, params, isManualTemporaryUpperCase);
+                onBufferDrawKey(key, mKeyboard, canvas, mPaint, params, isManualTemporaryUpperCase);
                 canvas.translate(-keyDrawX, -keyDrawY);
             }
         }
 
-        // 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;
-            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++)
-                canvas.drawLine(0, i * ch, cw * mKeyboard.GRID_WIDTH, i * ch, p);
-        }
-
         // Overlay a dark rectangle to dim the keyboard
         if (needsToDimKeyboard()) {
             mPaint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
@@ -497,7 +468,6 @@
         }
 
         mInvalidatedKey = null;
-        mDrawPending = false;
         mDirtyRect.setEmpty();
     }
 
@@ -505,8 +475,8 @@
         return false;
     }
 
-    private static void onBufferDrawKey(final Key key, final Canvas canvas, Paint paint,
-            KeyDrawParams params, boolean isManualTemporaryUpperCase) {
+    private static void onBufferDrawKey(final Key key, final Keyboard keyboard, final Canvas canvas,
+            Paint paint, KeyDrawParams params, boolean isManualTemporaryUpperCase) {
         final boolean debugShowAlign = LatinImeLogger.sVISUALDEBUG;
         // Draw key background.
         final int bgWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight
@@ -529,7 +499,7 @@
         canvas.translate(-bgX, -bgY);
 
         // Draw key top visuals.
-        final int keyWidth = key.mWidth;
+        final int keyWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
         final int keyHeight = key.mHeight;
         final float centerX = keyWidth * 0.5f;
         final float centerY = keyHeight * 0.5f;
@@ -539,10 +509,11 @@
         }
 
         // Draw key label.
+        final Drawable icon = key.getIcon();
         float positionX = centerX;
         if (key.mLabel != null) {
             // Switch the character to uppercase if shift is pressed
-            final CharSequence label = key.getCaseAdjustedLabel();
+            final CharSequence label = keyboard.adjustLabelCase(key.mLabel);
             // For characters, use large font. For labels like "Done", use smaller font.
             paint.setTypeface(key.selectTypeface(params.mKeyTextStyle));
             final int labelSize = key.selectTextSize(params.mKeyLetterSize,
@@ -555,16 +526,25 @@
             final float baseline = centerY + labelCharHeight / 2;
 
             // Horizontal label text alignment
-            if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT) != 0) {
+            float labelWidth = 0;
+            if (key.isAlignLeft()) {
                 positionX = (int)params.mKeyLabelHorizontalPadding;
                 paint.setTextAlign(Align.LEFT);
-            } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_RIGHT) != 0) {
+            } else if (key.isAlignRight()) {
                 positionX = keyWidth - (int)params.mKeyLabelHorizontalPadding;
                 paint.setTextAlign(Align.RIGHT);
-            } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT_OF_CENTER) != 0) {
+            } else if (key.isAlignLeftOfCenter()) {
                 // TODO: Parameterise this?
                 positionX = centerX - labelCharWidth * 7 / 4;
                 paint.setTextAlign(Align.LEFT);
+            } else if (key.hasLabelWithIconLeft() && icon != null) {
+                labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth();
+                positionX = centerX + labelWidth / 2;
+                paint.setTextAlign(Align.RIGHT);
+            } else if (key.hasLabelWithIconRight() && icon != null) {
+                labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth();
+                positionX = centerX - labelWidth / 2;
+                paint.setTextAlign(Align.LEFT);
             } else {
                 positionX = centerX;
                 paint.setTextAlign(Align.CENTER);
@@ -586,6 +566,19 @@
             // Turn off drop shadow
             paint.setShadowLayer(0, 0, 0, 0);
 
+            if (icon != null) {
+                final int iconWidth = icon.getIntrinsicWidth();
+                final int iconHeight = icon.getIntrinsicHeight();
+                final int iconY = (keyHeight - iconHeight) / 2;
+                if (key.hasLabelWithIconLeft()) {
+                    final int iconX = (int)(centerX - labelWidth / 2);
+                    drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight);
+                } else if (key.hasLabelWithIconRight()) {
+                    final int iconX = (int)(centerX + labelWidth / 2 - iconWidth);
+                    drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight);
+                }
+            }
+
             if (debugShowAlign) {
                 final Paint line = new Paint();
                 drawHorizontalLine(canvas, baseline, keyWidth, 0xc0008000, line);
@@ -639,16 +632,15 @@
         }
 
         // Draw key icon.
-        final Drawable icon = key.getIcon();
         if (key.mLabel == null && icon != null) {
             final int iconWidth = icon.getIntrinsicWidth();
             final int iconHeight = icon.getIntrinsicHeight();
             final int iconX, alignX;
             final int iconY = (keyHeight - iconHeight) / 2;
-            if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT) != 0) {
+            if (key.isAlignLeft()) {
                 iconX = (int)params.mKeyLabelHorizontalPadding;
                 alignX = iconX;
-            } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_RIGHT) != 0) {
+            } else if (key.isAlignRight()) {
                 iconX = keyWidth - (int)params.mKeyLabelHorizontalPadding - iconWidth;
                 alignX = iconX + iconWidth;
             } else { // Align center
@@ -665,7 +657,8 @@
         }
 
         // Draw popup hint "..." at the bottom right corner of the key.
-        if (key.hasPopupHint()) {
+        if ((key.hasPopupHint() && key.mPopupCharacters != null && key.mPopupCharacters.length > 0)
+                || key.needsSpecialPopupHint()) {
             paint.setTextSize(params.mKeyHintLetterSize);
             paint.setColor(params.mKeyHintLabelColor);
             paint.setTextAlign(Align.CENTER);
@@ -728,6 +721,11 @@
         return width;
     }
 
+    private static float getLabelWidth(CharSequence label, Paint paint) {
+        paint.getTextBounds(label.toString(), 0, label.length(), sTextBounds);
+        return sTextBounds.width();
+    }
+
     private static void drawIcon(Canvas canvas, Drawable icon, int x, int y, int width,
             int height) {
         canvas.translate(x, y);
@@ -764,13 +762,21 @@
         mDrawingHandler.cancelAllMessages();
     }
 
+    // Called by {@link PointerTracker} constructor to create a TextView.
+    @Override
+    public TextView inflateKeyPreviewText() {
+        final Context context = getContext();
+        if (mKeyPreviewLayoutId != 0) {
+            return (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null);
+        } else {
+            return new TextView(context);
+        }
+    }
+
     @Override
     public void showKeyPreview(int keyIndex, PointerTracker tracker) {
         if (mShowKeyPreviewPopup) {
             mDrawingHandler.showKeyPreview(mDelayBeforePreview, keyIndex, tracker);
-        } else if (mKeyboard.needSpacebarPreview(keyIndex)) {
-            // Show key preview (in this case, slide language switcher) without any delay.
-            showKey(keyIndex, tracker);
         }
     }
 
@@ -781,34 +787,30 @@
 
     @Override
     public void dismissKeyPreview(PointerTracker tracker) {
-        if (mShowKeyPreviewPopup) {
-            mDrawingHandler.cancelShowKeyPreview(tracker);
-            mDrawingHandler.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);
-        }
+        mDrawingHandler.cancelShowKeyPreview(tracker);
+        mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker);
     }
 
     private void addKeyPreview(TextView keyPreview) {
         if (mPreviewPlacer == null) {
-            mPreviewPlacer = FrameLayoutCompatUtils.getPlacer(
-                    (ViewGroup)getRootView().findViewById(android.R.id.content));
+            mPreviewPlacer = new RelativeLayout(getContext());
+            final ViewGroup windowContentView =
+                    (ViewGroup)getRootView().findViewById(android.R.id.content);
+            windowContentView.addView(mPreviewPlacer);
         }
-        final ViewGroup placer = mPreviewPlacer;
-        placer.addView(keyPreview, FrameLayoutCompatUtils.newLayoutParam(placer, 0, 0));
+        mPreviewPlacer.addView(
+                keyPreview, FrameLayoutCompatUtils.newLayoutParam(mPreviewPlacer, 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) {
-        final TextView previewText = mPreviewText;
+        final TextView previewText = tracker.getKeyPreviewText();
         // 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);
         }
 
+        mDrawingHandler.cancelDismissKeyPreview(tracker);
         final Key key = tracker.getKey(keyIndex);
         // If keyIndex is invalid or IME is already closed, we must not show key preview.
         // Trying to show key preview while root window is closed causes
@@ -816,7 +818,6 @@
         if (key == null)
             return;
 
-        mDrawingHandler.cancelAllDismissKeyPreviews();
         final KeyPreviewDrawParams params = mKeyPreviewDrawParams;
         final int keyDrawX = key.mX + key.mVisualInsetsLeft;
         final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
@@ -831,18 +832,14 @@
                 previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mPreviewTextSize);
                 previewText.setTypeface(params.mKeyTextStyle);
             }
-            previewText.setText(key.getCaseAdjustedLabel());
+            previewText.setText(mKeyboard.adjustLabelCase(key.mLabel));
         } else {
             final Drawable previewIcon = key.getPreviewIcon();
             previewText.setCompoundDrawables(null, null, null,
                    previewIcon != null ? previewIcon : key.getIcon());
             previewText.setText(null);
         }
-        if (key.mCode == Keyboard.CODE_SPACE) {
-            previewText.setBackgroundDrawable(params.mPreviewSpacebarBackground);
-        } else {
-            previewText.setBackgroundDrawable(params.mPreviewBackground);
-        }
+        previewText.setBackgroundDrawable(params.mPreviewBackground);
 
         previewText.measure(MEASURESPEC_UNSPECIFIED, MEASURESPEC_UNSPECIFIED);
         final int previewWidth = Math.max(previewText.getMeasuredWidth(), keyDrawWidth
@@ -877,7 +874,7 @@
      */
     public void invalidateAllKeys() {
         mDirtyRect.union(0, 0, getWidth(), getHeight());
-        mDrawPending = true;
+        mBufferNeedsUpdate = true;
         invalidate();
     }
 
@@ -897,18 +894,23 @@
         final int y = key.mY + getPaddingTop();
         mInvalidatedKeyRect.set(x, y, x + key.mWidth, y + key.mHeight);
         mDirtyRect.union(mInvalidatedKeyRect);
-        onBufferDraw();
+        mBufferNeedsUpdate = true;
         invalidate(mInvalidatedKeyRect);
     }
 
     public void closing() {
-        mPreviewText.setVisibility(View.GONE);
+        PointerTracker.dismissAllKeyPreviews();
         cancelAllMessages();
 
         mDirtyRect.union(0, 0, getWidth(), getHeight());
         requestLayout();
     }
 
+    @Override
+    public boolean dismissPopupPanel() {
+        return false;
+    }
+
     public void purgeKeyboardAndClosing() {
         mKeyboard = null;
         closing();
@@ -918,5 +920,8 @@
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         closing();
+        if (mPreviewPlacer != null) {
+            mPreviewPlacer.removeAllViews();
+        }
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index d925b8c..1b6f57b 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -31,23 +31,22 @@
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 
-import com.android.inputmethod.keyboard.internal.SlidingLocaleDrawable;
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.Utils;
 
 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 {
     private static final int SPACE_LED_LENGTH_PERCENT = 80;
 
-    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();
@@ -55,34 +54,20 @@
     /* Space key and its icons, drawables and colors. */
     private final Key mSpaceKey;
     private final Drawable mSpaceIcon;
-    private final Drawable mSpacePreviewIcon;
-    private final int mSpaceKeyIndex;
     private final boolean mAutoCorrectionSpacebarLedEnabled;
     private final Drawable mAutoCorrectionSpacebarLedIcon;
     private final int mSpacebarTextColor;
     private final int mSpacebarTextShadowColor;
     private float mSpacebarTextFadeFactor = 0.0f;
-    private final int mSpacebarLanguageSwitchThreshold;
-    private int mSpacebarSlidingLanguageSwitchDiff;
-    private final SlidingLocaleDrawable mSlidingLocaleIcon;
     private final HashMap<Integer, SoftReference<BitmapDrawable>> mSpaceDrawableCache =
             new HashMap<Integer, SoftReference<BitmapDrawable>>();
+    private final boolean mIsSpacebarTriggeringPopupByLongPress;
 
     /* Shortcut key and its icons if available */
     private final Key mShortcutKey;
     private final Drawable mEnabledShortcutIcon;
     private final Drawable mDisabledShortcutIcon;
 
-    // BLACK LEFT-POINTING TRIANGLE and two spaces.
-    public static final String ARROW_LEFT = "\u25C0  ";
-    // Two spaces and BLACK RIGHT-POINTING TRIANGLE.
-    public static final String ARROW_RIGHT = "  \u25B6";
-
-    // 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,
@@ -92,35 +77,20 @@
     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, int width) {
-        super(context, id.getXmlId(), id, width);
+    private LatinKeyboard(Context context, LatinKeyboardParams params) {
+        super(params);
         mRes = context.getResources();
         mTheme = context.getTheme();
 
-        final List<Key> keys = getKeys();
-        int spaceKeyIndex = -1;
-        int shortcutKeyIndex = -1;
-        final int keyCount = keys.size();
-        for (int index = 0; index < keyCount; index++) {
-            // For now, assuming there are up to one space key and one shortcut key respectively.
-            switch (keys.get(index).mCode) {
-            case CODE_SPACE:
-                spaceKeyIndex = index;
-                break;
-            case CODE_SHORTCUT:
-                shortcutKeyIndex = index;
-                break;
-            }
-        }
-
         // The index of space key is available only after Keyboard constructor has finished.
-        mSpaceKey = (spaceKeyIndex >= 0) ? keys.get(spaceKeyIndex) : null;
+        mSpaceKey = params.mSpaceKey;
         mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon() : null;
-        mSpacePreviewIcon = (mSpaceKey != null) ? mSpaceKey.getPreviewIcon() : null;
-        mSpaceKeyIndex = spaceKeyIndex;
 
-        mShortcutKey = (shortcutKeyIndex >= 0) ? keys.get(shortcutKeyIndex) : null;
+        mShortcutKey = params.mShortcutKey;
         mEnabledShortcutIcon = (mShortcutKey != null) ? mShortcutKey.getIcon() : null;
+        final int longPressSpaceKeyTimeout =
+                mRes.getInteger(R.integer.config_long_press_space_key_timeout);
+        mIsSpacebarTriggeringPopupByLongPress = (longPressSpaceKeyTimeout > 0);
 
         final TypedArray a = context.obtainStyledAttributes(
                 null, R.styleable.LatinKeyboard, R.attr.latinKeyboardStyle, R.style.LatinKeyboard);
@@ -133,19 +103,41 @@
         mSpacebarTextShadowColor = a.getColor(
                 R.styleable.LatinKeyboard_spacebarTextShadowColor, 0);
         a.recycle();
+    }
 
-        // The threshold is "key width" x 1.25
-        mSpacebarLanguageSwitchThreshold = (getMostCommonKeyWidth() * 5) / 4;
+    private static class LatinKeyboardParams extends KeyboardParams {
+        public Key mSpaceKey = null;
+        public Key mShortcutKey = null;
 
-        if (mSpaceKey != null && mSpacePreviewIcon != 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;
+        @Override
+        public void onAddKey(Key key) {
+            super.onAddKey(key);
+
+            switch (key.mCode) {
+            case Keyboard.CODE_SPACE:
+                mSpaceKey = key;
+                break;
+            case Keyboard.CODE_SHORTCUT:
+                mShortcutKey = key;
+                break;
+            }
+        }
+    }
+
+    public static class Builder extends KeyboardBuilder<LatinKeyboardParams> {
+        public Builder(Context context) {
+            super(context, new LatinKeyboardParams());
+        }
+
+        @Override
+        public Builder load(KeyboardId id) {
+            super.load(id);
+            return this;
+        }
+
+        @Override
+        public LatinKeyboard build() {
+            return new LatinKeyboard(mContext, mParams);
         }
     }
 
@@ -193,8 +185,13 @@
     }
 
     private void updateSpacebarForLocale(boolean isAutoCorrection) {
-        if (mSpaceKey == null)
-            return;
+        if (mSpaceKey == null) return;
+        final InputMethodManagerCompatWrapper imm = InputMethodManagerCompatWrapper.getInstance();
+        if (imm == null) return;
+        // The "..." popup hint for triggering something by a long-pressing the spacebar
+        final boolean shouldShowInputMethodPicker = mIsSpacebarTriggeringPopupByLongPress
+                && Utils.hasMultipleEnabledIMEsOrSubtypes(imm, true /* include aux subtypes */);
+        mSpaceKey.setNeedsSpecialPopupHint(shouldShowInputMethodPicker);
         // If application locales are explicitly selected.
         if (mSubtypeSwitcher.needsToDisplayLanguage()) {
             mSpaceKey.setIcon(getSpaceDrawable(
@@ -219,8 +216,7 @@
         final Rect bounds = new Rect();
 
         // Estimate appropriate language name text size to fit in maxTextWidth.
-        String language = ARROW_LEFT + SubtypeSwitcher.getFullDisplayName(locale, true)
-                + ARROW_RIGHT;
+        String language = SubtypeSwitcher.getFullDisplayName(locale, true);
         int textWidth = getTextWidth(paint, language, origTextSize, bounds);
         // Assuming text width and text size are proportional to each other.
         float textSize = origTextSize * Math.min(width / textWidth, 1.0f);
@@ -232,7 +228,7 @@
 
         final boolean useShortName;
         if (useMiddleName) {
-            language = ARROW_LEFT + SubtypeSwitcher.getMiddleDisplayLanguage(locale) + ARROW_RIGHT;
+            language = SubtypeSwitcher.getMiddleDisplayLanguage(locale);
             textWidth = getTextWidth(paint, language, origTextSize, bounds);
             textSize = origTextSize * Math.min(width / textWidth, 1.0f);
             useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME)
@@ -242,7 +238,7 @@
         }
 
         if (useShortName) {
-            language = ARROW_LEFT + SubtypeSwitcher.getShortDisplayLanguage(locale) + ARROW_RIGHT;
+            language = SubtypeSwitcher.getShortDisplayLanguage(locale);
             textWidth = getTextWidth(paint, language, origTextSize, bounds);
             textSize = origTextSize * Math.min(width / textWidth, 1.0f);
         }
@@ -327,68 +323,11 @@
         return buffer;
     }
 
-    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);
-        } else {
-            mSpaceKey.setPreviewIcon(mSlidingLocaleIcon);
-        }
-        mSpaceKey.getPreviewIcon().invalidateSelf();
-    }
-
-    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;
-    }
-
-    /**
-     * 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 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
-        }
-        return mSpacebarSlidingLanguageSwitchDiff > 0 ? 1 : -1;
-    }
-
     @Override
     public int[] getNearestKeys(int x, int y) {
         // 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)));
+        return super.getNearestKeys(Math.max(0, Math.min(x, mOccupiedWidth - 1)),
+                Math.max(0, Math.min(y, mOccupiedHeight - 1)));
     }
 
     public static int getTextSizeFromTheme(Theme theme, int style, int defValue) {
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
index 078d89f..12aadcb 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
@@ -269,7 +269,7 @@
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         // TODO: Should notify InputMethodService instead?
-        KeyboardSwitcher.getInstance().onSizeChanged();
+        KeyboardSwitcher.getInstance().onSizeChanged(w, h, oldw, oldh);
     }
 
     /**
@@ -281,15 +281,12 @@
      */
     @Override
     public void setKeyboard(Keyboard keyboard) {
-        if (getKeyboard() != null) {
-            PointerTracker.dismissAllKeyPreviews();
-        }
         // Remove any pending messages, except dismissing preview
         mKeyTimerHandler.cancelKeyTimers();
         super.setKeyboard(keyboard);
         mKeyDetector.setKeyboard(
                 keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
-        mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth());
+        mKeyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth);
         PointerTracker.setKeyDetector(mKeyDetector);
         mPopupPanelCache.clear();
     }
@@ -348,7 +345,6 @@
     }
 
     // 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;
@@ -361,7 +357,7 @@
                 (PopupMiniKeyboardView)container.findViewById(R.id.mini_keyboard_view);
         final Keyboard parentKeyboard = getKeyboard();
         final Keyboard miniKeyboard = new MiniKeyboardBuilder(
-                this, parentKeyboard.getPopupKeyboardResId(), parentKey, parentKeyboard).build();
+                this, parentKeyboard.mPopupKeyboardResId, parentKey, parentKeyboard).build();
         miniKeyboardView.setKeyboard(miniKeyboard);
 
         container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
@@ -401,11 +397,10 @@
         mPopupPanel = popupPanel;
         mPopupPanelPointerTrackerId = tracker.mPointerId;
 
-        tracker.onLongPressed();
-        popupPanel.showPanel(this, parentKey, tracker, mPopupWindow);
+        popupPanel.showPopupPanel(this, parentKey, tracker, mPopupWindow);
         final int translatedX = popupPanel.translateX(tracker.getLastX());
         final int translatedY = popupPanel.translateY(tracker.getLastY());
-        tracker.onDownEvent(translatedX, translatedY, SystemClock.uptimeMillis(), popupPanel);
+        tracker.onShowPopupPanel(translatedX, translatedY, SystemClock.uptimeMillis(), popupPanel);
 
         invalidateAllKeys();
         return true;
@@ -547,11 +542,12 @@
     @Override
     public void closing() {
         super.closing();
-        dismissMiniKeyboard();
+        dismissPopupPanel();
         mPopupPanelCache.clear();
     }
 
-    public boolean dismissMiniKeyboard() {
+    @Override
+    public boolean dismissPopupPanel() {
         if (mPopupWindow != null && mPopupWindow.isShowing()) {
             mPopupWindow.dismiss();
             mPopupPanel = null;
@@ -563,14 +559,14 @@
     }
 
     public boolean handleBack() {
-        return dismissMiniKeyboard();
+        return dismissPopupPanel();
     }
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent event) {
+        // Drop non-hover touch events when touch exploration is enabled.
         if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
-            return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event)
-                    || super.dispatchTouchEvent(event);
+            return false;
         }
 
         return super.dispatchTouchEvent(event);
@@ -587,15 +583,22 @@
         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.
+    /**
+     * Receives hover events from the input framework. This method overrides
+     * View.dispatchHoverEvent(MotionEvent) on SDK version ICS or higher. On
+     * lower SDK versions, this method is never called.
+     *
+     * @param event The motion event to be dispatched.
+     * @return {@code true} if the event was handled by the view, {@code false}
+     *         otherwise
+     */
+    public boolean dispatchHoverEvent(MotionEvent event) {
         if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
             final PointerTracker tracker = getPointerTracker(0);
-            return AccessibleKeyboardViewProxy.getInstance().onHoverEvent(event, tracker);
+            return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
         }
 
+        // Reflection doesn't support calling superclass methods.
         return false;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index 617961b..dad37e7 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -23,6 +23,7 @@
 import android.view.MotionEvent;
 
 import com.android.inputmethod.deprecated.VoiceProxy;
+import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.Utils;
 
@@ -40,8 +41,6 @@
     private boolean mDisableDisambiguation;
     /** The distance threshold at which we start treating the touch session as a multi-touch */
     private int mJumpThresholdSquare = Integer.MAX_VALUE;
-    /** The y coordinate of the last row */
-    private int mLastRowY;
     private int mLastX;
     private int mLastY;
 
@@ -55,57 +54,60 @@
 
     @Override
     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.setKeyPreviewPopupEnabled(false, delay);
-        } else {
-            super.setKeyPreviewPopupEnabled(previewEnabled, delay);
+        final Keyboard keyboard = getKeyboard();
+        if (keyboard instanceof LatinKeyboard) {
+            final LatinKeyboard latinKeyboard = (LatinKeyboard)keyboard;
+            if (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard()) {
+                // Phone and number keyboard never shows popup preview.
+                super.setKeyPreviewPopupEnabled(false, delay);
+                return;
+            }
         }
+        super.setKeyPreviewPopupEnabled(previewEnabled, delay);
     }
 
     @Override
     public void setKeyboard(Keyboard newKeyboard) {
         super.setKeyboard(newKeyboard);
         // One-seventh of the keyboard width seems like a reasonable threshold
-        mJumpThresholdSquare = newKeyboard.getMinWidth() / 7;
-        mJumpThresholdSquare *= mJumpThresholdSquare;
-        // Assuming there are 4 rows, this is the coordinate of the last row
-        mLastRowY = (newKeyboard.getHeight() * 3) / 4;
-    }
-
-    private LatinKeyboard getLatinKeyboard() {
-        Keyboard keyboard = getKeyboard();
-        if (keyboard instanceof LatinKeyboard) {
-            return (LatinKeyboard)keyboard;
-        } else {
-            return null;
-        }
+        final int jumpThreshold = newKeyboard.mOccupiedWidth / 7;
+        mJumpThresholdSquare = jumpThreshold * jumpThreshold;
     }
 
     public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboard oldKeyboard) {
-        final LatinKeyboard currentKeyboard = getLatinKeyboard();
+        final Keyboard keyboard = getKeyboard();
         // We should not set text fade factor to the keyboard which does not display the language on
         // its spacebar.
-        if (currentKeyboard != null && currentKeyboard == oldKeyboard)
-            currentKeyboard.setSpacebarTextFadeFactor(fadeFactor, this);
+        if (keyboard instanceof LatinKeyboard && keyboard == oldKeyboard) {
+            ((LatinKeyboard)keyboard).setSpacebarTextFadeFactor(fadeFactor, this);
+        }
     }
 
     @Override
     protected boolean onLongPress(Key key, PointerTracker tracker) {
-        int primaryCode = key.mCode;
+        final int primaryCode = key.mCode;
+        final Keyboard keyboard = getKeyboard();
+        if (keyboard instanceof LatinKeyboard) {
+            final LatinKeyboard latinKeyboard = (LatinKeyboard) keyboard;
+            if (primaryCode == Keyboard.CODE_DIGIT0 && latinKeyboard.isPhoneKeyboard()) {
+                tracker.onLongPressed();
+                // Long pressing on 0 in phone number keypad gives you a '+'.
+                return invokeOnKey(Keyboard.CODE_PLUS);
+            }
+            if (primaryCode == Keyboard.CODE_SHIFT && latinKeyboard.isAlphaKeyboard()) {
+                tracker.onLongPressed();
+                return invokeOnKey(Keyboard.CODE_CAPSLOCK);
+            }
+        }
         if (primaryCode == Keyboard.CODE_SETTINGS || primaryCode == Keyboard.CODE_SPACE) {
-            tracker.onLongPressed();
             // Both long pressing settings key and space key invoke IME switcher dialog.
-            return invokeOnKey(Keyboard.CODE_SETTINGS_LONGPRESS);
-        } else if (primaryCode == '0' && getLatinKeyboard().isPhoneKeyboard()) {
-            tracker.onLongPressed();
-            // Long pressing on 0 in phone number keypad gives you a '+'.
-            return invokeOnKey('+');
-        } else if (primaryCode == Keyboard.CODE_SHIFT) {
-            tracker.onLongPressed();
-            return invokeOnKey(Keyboard.CODE_CAPSLOCK);
+            if (getKeyboardActionListener().onCustomRequest(
+                    LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
+                tracker.onLongPressed();
+                return true;
+            } else {
+                return super.onLongPress(key, tracker);
+            }
         } else {
             return super.onLongPress(key, tracker);
         }
@@ -127,7 +129,7 @@
      * the sudden moves subside, a DOWN event is simulated for the second key.
      * @param me the motion event
      * @return true if the event was consumed, so that it doesn't continue to be handled by
-     * KeyboardView.
+     * {@link LatinKeyboardBaseView}.
      */
     private boolean handleSuddenJump(MotionEvent me) {
         // If device has distinct multi touch panel, there is no need to check sudden jump.
@@ -157,11 +159,8 @@
         case MotionEvent.ACTION_MOVE:
             // Is this a big jump?
             final int distanceSquare = (mLastX - x) * (mLastX - x) + (mLastY - y) * (mLastY - y);
-            // Check the distance and also if the move is not entirely within the bottom row
-            // If it's only in the bottom row, it might be an intentional slide gesture
-            // for language switching
-            if (distanceSquare > mJumpThresholdSquare
-                    && (mLastY < mLastRowY || y < mLastRowY)) {
+            // Check the distance.
+            if (distanceSquare > mJumpThresholdSquare) {
                 // If we're not yet dropping events, start dropping and send an UP event
                 if (!mDroppingEvents) {
                     mDroppingEvents = true;
@@ -201,7 +200,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent me) {
-        if (getLatinKeyboard() == null) return true;
+        if (getKeyboard() == null) return true;
 
         // If there was a sudden jump, return without processing the actual motion event.
         if (handleSuddenJump(me)) {
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
index 44f9f19..08e7d7e 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
@@ -16,28 +16,14 @@
 
 package com.android.inputmethod.keyboard;
 
-import android.content.Context;
+import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder.MiniKeyboardParams;
 
 public class MiniKeyboard extends Keyboard {
-    private int mDefaultKeyCoordX;
+    private final int mDefaultKeyCoordX;
 
-    public MiniKeyboard(Context context, int xmlLayoutResId, Keyboard parentKeyboard) {
-        super(context, xmlLayoutResId, parentKeyboard.mId.cloneAsMiniKeyboard(),
-                parentKeyboard.getMinWidth());
-        // HACK: Current mini keyboard design totally relies on the 9-patch padding about horizontal
-        // and vertical key spacing. To keep the visual of mini keyboard as is, these hacks are
-        // needed to keep having the same horizontal and vertical key spacing.
-        setHorizontalGap(0);
-        setVerticalGap(parentKeyboard.getVerticalGap() / 2);
-
-        // TODO: When we have correctly padded key background 9-patch drawables for mini keyboard,
-        // revert the above hacks and uncomment the following lines.
-        //setHorizontalGap(parentKeyboard.getHorizontalGap());
-        //setVerticalGap(parentKeyboard.getVerticalGap());
-    }
-
-    public void setDefaultCoordX(int pos) {
-        mDefaultKeyCoordX = pos;
+    public MiniKeyboard(MiniKeyboardParams params) {
+        super(params);
+        mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2;
     }
 
     public int getDefaultCoordX() {
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
index 1ec0dda..84bd44c 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
@@ -37,7 +37,7 @@
 
     @Override
     public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
-        final List<Key> keys = getKeyboard().getKeys();
+        final List<Key> keys = getKeyboard().mKeys;
         final int touchX = getTouchX(x);
         final int touchY = getTouchY(y);
 
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 8d7496c..1f8119a 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -19,11 +19,11 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.util.Log;
+import android.widget.TextView;
 
 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.ArrayList;
 import java.util.Arrays;
@@ -65,9 +65,11 @@
 
     public interface DrawingProxy {
         public void invalidateKey(Key key);
+        public TextView inflateKeyPreviewText();
         public void showKeyPreview(int keyIndex, PointerTracker tracker);
         public void cancelShowKeyPreview(PointerTracker tracker);
         public void dismissKeyPreview(PointerTracker tracker);
+        public boolean dismissPopupPanel();
     }
 
     public interface TimerProxy {
@@ -100,6 +102,7 @@
     private Keyboard mKeyboard;
     private List<Key> mKeys;
     private int mKeyQuarterWidthSquared;
+    private final TextView mKeyPreviewText;
 
     // The position and time at which first down event occurred.
     private long mDownTime;
@@ -118,9 +121,12 @@
     // true if keyboard layout has been changed.
     private boolean mKeyboardLayoutHasBeenChanged;
 
-    // true if event is already translated to a key action (long press or mini-keyboard)
+    // true if event is already translated to a key action.
     private boolean mKeyAlreadyProcessed;
 
+    // true if this pointer has been long-pressed and is showing a popup panel.
+    private boolean mIsShowingPopupPanel;
+
     // true if this pointer is repeatable key
     private boolean mIsRepeatableKey;
 
@@ -133,12 +139,6 @@
     // 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 static SubtypeSwitcher sSubtypeSwitcher;
-
     // Empty {@link KeyboardActionListener}
     private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener() {
         @Override
@@ -151,6 +151,8 @@
         public void onTextInput(CharSequence text) {}
         @Override
         public void onCancelInput() {}
+        @Override
+        public boolean onCustomRequest(int requestCode) { return false; }
     };
 
     public static void init(boolean hasDistinctMultitouch, Context context) {
@@ -172,7 +174,6 @@
         sTouchNoiseThresholdDistanceSquared = (int)(
                 touchNoiseThresholdDistance * touchNoiseThresholdDistance);
         sKeyboardSwitcher = KeyboardSwitcher.getInstance();
-        sSubtypeSwitcher = SubtypeSwitcher.getInstance();
     }
 
     public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) {
@@ -207,8 +208,7 @@
 
     public static void dismissAllKeyPreviews() {
         for (final PointerTracker tracker : sTrackers) {
-            tracker.setReleasedKeyGraphics();
-            tracker.dismissKeyPreview();
+            tracker.setReleasedKeyGraphics(tracker.mKeyIndex);
         }
     }
 
@@ -220,6 +220,11 @@
         mListener = handler.getKeyboardActionListener();
         mDrawingProxy = handler.getDrawingProxy();
         mTimerProxy = handler.getTimerProxy();
+        mKeyPreviewText = mDrawingProxy.inflateKeyPreviewText();
+    }
+
+    public TextView getKeyPreviewText() {
+        return mKeyPreviewText;
     }
 
     // Returns true if keyboard has been changed by this callback.
@@ -282,8 +287,8 @@
     public void setKeyDetectorInner(KeyDetector keyDetector) {
         mKeyDetector = keyDetector;
         mKeyboard = keyDetector.getKeyboard();
-        mKeys = mKeyboard.getKeys();
-        final int keyQuarterWidth = mKeyboard.getKeyWidth() / 4;
+        mKeys = mKeyboard.mKeys;
+        final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4;
         mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
     }
 
@@ -326,18 +331,10 @@
         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 setReleasedKeyGraphics() {
-        setReleasedKeyGraphics(mKeyIndex);
-    }
-
     private void setReleasedKeyGraphics(int keyIndex) {
+        mDrawingProxy.dismissKeyPreview(this);
         final Key key = getKey(keyIndex);
-        if (key != null) {
+        if (key != null && key.isEnabled()) {
             key.onReleased();
             mDrawingProxy.invalidateKey(key);
         }
@@ -346,11 +343,23 @@
     private void setPressedKeyGraphics(int keyIndex) {
         final Key key = getKey(keyIndex);
         if (key != null && key.isEnabled()) {
+            if (isKeyPreviewRequired(key)) {
+                mDrawingProxy.showKeyPreview(keyIndex, this);
+            }
             key.onPressed();
             mDrawingProxy.invalidateKey(key);
         }
     }
 
+    // The modifier key, such as shift key, should not show its key preview.
+    private static boolean isKeyPreviewRequired(Key key) {
+        final int code = key.mCode;
+        if (isModifierCode(code) || code == Keyboard.CODE_DELETE
+                || code == Keyboard.CODE_ENTER || code == Keyboard.CODE_SPACE)
+            return false;
+        return true;
+    }
+
     public int getLastX() {
         return mLastX;
     }
@@ -436,7 +445,6 @@
         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
@@ -447,7 +455,6 @@
 
             startRepeatKey(keyIndex);
             startLongPressTimer(keyIndex);
-            showKeyPreview(keyIndex);
             setPressedKeyGraphics(keyIndex);
         }
     }
@@ -464,12 +471,6 @@
         if (mKeyAlreadyProcessed)
             return;
 
-        // TODO: Remove this hacking code
-        if (mIsInSlidingLanguageSwitch) {
-            ((LatinKeyboard)mKeyboard).updateSpacebarPreviewIcon(x - mKeyX);
-            showKeyPreview(mSpaceKeyIndex);
-            return;
-        }
         final int lastX = mLastX;
         final int lastY = mLastY;
         final int oldKeyIndex = mKeyIndex;
@@ -486,7 +487,6 @@
                     keyIndex = onMoveKey(x, y);
                 onMoveToNewKey(keyIndex, x, y);
                 startLongPressTimer(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
@@ -506,7 +506,6 @@
                     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
@@ -518,35 +517,14 @@
                         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, true);
+                        onUpEventInternal(lastX, lastY, eventTime);
                         onDownEventInternal(x, y, eventTime);
                     } else {
                         mKeyAlreadyProcessed = true;
-                        dismissKeyPreview();
                         setReleasedKeyGraphics(oldKeyIndex);
                     }
                 }
             }
-            // TODO: Remove this hack code
-            else if (isSpaceKey(keyIndex) && !mIsInSlidingLanguageSwitch
-                    && mKeyboard instanceof LatinKeyboard) {
-                final LatinKeyboard keyboard = ((LatinKeyboard)mKeyboard);
-                if (sSubtypeSwitcher.useSpacebarLanguageSwitcher()
-                        && sSubtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
-                    final int diff = x - mKeyX;
-                    if (keyboard.shouldTriggerSpacebarSlidingLanguageSwitch(diff)) {
-                        // Detect start sliding language switch.
-                        mIsInSlidingLanguageSwitch = true;
-                        mSpaceKeyIndex = keyIndex;
-                        keyboard.updateSpacebarPreviewIcon(diff);
-                        // Display spacebar slide language switcher.
-                        showKeyPreview(keyIndex);
-                        final PointerTrackerQueue queue = sPointerTrackerQueue;
-                        if (queue != null)
-                            queue.releaseAllPointersExcept(this, eventTime, true);
-                    }
-                }
-            }
         } else {
             if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, keyIndex)) {
                 // The pointer has been slid out from the previous key, we must call onRelease() to
@@ -559,7 +537,6 @@
                     onMoveToNewKey(keyIndex, x, y);
                 } else {
                     mKeyAlreadyProcessed = true;
-                    dismissKeyPreview();
                 }
             }
         }
@@ -574,27 +551,26 @@
             if (isModifier()) {
                 // Before processing an up event of modifier key, all pointers already being
                 // tracked should be released.
-                queue.releaseAllPointersExcept(this, eventTime, true);
+                queue.releaseAllPointersExcept(this, eventTime);
             } else {
                 queue.releaseAllPointersOlderThan(this, eventTime);
             }
             queue.remove(this);
         }
-        onUpEventInternal(x, y, eventTime, true);
+        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) {
+    public void onPhantomUpEvent(int x, int y, long eventTime) {
         if (DEBUG_EVENT)
             printTouchEvent("onPhntEvent:", x, y, eventTime);
-        onUpEventInternal(x, y, eventTime, updateReleasedKeyGraphics);
+        onUpEventInternal(x, y, eventTime);
         mKeyAlreadyProcessed = true;
     }
 
-    private void onUpEventInternal(int x, int y, long eventTime,
-            boolean updateReleasedKeyGraphics) {
+    private void onUpEventInternal(int x, int y, long eventTime) {
         mTimerProxy.cancelKeyTimers();
         mDrawingProxy.cancelShowKeyPreview(this);
         mIsInSlidingKeyInput = false;
@@ -608,34 +584,27 @@
             keyY = mKeyY;
         }
         final int keyIndex = onUpKey(keyX, keyY, eventTime);
-        dismissKeyPreview();
-        if (updateReleasedKeyGraphics)
-            setReleasedKeyGraphics(keyIndex);
+        setReleasedKeyGraphics(keyIndex);
+        if (mIsShowingPopupPanel) {
+            mDrawingProxy.dismissPopupPanel();
+            mIsShowingPopupPanel = false;
+        }
         if (mKeyAlreadyProcessed)
             return;
-        // 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, keyX, keyY);
         }
     }
 
+    public void onShowPopupPanel(int x, int y, long eventTime, KeyEventHandler handler) {
+        onLongPressed();
+        onDownEvent(x, y, eventTime, handler);
+        mIsShowingPopupPanel = true;
+    }
+
     public void onLongPressed() {
         mKeyAlreadyProcessed = true;
-        setReleasedKeyGraphics();
-        dismissKeyPreview();
+        setReleasedKeyGraphics(mKeyIndex);
         final PointerTrackerQueue queue = sPointerTrackerQueue;
         if (queue != null) {
             queue.remove(this);
@@ -648,7 +617,7 @@
 
         final PointerTrackerQueue queue = sPointerTrackerQueue;
         if (queue != null) {
-            queue.releaseAllPointersExcept(this, eventTime, true);
+            queue.releaseAllPointersExcept(this, eventTime);
             queue.remove(this);
         }
         onCancelEventInternal();
@@ -657,15 +626,17 @@
     private void onCancelEventInternal() {
         mTimerProxy.cancelKeyTimers();
         mDrawingProxy.cancelShowKeyPreview(this);
-        dismissKeyPreview();
         setReleasedKeyGraphics(mKeyIndex);
         mIsInSlidingKeyInput = false;
+        if (mIsShowingPopupPanel) {
+            mDrawingProxy.dismissPopupPanel();
+            mIsShowingPopupPanel = false;
+        }
     }
 
     private void startRepeatKey(int keyIndex) {
         final Key key = getKey(keyIndex);
         if (key != null && key.mRepeatable) {
-            dismissKeyPreview();
             onRepeatKey(keyIndex);
             mTimerProxy.startKeyRepeatTimer(sDelayBeforeKeyRepeatStart, keyIndex, this);
             mIsRepeatableKey = true;
@@ -695,31 +666,9 @@
         }
     }
 
-    // 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;
-        mDrawingProxy.showKeyPreview(keyIndex, this);
-    }
-
-    private void dismissKeyPreview() {
-        mDrawingProxy.dismissKeyPreview(this);
-    }
-
     private void startLongPressTimer(int keyIndex) {
         Key key = getKey(keyIndex);
+        if (key == null) return;
         if (key.mCode == Keyboard.CODE_SHIFT) {
             if (sLongPressShiftKeyTimeout > 0) {
                 mTimerProxy.startLongPressTimer(sLongPressShiftKeyTimeout, keyIndex, this);
diff --git a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
index 5ab44d0..fb932e3 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
@@ -59,19 +59,16 @@
         public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
             mParentKeyboardView.getKeyboardActionListener()
                     .onCodeInput(primaryCode, keyCodes, x, y);
-            mParentKeyboardView.dismissMiniKeyboard();
         }
 
         @Override
         public void onTextInput(CharSequence text) {
             mParentKeyboardView.getKeyboardActionListener().onTextInput(text);
-            mParentKeyboardView.dismissMiniKeyboard();
         }
 
         @Override
         public void onCancelInput() {
             mParentKeyboardView.getKeyboardActionListener().onCancelInput();
-            mParentKeyboardView.dismissMiniKeyboard();
         }
 
         @Override
@@ -82,6 +79,8 @@
         public void onRelease(int primaryCode, boolean withSliding) {
             mParentKeyboardView.getKeyboardActionListener().onRelease(primaryCode, withSliding);
         }
+        @Override
+        public boolean onCustomRequest(int requestCode) { return false; }
     };
 
     public PopupMiniKeyboardView(Context context, AttributeSet attrs) {
@@ -108,6 +107,18 @@
     }
 
     @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final Keyboard keyboard = getKeyboard();
+        if (keyboard != null) {
+            final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
+            final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
+            setMeasuredDimension(width, height);
+        } else {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    @Override
     public void setKeyboard(Keyboard keyboard) {
         super.setKeyboard(keyboard);
         mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
@@ -147,7 +158,7 @@
     }
 
     @Override
-    public void showPanel(LatinKeyboardBaseView parentKeyboardView, Key parentKey,
+    public void showPopupPanel(LatinKeyboardBaseView parentKeyboardView, Key parentKey,
             PointerTracker tracker, PopupWindow window) {
         mParentKeyboardView = parentKeyboardView;
         final View container = (View)getParent();
@@ -161,9 +172,9 @@
         final int miniKeyboardLeft = pointX - miniKeyboard.getDefaultCoordX()
                 + parentKeyboardView.getPaddingLeft();
         final int x = Math.max(0, Math.min(miniKeyboardLeft,
-                parentKeyboardView.getWidth() - miniKeyboard.getMinWidth()))
+                parentKeyboardView.getWidth() - miniKeyboard.mOccupiedWidth))
                 - container.getPaddingLeft() + mCoordinates[0];
-        final int y = pointY - parentKeyboard.getVerticalGap()
+        final int y = pointY - parentKeyboard.mVerticalGap
                 - (container.getMeasuredHeight() - container.getPaddingBottom())
                 + parentKeyboardView.getPaddingTop() + mCoordinates[1];
 
@@ -180,6 +191,11 @@
     }
 
     @Override
+    public boolean dismissPopupPanel() {
+        return mParentKeyboardView.dismissPopupPanel();
+    }
+
+    @Override
     public int translateX(int x) {
         return x - mOriginX;
     }
diff --git a/java/src/com/android/inputmethod/keyboard/PopupPanel.java b/java/src/com/android/inputmethod/keyboard/PopupPanel.java
index f94d1c5..dc526e7 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupPanel.java
+++ b/java/src/com/android/inputmethod/keyboard/PopupPanel.java
@@ -26,7 +26,7 @@
      * @param tracker the pointer tracker that pressesd the parent key
      * @param window PopupWindow to be used to show this popup panel
      */
-    public void showPanel(LatinKeyboardBaseView parentKeyboardView, Key parentKey,
+    public void showPopupPanel(LatinKeyboardBaseView parentKeyboardView, Key parentKey,
             PointerTracker tracker, PopupWindow window);
 
     /**
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index aadedc6..5e73d63 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -17,8 +17,10 @@
 package com.android.inputmethod.keyboard;
 
 import com.android.inputmethod.latin.Utils;
+import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 public class ProximityInfo {
@@ -54,6 +56,19 @@
         computeNearestNeighbors(keyWidth, keys);
     }
 
+    public static ProximityInfo getDummyProximityInfo() {
+        return new ProximityInfo(1, 1, 1, 1, 1, Collections.<Key>emptyList());
+    }
+
+    public static ProximityInfo getSpellCheckerProximityInfo() {
+        final ProximityInfo spellCheckerProximityInfo = getDummyProximityInfo();
+        spellCheckerProximityInfo.mNativeProximityInfo =
+                spellCheckerProximityInfo.setProximityInfoNative(
+                        SpellCheckerProximityInfo.ROW_SIZE,
+                        480, 300, 10, 3, SpellCheckerProximityInfo.PROXIMITY);
+        return spellCheckerProximityInfo;
+    }
+
     private int mNativeProximityInfo;
     static {
         Utils.loadNativeLibrary();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
index 30d9692..c0dba41 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
@@ -20,7 +20,7 @@
 import android.content.res.XmlResourceParser;
 import android.util.Log;
 
-import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder.ParseException;
 import com.android.inputmethod.latin.R;
 
 import java.util.ArrayList;
@@ -212,19 +212,19 @@
 
     public void parseKeyStyleAttributes(TypedArray keyStyleAttr, TypedArray keyAttrs,
             XmlResourceParser parser) {
-        String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
+        final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
         if (DEBUG) Log.d(TAG, String.format("<%s styleName=%s />",
-                KeyboardParser.TAG_KEY_STYLE, styleName));
+                KeyboardBuilder.TAG_KEY_STYLE, styleName));
         if (mStyles.containsKey(styleName))
             throw new ParseException("duplicate key style declared: " + styleName, parser);
 
         final DeclaredKeyStyle style = new DeclaredKeyStyle();
         if (keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_parentStyle)) {
-            String parentStyle = keyStyleAttr.getString(
+            final String parentStyle = keyStyleAttr.getString(
                     R.styleable.Keyboard_KeyStyle_parentStyle);
             final DeclaredKeyStyle parent = mStyles.get(parentStyle);
             if (parent == null)
-                throw new ParseException("Unknown parentStyle " + parent, parser);
+                throw new ParseException("Unknown parentStyle " + parentStyle, parser);
             style.addParent(parent);
         }
         style.parseKeyStyleAttributes(keyAttrs);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
similarity index 77%
rename from java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
rename to java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index e35db89..de04ecd 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -20,6 +20,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
 import android.util.Xml;
@@ -36,12 +37,11 @@
 
 import java.io.IOException;
 import java.util.Arrays;
-import java.util.List;
 
 /**
- * Parser for BaseKeyboard.
+ * Keyboard Building helper.
  *
- * This class parses Keyboard XML file and fill out keys in Keyboard.
+ * This class parses Keyboard XML file and eventually build a Keyboard.
  * The Keyboard XML file looks like:
  * <pre>
  *   &gt;!-- xml/keyboard.xml --&lt;
@@ -107,8 +107,8 @@
  * </pre>
  */
 
-public class KeyboardParser {
-    private static final String TAG = KeyboardParser.class.getSimpleName();
+public class KeyboardBuilder<KP extends KeyboardParams> {
+    private static final String TAG = KeyboardBuilder.class.getSimpleName();
     private static final boolean DEBUG = false;
 
     // Keyboard XML Tags
@@ -123,38 +123,52 @@
     private static final String TAG_DEFAULT = "default";
     public static final String TAG_KEY_STYLE = "key-style";
 
-    private final Keyboard mKeyboard;
-    private final Context mContext;
-    private final Resources mResources;
+    protected final KP mParams;
+    protected final Context mContext;
+    protected final Resources mResources;
+    private final DisplayMetrics mDisplayMetrics;
 
-    private int mKeyboardTopPadding;
-    private int mKeyboardBottomPadding;
-    private int mHorizontalEdgesPadding;
     private int mCurrentX = 0;
     private int mCurrentY = 0;
-    private int mMaxRowWidth = 0;
-    private int mTotalHeight = 0;
     private Row mCurrentRow = null;
+    private boolean mLeftEdge;
+    private Key mRightEdgeKey = null;
     private final KeyStyles mKeyStyles = new KeyStyles();
 
-    public KeyboardParser(Keyboard keyboard, Context context) {
-        mKeyboard = keyboard;
+    public KeyboardBuilder(Context context, KP params) {
         mContext = context;
         final Resources res = context.getResources();
         mResources = res;
-        mHorizontalEdgesPadding = (int)res.getDimension(R.dimen.keyboard_horizontal_edges_padding);
+        mDisplayMetrics = res.getDisplayMetrics();
+
+        mParams = params;
+        mParams.mHorizontalEdgesPadding = (int)res.getDimension(
+                R.dimen.keyboard_horizontal_edges_padding);
+
+        mParams.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
+        mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
     }
 
-    public int getMaxRowWidth() {
-        return mMaxRowWidth;
+    public KeyboardBuilder<KP> load(KeyboardId id) {
+        mParams.mId = id;
+        try {
+            parseKeyboard(id.getXmlId());
+        } catch (XmlPullParserException e) {
+            Log.w(TAG, "keyboard XML parse error: " + e);
+            throw new IllegalArgumentException(e);
+        } catch (IOException e) {
+            Log.w(TAG, "keyboard XML parse error: " + e);
+            throw new RuntimeException(e);
+        }
+        return this;
     }
 
-    public int getTotalHeight() {
-        return mTotalHeight;
+    public Keyboard build() {
+        return new Keyboard(mParams);
     }
 
-    public void parseKeyboard(int resId) throws XmlPullParserException, IOException {
-        if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mKeyboard.mId));
+    private void parseKeyboard(int resId) throws XmlPullParserException, IOException {
+        if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId));
         final XmlResourceParser parser = mResources.getXml(resId);
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -163,7 +177,7 @@
                 if (TAG_KEYBOARD.equals(tag)) {
                     parseKeyboardAttributes(parser);
                     startKeyboard();
-                    parseKeyboardContent(parser, mKeyboard.getKeys());
+                    parseKeyboardContent(parser, false);
                     break;
                 } else {
                     throw new IllegalStartTag(parser, TAG_KEYBOARD);
@@ -194,15 +208,14 @@
     }
 
     private void parseKeyboardAttributes(XmlResourceParser parser) {
-        final Keyboard keyboard = mKeyboard;
-        final int displayWidth = keyboard.getDisplayWidth();
+        final int displayWidth = mDisplayMetrics.widthPixels;
         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 {
-            final int displayHeight = keyboard.getDisplayHeight();
+            final int displayHeight = mDisplayMetrics.heightPixels;
             final int keyboardHeight = (int)keyboardAttr.getDimension(
                     R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
             final int maxKeyboardHeight = getDimensionOrFraction(keyboardAttr,
@@ -217,60 +230,67 @@
             }
             // Keyboard height will not exceed maxKeyboardHeight and will not be less than
             // minKeyboardHeight.
-            final int height = Math.max(
+            mParams.mOccupiedHeight = Math.max(
                     Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
+            mParams.mOccupiedWidth = mParams.mId.mWidth;
+            mParams.mTopPadding = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_keyboardTopPadding, mParams.mOccupiedHeight, 0);
+            mParams.mBottomPadding = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_keyboardBottomPadding, mParams.mOccupiedHeight, 0);
 
+            final int height = mParams.mOccupiedHeight;
+            final int width = mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding * 2
+                    - mParams.mHorizontalCenterPadding;
+            mParams.mHeight = height;
+            mParams.mWidth = width;
+            mParams.mDefaultKeyWidth = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_keyWidth, width, width / 10);
+            mParams.mDefaultRowHeight = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_rowHeight, height, height / 4);
+            mParams.mHorizontalGap = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_horizontalGap, width, 0);
+            mParams.mVerticalGap = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_verticalGap, height, 0);
 
-            keyboard.setKeyboardHeight(height);
-            keyboard.setKeyWidth(getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_keyWidth, displayWidth, displayWidth / 10));
-            keyboard.setRowHeight(getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_rowHeight, height, 50));
-            keyboard.setHorizontalGap(getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_horizontalGap, displayWidth, 0));
-            keyboard.setVerticalGap(getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_verticalGap, height, 0));
-            keyboard.setPopupKeyboardResId(keyboardAttr.getResourceId(
-                    R.styleable.Keyboard_popupKeyboardTemplate, 0));
-            keyboard.setMaxPopupKeyboardColumn(keyAttr.getInt(
-                    R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5));
+            mParams.mIsRtlKeyboard = keyboardAttr.getBoolean(
+                    R.styleable.Keyboard_isRtlKeyboard, false);
+            mParams.mPopupKeyboardResId = keyboardAttr.getResourceId(
+                    R.styleable.Keyboard_popupKeyboardTemplate, 0);
+            mParams.mMaxPopupColumn = keyAttr.getInt(
+                    R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5);
 
-            mKeyboard.mIconsSet.loadIcons(keyboardAttr);
-            mKeyboardTopPadding = getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_keyboardTopPadding, height, 0);
-            mKeyboardBottomPadding = getDimensionOrFraction(keyboardAttr,
-                    R.styleable.Keyboard_keyboardBottomPadding, height, 0);
+            mParams.mIconsSet.loadIcons(keyboardAttr);
         } finally {
             keyAttr.recycle();
             keyboardAttr.recycle();
         }
     }
 
-    private void parseKeyboardContent(XmlResourceParser parser, List<Key> keys)
+    private void parseKeyboardContent(XmlResourceParser parser, boolean skip)
             throws XmlPullParserException, IOException {
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_ROW.equals(tag)) {
-                    Row row = new Row(mResources, mKeyboard, parser);
+                    Row row = parseRowAttributes(parser);
                     if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW));
-                    if (keys != null)
+                    if (!skip)
                         startRow(row);
-                    parseRowContent(parser, row, keys);
+                    parseRowContent(parser, row, skip);
                 } else if (TAG_INCLUDE.equals(tag)) {
-                    parseIncludeKeyboardContent(parser, keys);
+                    parseIncludeKeyboardContent(parser, skip);
                 } else if (TAG_SWITCH.equals(tag)) {
-                    parseSwitchKeyboardContent(parser, keys);
+                    parseSwitchKeyboardContent(parser, skip);
                 } else if (TAG_KEY_STYLE.equals(tag)) {
-                    parseKeyStyle(parser, keys);
+                    parseKeyStyle(parser, skip);
                 } else {
                     throw new IllegalStartTag(parser, TAG_ROW);
                 }
             } else if (event == XmlPullParser.END_TAG) {
                 final String tag = parser.getName();
                 if (TAG_KEYBOARD.equals(tag)) {
-                    endKeyboard(mKeyboard.getVerticalGap());
+                    endKeyboard();
                     break;
                 } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
                         || TAG_MERGE.equals(tag)) {
@@ -285,22 +305,36 @@
         }
     }
 
-    private void parseRowContent(XmlResourceParser parser, Row row, List<Key> keys)
+    private Row parseRowAttributes(XmlResourceParser parser) {
+        final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+                R.styleable.Keyboard);
+        try {
+            if (a.hasValue(R.styleable.Keyboard_horizontalGap))
+                throw new IllegalAttribute(parser, "horizontalGap");
+            if (a.hasValue(R.styleable.Keyboard_verticalGap))
+                throw new IllegalAttribute(parser, "verticalGap");
+            return new Row(mResources, mParams, parser);
+        } finally {
+            a.recycle();
+        }
+    }
+
+    private void parseRowContent(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_KEY.equals(tag)) {
-                    parseKey(parser, row, keys);
+                    parseKey(parser, row, skip);
                 } else if (TAG_SPACER.equals(tag)) {
-                    parseSpacer(parser, row, keys);
+                    parseSpacer(parser, row, skip);
                 } else if (TAG_INCLUDE.equals(tag)) {
-                    parseIncludeRowContent(parser, row, keys);
+                    parseIncludeRowContent(parser, row, skip);
                 } else if (TAG_SWITCH.equals(tag)) {
-                    parseSwitchRowContent(parser, row, keys);
+                    parseSwitchRowContent(parser, row, skip);
                 } else if (TAG_KEY_STYLE.equals(tag)) {
-                    parseKeyStyle(parser, keys);
+                    parseKeyStyle(parser, skip);
                 } else {
                     throw new IllegalStartTag(parser, TAG_KEY);
                 }
@@ -308,7 +342,7 @@
                 final String tag = parser.getName();
                 if (TAG_ROW.equals(tag)) {
                     if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW));
-                    if (keys != null)
+                    if (!skip)
                         endRow();
                     break;
                 } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
@@ -324,26 +358,24 @@
         }
     }
 
-    private void parseKey(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseKey(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
-        if (keys == null) {
+        if (skip) {
             checkEndTag(TAG_KEY, parser);
         } else {
-            Key key = new Key(mResources, row, mCurrentX, mCurrentY, parser, mKeyStyles);
+            Key key = new Key(mResources, mParams, row, mCurrentX, mCurrentY, parser, mKeyStyles);
             if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d popupCharacters=%s />",
                     TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode,
                     Arrays.toString(key.mPopupCharacters)));
             checkEndTag(TAG_KEY, parser);
-            keys.add(key);
-            if (key.mCode == Keyboard.CODE_SHIFT)
-                mKeyboard.getShiftKeys().add(key);
+            mParams.onAddKey(key);
             endKey(key);
         }
     }
 
-    private void parseSpacer(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseSpacer(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
-        if (keys == null) {
+        if (skip) {
             checkEndTag(TAG_SPACER, parser);
         } else {
             if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER));
@@ -351,14 +383,14 @@
                     R.styleable.Keyboard);
             if (keyboardAttr.hasValue(R.styleable.Keyboard_horizontalGap))
                 throw new IllegalAttribute(parser, "horizontalGap");
-            final int keyboardWidth = mKeyboard.getDisplayWidth();
+            final int keyboardWidth = mParams.mWidth;
             final int keyWidth = getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_keyWidth,
-                    keyboardWidth, row.mDefaultWidth);
+                    keyboardWidth, row.mDefaultKeyWidth);
             keyboardAttr.recycle();
 
             final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                     R.styleable.Keyboard_Key);
-            int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr,
+            int keyXPos = KeyboardBuilder.getDimensionOrFraction(keyAttr,
                     R.styleable.Keyboard_Key_keyXPos, keyboardWidth, mCurrentX);
             if (keyXPos < 0) {
                 // If keyXPos is negative, the actual x-coordinate will be display_width + keyXPos.
@@ -370,19 +402,19 @@
         }
     }
 
-    private void parseIncludeKeyboardContent(XmlResourceParser parser, List<Key> keys)
+    private void parseIncludeKeyboardContent(XmlResourceParser parser, boolean skip)
             throws XmlPullParserException, IOException {
-        parseIncludeInternal(parser, null, keys);
+        parseIncludeInternal(parser, null, skip);
     }
 
-    private void parseIncludeRowContent(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseIncludeRowContent(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
-        parseIncludeInternal(parser, row, keys);
+        parseIncludeInternal(parser, row, skip);
     }
 
-    private void parseIncludeInternal(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseIncludeInternal(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
-        if (keys == null) {
+        if (skip) {
             checkEndTag(TAG_INCLUDE, parser);
         } else {
             final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
@@ -396,11 +428,11 @@
                 throw new ParseException("No keyboardLayout attribute in <include/>", parser);
             if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />",
                     TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout)));
-            parseMerge(mResources.getLayout(keyboardLayout), row, keys);
+            parseMerge(mResources.getLayout(keyboardLayout), row, skip);
         }
     }
 
-    private void parseMerge(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseMerge(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -408,9 +440,9 @@
                 final String tag = parser.getName();
                 if (TAG_MERGE.equals(tag)) {
                     if (row == null) {
-                        parseKeyboardContent(parser, keys);
+                        parseKeyboardContent(parser, skip);
                     } else {
-                        parseRowContent(parser, row, keys);
+                        parseRowContent(parser, row, skip);
                     }
                     break;
                 } else {
@@ -421,28 +453,28 @@
         }
     }
 
-    private void parseSwitchKeyboardContent(XmlResourceParser parser, List<Key> keys)
+    private void parseSwitchKeyboardContent(XmlResourceParser parser, boolean skip)
             throws XmlPullParserException, IOException {
-        parseSwitchInternal(parser, null, keys);
+        parseSwitchInternal(parser, null, skip);
     }
 
-    private void parseSwitchRowContent(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseSwitchRowContent(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
-        parseSwitchInternal(parser, row, keys);
+        parseSwitchInternal(parser, row, skip);
     }
 
-    private void parseSwitchInternal(XmlResourceParser parser, Row row, List<Key> keys)
+    private void parseSwitchInternal(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
-        if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mKeyboard.mId));
+        if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mParams.mId));
         boolean selected = false;
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_CASE.equals(tag)) {
-                    selected |= parseCase(parser, row, selected ? null : keys);
+                    selected |= parseCase(parser, row, selected ? true : skip);
                 } else if (TAG_DEFAULT.equals(tag)) {
-                    selected |= parseDefault(parser, row, selected ? null : keys);
+                    selected |= parseDefault(parser, row, selected ? true : skip);
                 } else {
                     throw new IllegalStartTag(parser, TAG_KEY);
                 }
@@ -458,21 +490,21 @@
         }
     }
 
-    private boolean parseCase(XmlResourceParser parser, Row row, List<Key> keys)
+    private boolean parseCase(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         final boolean selected = parseCaseCondition(parser);
         if (row == null) {
             // Processing Rows.
-            parseKeyboardContent(parser, selected ? keys : null);
+            parseKeyboardContent(parser, selected ? skip : true);
         } else {
             // Processing Keys.
-            parseRowContent(parser, row, selected ? keys : null);
+            parseRowContent(parser, row, selected ? skip : true);
         }
         return selected;
     }
 
     private boolean parseCaseCondition(XmlResourceParser parser) {
-        final KeyboardId id = mKeyboard.mId;
+        final KeyboardId id = mParams.mId;
         if (id == null)
             return true;
 
@@ -493,10 +525,10 @@
                     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 shortcutKeyEnabledMatched = matchBoolean(a,
+                    R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
+            final boolean hasShortcutKeyMatched = matchBoolean(a,
+                    R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
             // 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
@@ -511,7 +543,7 @@
                     R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
             final boolean selected = modeMatched && navigateActionMatched && passwordInputMatched
                     && hasSettingsKeyMatched && f2KeyModeMatched && clobberSettingsKeyMatched
-                    && voiceEnabledMatched && voiceKeyMatched && imeActionMatched &&
+                    && shortcutKeyEnabledMatched && hasShortcutKeyMatched && 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,
@@ -523,8 +555,9 @@
                             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"),
+                    booleanAttr(
+                            a, R.styleable.Keyboard_Case_shortcutKeyEnabled, "shortcutKeyEnabled"),
+                    booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"),
                     textAttr(EditorInfoCompatUtils.imeOptionsName(
                             a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"),
                     textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"),
@@ -580,18 +613,18 @@
         return false;
     }
 
-    private boolean parseDefault(XmlResourceParser parser, Row row, List<Key> keys)
+    private boolean parseDefault(XmlResourceParser parser, Row row, boolean skip)
             throws XmlPullParserException, IOException {
         if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT));
         if (row == null) {
-            parseKeyboardContent(parser, keys);
+            parseKeyboardContent(parser, skip);
         } else {
-            parseRowContent(parser, row, keys);
+            parseRowContent(parser, row, skip);
         }
         return true;
     }
 
-    private void parseKeyStyle(XmlResourceParser parser, List<Key> keys) {
+    private void parseKeyStyle(XmlResourceParser parser, boolean skip) {
         TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard_KeyStyle);
         TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
@@ -600,7 +633,7 @@
             if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
                 throw new ParseException("<" + TAG_KEY_STYLE
                         + "/> needs styleName attribute", parser);
-            if (keys != null)
+            if (!skip)
                 mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
         } finally {
             keyStyleAttr.recycle();
@@ -616,36 +649,45 @@
     }
 
     private void startKeyboard() {
-        mCurrentY += mKeyboardTopPadding;
+        mCurrentY += mParams.mTopPadding;
     }
 
     private void startRow(Row row) {
         mCurrentX = 0;
-        setSpacer(mCurrentX, mHorizontalEdgesPadding);
+        setSpacer(mCurrentX, mParams.mHorizontalEdgesPadding);
         mCurrentRow = row;
+        mLeftEdge = true;
+        mRightEdgeKey = null;
     }
 
     private void endRow() {
         if (mCurrentRow == null)
             throw new InflateException("orphant end row tag");
-        setSpacer(mCurrentX, mHorizontalEdgesPadding);
-        if (mCurrentX > mMaxRowWidth)
-            mMaxRowWidth = mCurrentX;
-        mCurrentY += mCurrentRow.mDefaultHeight;
+        if (mRightEdgeKey != null) {
+            mRightEdgeKey.addEdgeFlags(Keyboard.EDGE_RIGHT);
+            mRightEdgeKey = null;
+        }
+        setSpacer(mCurrentX, mParams.mHorizontalEdgesPadding);
+        mCurrentY += mCurrentRow.mRowHeight;
         mCurrentRow = null;
     }
 
     private void endKey(Key key) {
-        mCurrentX = key.mX - key.mGap / 2 + key.mWidth + key.mGap;
+        mCurrentX = key.mX - key.mHorizontalGap / 2 + key.mWidth + key.mHorizontalGap;
+        if (mLeftEdge) {
+            key.addEdgeFlags(Keyboard.EDGE_LEFT);
+            mLeftEdge = false;
+        }
+        mRightEdgeKey = key;
     }
 
-    private void endKeyboard(int defaultVerticalGap) {
-        mCurrentY += mKeyboardBottomPadding;
-        mTotalHeight = mCurrentY - defaultVerticalGap;
+    private void endKeyboard() {
     }
 
     private void setSpacer(int keyXPos, int width) {
         mCurrentX = keyXPos + width;
+        mLeftEdge = false;
+        mRightEdgeKey = null;
     }
 
     public static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
index 535a695..2d8b7bf 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
@@ -21,7 +21,6 @@
 import android.graphics.drawable.Drawable;
 import android.util.Log;
 
-import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.latin.R;
 
 public class KeyboardIconsSet {
@@ -31,44 +30,33 @@
 
     // 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_DELETE_RTL_KEY = 5;
-    private static final int ICON_SETTINGS_KEY = 6;
-    private static final int ICON_SHORTCUT_KEY = 7;
-    private static final int ICON_SPACE_KEY = 8;
-    private static final int ICON_RETURN_KEY = 9;
-    private static final int ICON_SEARCH_KEY = 10;
-    private static final int ICON_TAB_KEY = 11;
+    private static final int ICON_DELETE_KEY = 2;
+    private static final int ICON_SETTINGS_KEY = 3; // This is also represented as "@icon/3" in XML.
+    private static final int ICON_SPACE_KEY = 4;
+    private static final int ICON_RETURN_KEY = 5;
+    private static final int ICON_SEARCH_KEY = 6;
+    private static final int ICON_TAB_KEY = 7; // This is also represented as "@icon/7" in XML.
+    private static final int ICON_SHORTCUT_KEY = 8;
+    private static final int ICON_SHORTCUT_FOR_LABEL = 9;
     // This should be aligned with Keyboard.keyIconShifted enum.
-    private static final int ICON_SHIFTED_SHIFT_KEY = 12;
+    private static final int ICON_SHIFTED_SHIFT_KEY = 10;
     // This should be aligned with Keyboard.keyIconPreview enum.
-    private static final int ICON_PREVIEW_SPACE_KEY = 13;
-    private static final int ICON_PREVIEW_TAB_KEY = 14;
-    private static final int ICON_PREVIEW_SETTINGS_KEY = 15;
-    private static final int ICON_PREVIEW_SHORTCUT_KEY = 16;
+    private static final int ICON_PREVIEW_TAB_KEY = 11;
+    private static final int ICON_PREVIEW_SETTINGS_KEY = 12;
+    private static final int ICON_PREVIEW_SHORTCUT_KEY = 13;
 
-    private static final int ICON_LAST = 16;
+    private static final int ICON_LAST = 13;
 
     private final Drawable mIcons[] = new Drawable[ICON_LAST + 1];
 
-    private static final int getIconId(int attrIndex) {
+    private static final int getIconId(final 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_iconDeleteRtlKey:
-            return ICON_DELETE_RTL_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:
@@ -77,10 +65,12 @@
             return ICON_SEARCH_KEY;
         case R.styleable.Keyboard_iconTabKey:
             return ICON_TAB_KEY;
+        case R.styleable.Keyboard_iconShortcutKey:
+            return ICON_SHORTCUT_KEY;
+        case R.styleable.Keyboard_iconShortcutForLabel:
+            return ICON_SHORTCUT_FOR_LABEL;
         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:
@@ -92,16 +82,14 @@
         }
     }
 
-    public void loadIcons(TypedArray keyboardAttrs) {
+    public void loadIcons(final 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;
+                    mIcons[iconId] = setDefaultBounds(keyboardAttrs.getDrawable(attrIndex));
                 } catch (Resources.NotFoundException e) {
                     Log.w(TAG, "Drawable resource for icon #" + iconId + " not found");
                 }
@@ -109,11 +97,18 @@
         }
     }
 
-    public Drawable getIcon(int iconId) {
+    public Drawable getIcon(final 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];
     }
+
+    private static Drawable setDefaultBounds(final Drawable icon)  {
+        if (icon != null) {
+            icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
+        }
+        return icon;
+    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
new file mode 100644
index 0000000..4ccaa72
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
@@ -0,0 +1,95 @@
+/*
+ * 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.graphics.drawable.Drawable;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class KeyboardParams {
+    public KeyboardId mId;
+
+    public int mOccupiedHeight;
+    public int mOccupiedWidth;
+
+    public int mHeight;
+    public int mWidth;
+
+    public int mTopPadding;
+    public int mBottomPadding;
+    public int mHorizontalEdgesPadding;
+    public int mHorizontalCenterPadding;
+
+    public int mDefaultRowHeight;
+    public int mDefaultKeyWidth;
+    public int mHorizontalGap;
+    public int mVerticalGap;
+
+    public boolean mIsRtlKeyboard;
+    public int mPopupKeyboardResId;
+    public int mMaxPopupColumn;
+
+    public int GRID_WIDTH;
+    public int GRID_HEIGHT;
+
+    public final List<Key> mKeys = new ArrayList<Key>();
+    public final List<Key> mShiftKeys = new ArrayList<Key>();
+    public final Set<Key> mShiftLockKeys = new HashSet<Key>();
+    public final Map<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>();
+    public final Map<Key, Drawable> mUnshiftedIcons = new HashMap<Key, Drawable>();
+    public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
+
+    public int mMostCommonKeyWidth = 0;
+
+    public void onAddKey(Key key) {
+        mKeys.add(key);
+        updateHistogram(key);
+        if (key.mCode == Keyboard.CODE_SHIFT) {
+            mShiftKeys.add(key);
+            if (key.mSticky) {
+                mShiftLockKeys.add(key);
+            }
+        }
+    }
+
+    public void addShiftedIcon(Key key, Drawable icon) {
+        mUnshiftedIcons.put(key, key.getIcon());
+        mShiftedIcons.put(key, icon);
+    }
+
+    private int mMaxCount = 0;
+    private final Map<Integer, Integer> mHistogram = new HashMap<Integer, Integer>();
+
+    private void updateHistogram(Key key) {
+        final Integer width = key.mWidth + key.mHorizontalGap;
+        final int count = (mHistogram.containsKey(width) ? mHistogram.get(width) : 0) + 1;
+        mHistogram.put(width, count);
+        if (count > mMaxCount) {
+            mMaxCount = count;
+            mMostCommonKeyWidth = width;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
index cc89579..31a291c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
@@ -16,8 +16,6 @@
 
 package com.android.inputmethod.keyboard.internal;
 
-import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Paint;
 import android.graphics.Rect;
 
@@ -27,26 +25,30 @@
 import com.android.inputmethod.keyboard.MiniKeyboard;
 import com.android.inputmethod.latin.R;
 
-import java.util.List;
-
-public class MiniKeyboardBuilder {
-    private final Resources mRes;
-    private final MiniKeyboard mKeyboard;
+public class MiniKeyboardBuilder extends
+        KeyboardBuilder<MiniKeyboardBuilder.MiniKeyboardParams> {
     private final CharSequence[] mPopupCharacters;
-    private final MiniKeyboardLayoutParams mParams;
 
-    /* package */ static class MiniKeyboardLayoutParams {
-        public final int mKeyWidth;
-        public final int mRowHeight;
-        /* package */ final int mTopRowAdjustment;
-        public final int mNumRows;
-        public final int mNumColumns;
-        public final int mLeftKeys;
-        public final int mRightKeys; // includes default key.
-        public int mTopPadding;
+    public static class MiniKeyboardParams extends KeyboardParams {
+        /* package */ int mTopRowAdjustment;
+        public int mNumRows;
+        public int mNumColumns;
+        public int mLeftKeys;
+        public int mRightKeys; // includes default key.
+
+        public MiniKeyboardParams() {
+            super();
+        }
+
+        /* package for test */ MiniKeyboardParams(int numKeys, int maxColumns, int keyWidth,
+                int rowHeight, int coordXInParent, int parentKeyboardWidth) {
+            super();
+            setParameters(
+                    numKeys, maxColumns, keyWidth, rowHeight, coordXInParent, parentKeyboardWidth);
+        }
 
         /**
-         * The object holding mini keyboard layout parameters.
+         * Set keyboard parameters of mini keyboard.
          *
          * @param numKeys number of keys in this mini keyboard.
          * @param maxColumns number of maximum columns of this mini keyboard.
@@ -54,15 +56,15 @@
          * @param rowHeight mini keyboard row height in pixel, including vertical gap.
          * @param coordXInParent coordinate x of the popup key in parent keyboard.
          * @param parentKeyboardWidth parent keyboard width in pixel.
-         * parent keyboard.
          */
-        public MiniKeyboardLayoutParams(int numKeys, int maxColumns, int keyWidth, int rowHeight,
+        public void setParameters(int numKeys, int maxColumns, int keyWidth, int rowHeight,
                 int coordXInParent, int parentKeyboardWidth) {
-            if (parentKeyboardWidth / keyWidth < maxColumns)
+            if (parentKeyboardWidth / keyWidth < maxColumns) {
                 throw new IllegalArgumentException("Keyboard is too small to hold mini keyboard: "
                         + parentKeyboardWidth + " " + keyWidth + " " + maxColumns);
-            mKeyWidth = keyWidth;
-            mRowHeight = rowHeight;
+            }
+            mDefaultKeyWidth = keyWidth;
+            mDefaultRowHeight = rowHeight;
 
             final int numRows = (numKeys + maxColumns - 1) / maxColumns;
             mNumRows = numRows;
@@ -108,6 +110,9 @@
             } else {
                 mTopRowAdjustment = -1;
             }
+
+            mWidth = mOccupiedWidth = mNumColumns * mDefaultKeyWidth;
+            mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
         }
 
         // Return key position according to column count (0 is default).
@@ -160,19 +165,19 @@
         }
 
         public int getDefaultKeyCoordX() {
-            return mLeftKeys * mKeyWidth;
+            return mLeftKeys * mDefaultKeyWidth;
         }
 
         public int getX(int n, int row) {
-            final int x = getColumnPos(n) * mKeyWidth + getDefaultKeyCoordX();
+            final int x = getColumnPos(n) * mDefaultKeyWidth + getDefaultKeyCoordX();
             if (isTopRow(row)) {
-                return x + mTopRowAdjustment * (mKeyWidth / 2);
+                return x + mTopRowAdjustment * (mDefaultKeyWidth / 2);
             }
             return x;
         }
 
         public int getY(int row) {
-            return (mNumRows - 1 - row) * mRowHeight + mTopPadding;
+            return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
         }
 
         public int getRowFlags(int row) {
@@ -185,42 +190,32 @@
         private boolean isTopRow(int rowCount) {
             return rowCount == mNumRows - 1;
         }
-
-        public void setTopPadding (int topPadding) {
-            mTopPadding = topPadding;
-        }
-
-        public int getKeyboardHeight() {
-            return mNumRows * mRowHeight + mTopPadding;
-        }
-
-        public int getKeyboardWidth() {
-            return mNumColumns * mKeyWidth;
-        }
     }
 
-    public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key parentKey,
+    public MiniKeyboardBuilder(KeyboardView view, int xmlId, Key parentKey,
             Keyboard parentKeyboard) {
-        final Context context = view.getContext();
-        mRes = context.getResources();
-        final MiniKeyboard keyboard = new MiniKeyboard(
-                context, layoutTemplateResId, parentKeyboard);
-        mKeyboard = keyboard;
+        super(view.getContext(), new MiniKeyboardParams());
+        load(parentKeyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId));
+
+        // HACK: Current mini keyboard design totally relies on the 9-patch padding about horizontal
+        // and vertical key spacing. To keep the visual of mini keyboard as is, these hacks are
+        // needed to keep having the same horizontal and vertical key spacing.
+        mParams.mHorizontalGap = 0;
+        mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2;
+        // TODO: When we have correctly padded key background 9-patch drawables for mini keyboard,
+        // revert the above hacks and uncomment the following lines.
+        //mParams.mHorizontalGap = parentKeyboard.mHorizontalGap;
+        //mParams.mVerticalGap = parentKeyboard.mVerticalGap;
+
+        mParams.mIsRtlKeyboard = parentKeyboard.mIsRtlKeyboard;
         mPopupCharacters = parentKey.mPopupCharacters;
 
-        final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, keyboard.getKeyWidth());
-        final MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
+        final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, mParams.mDefaultKeyWidth);
+        mParams.setParameters(
                 mPopupCharacters.length, parentKey.mMaxPopupColumn,
-                keyWidth, parentKeyboard.getRowHeight(),
-                parentKey.mX + (parentKey.mWidth + parentKey.mGap) / 2 - keyWidth / 2,
+                keyWidth, parentKeyboard.mDefaultRowHeight,
+                parentKey.mX + (mParams.mDefaultKeyWidth - keyWidth) / 2,
                 view.getMeasuredWidth());
-        params.setTopPadding(keyboard.getVerticalGap());
-        mParams = params;
-
-        keyboard.setRowHeight(params.mRowHeight);
-        keyboard.setHeight(params.getKeyboardHeight());
-        keyboard.setMinWidth(params.getKeyboardWidth());
-        keyboard.setDefaultCoordX(params.getDefaultKeyCoordX() + params.mKeyWidth / 2);
     }
 
     private static int getMaxKeyWidth(KeyboardView view, CharSequence[] popupCharacters,
@@ -249,17 +244,16 @@
         return Math.max(minKeyWidth, maxWidth + horizontalPadding);
     }
 
+    @Override
     public MiniKeyboard build() {
-        final MiniKeyboard keyboard = mKeyboard;
-        final List<Key> keys = keyboard.getKeys();
-        final MiniKeyboardLayoutParams params = mParams;
+        final MiniKeyboardParams params = mParams;
         for (int n = 0; n < mPopupCharacters.length; n++) {
             final CharSequence label = mPopupCharacters[n];
             final int row = n / params.mNumColumns;
-            final Key key = new Key(mRes, keyboard, label, params.getX(n, row), params.getY(row),
-                    params.mKeyWidth, params.mRowHeight, params.getRowFlags(row));
-            keys.add(key);
+            final Key key = new Key(mResources, params, label, params.getX(n, row), params.getY(row),
+                    params.mDefaultKeyWidth, params.mDefaultRowHeight, params.getRowFlags(row));
+            params.onAddKey(key);
         }
-        return keyboard;
+        return new MiniKeyboard(params);
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
index 545b27f..55175e0 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -37,22 +37,21 @@
             if (t.isModifier()) {
                 oldestPos++;
             } else {
-                t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime, true);
+                t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
                 queue.remove(oldestPos);
             }
         }
     }
 
     public void releaseAllPointers(long eventTime) {
-        releaseAllPointersExcept(null, eventTime, true);
+        releaseAllPointersExcept(null, eventTime);
     }
 
-    public void releaseAllPointersExcept(PointerTracker tracker, long eventTime,
-            boolean updateReleasedKeyGraphics) {
+    public void releaseAllPointersExcept(PointerTracker tracker, long eventTime) {
         for (PointerTracker t : mQueue) {
             if (t == tracker)
                 continue;
-            t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime, updateReleasedKeyGraphics);
+            t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
         }
         mQueue.clear();
         if (tracker != null)
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java b/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
index 8276f5d..032489e 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
@@ -23,6 +23,8 @@
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.latin.R;
 
+import java.util.ArrayList;
+
 /**
  * String parser of popupCharacters attribute of Key.
  * The string is comma separated texts each of which represents one popup key.
@@ -182,4 +184,54 @@
             super(message);
         }
     }
+
+    public interface CodeFilter {
+        public boolean shouldFilterOut(int code);
+    }
+
+    public static final CodeFilter DIGIT_FILTER = new CodeFilter() {
+        @Override
+        public boolean shouldFilterOut(int code) {
+            return Character.isDigit(code);
+        }
+    };
+
+    public static final CodeFilter NON_ASCII_FILTER = new CodeFilter() {
+        @Override
+        public boolean shouldFilterOut(int code) {
+            return code < 0x20 || code > 0x7e;
+        }
+    };
+
+    public static CharSequence[] filterOut(Resources res, CharSequence[] popupCharacters,
+            CodeFilter filter) {
+        if (popupCharacters == null || popupCharacters.length < 1) {
+            return null;
+        }
+        if (popupCharacters.length == 1
+                && filter.shouldFilterOut(getCode(res, popupCharacters[0].toString()))) {
+            return null;
+        }
+        ArrayList<CharSequence> filtered = null;
+        for (int i = 0; i < popupCharacters.length; i++) {
+            final CharSequence popupSpec = popupCharacters[i];
+            if (filter.shouldFilterOut(getCode(res, popupSpec.toString()))) {
+                if (filtered == null) {
+                    filtered = new ArrayList<CharSequence>();
+                    for (int j = 0; j < i; j++) {
+                        filtered.add(popupCharacters[j]);
+                    }
+                }
+            } else if (filtered != null) {
+                filtered.add(popupSpec);
+            }
+        }
+        if (filtered == null) {
+            return popupCharacters;
+        }
+        if (filtered.size() == 0) {
+            return null;
+        }
+        return filtered.toArray(new CharSequence[filtered.size()]);
+    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/Row.java b/java/src/com/android/inputmethod/keyboard/internal/Row.java
index 06aadcc..d53fe12 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/Row.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/Row.java
@@ -31,43 +31,19 @@
  */
 public class Row {
     /** Default width of a key in this row. */
-    public final int mDefaultWidth;
+    public final int mDefaultKeyWidth;
     /** Default height of a key in this row. */
-    public final int mDefaultHeight;
-    /** Default horizontal gap between keys in this row. */
-    public final int mDefaultHorizontalGap;
-    /** Vertical gap following this row. */
-    public final int mVerticalGap;
-    /**
-     * Edge flags for this row of keys. Possible values that can be assigned are
-     * {@link Keyboard#EDGE_TOP EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM EDGE_BOTTOM}
-     */
-    public final int mRowEdgeFlags;
+    public final int mRowHeight;
 
-    private final Keyboard mKeyboard;
-
-    public Row(Resources res, Keyboard keyboard, XmlResourceParser parser) {
-        this.mKeyboard = keyboard;
-        final int keyboardWidth = keyboard.getDisplayWidth();
-        final int keyboardHeight = keyboard.getKeyboardHeight();
+    public Row(Resources res, KeyboardParams params, XmlResourceParser parser) {
+        final int keyboardWidth = params.mWidth;
+        final int keyboardHeight = params.mHeight;
         TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard);
-        mDefaultWidth = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_keyWidth, keyboardWidth, keyboard.getKeyWidth());
-        mDefaultHeight = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_rowHeight, keyboardHeight, keyboard.getRowHeight());
-        mDefaultHorizontalGap = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_horizontalGap, keyboardWidth, keyboard.getHorizontalGap());
-        mVerticalGap = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_verticalGap, keyboardHeight, keyboard.getVerticalGap());
+        mDefaultKeyWidth = KeyboardBuilder.getDimensionOrFraction(a,
+                R.styleable.Keyboard_keyWidth, keyboardWidth, params.mDefaultKeyWidth);
+        mRowHeight = KeyboardBuilder.getDimensionOrFraction(a,
+                R.styleable.Keyboard_rowHeight, keyboardHeight, params.mDefaultRowHeight);
         a.recycle();
-        a = res.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.Keyboard_Row);
-        mRowEdgeFlags = a.getInt(R.styleable.Keyboard_Row_rowEdgeFlags, 0);
-        a.recycle();
-    }
-
-    public Keyboard getKeyboard() {
-        return mKeyboard;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java b/java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java
deleted file mode 100644
index ef3ea4c..0000000
--- a/java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java
+++ /dev/null
@@ -1,158 +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.internal;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-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 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 boolean mDrawArrows;
-    private final int mThreshold;
-
-    private int mDiff;
-    private boolean mHitThreshold;
-    private String mCurrentLanguage;
-    private String mNextLanguage;
-    private String mPrevLanguage;
-
-    public SlidingLocaleDrawable(Context context, Drawable background, int width, int height) {
-        mBackground = background;
-        Keyboard.setDefaultBounds(background);
-        mWidth = width;
-        mHeight = height;
-        final TextPaint textPaint = new TextPaint();
-        textPaint.setTextSize(LatinKeyboard.getTextSizeFromTheme(
-                context.getTheme(), android.R.style.TextAppearance_Medium, 18));
-        textPaint.setColor(Color.TRANSPARENT);
-        textPaint.setAntiAlias(true);
-        mTextPaint = textPaint;
-        mMiddleX = (background != null) ? (mWidth - mBackground.getIntrinsicWidth()) / 2 : 0;
-
-        final TypedArray a = context.obtainStyledAttributes(
-                null, R.styleable.KeyboardView, R.attr.keyboardViewStyle, R.style.KeyboardView);
-        mSpacebarTextColor = a.getColor(R.styleable.KeyboardView_keyPreviewTextColor, 0);
-        final int spacebarPreviewBackrgound = a.getResourceId(
-                R.styleable.KeyboardView_keyPreviewSpacebarBackground, 0);
-        // If spacebar preview background is transparent, we need not draw arrows.
-        mDrawArrows = (spacebarPreviewBackrgound != R.drawable.transparent);
-        a.recycle();
-
-        mThreshold = ViewConfiguration.get(context).getScaledTouchSlop();
-    }
-
-    public void setDiff(int diff) {
-        if (diff == Integer.MAX_VALUE) {
-            mHitThreshold = false;
-            mCurrentLanguage = null;
-            return;
-        }
-        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;
-        invalidateSelf();
-    }
-
-
-    @Override
-    public void draw(Canvas canvas) {
-        canvas.save();
-        if (mHitThreshold) {
-            Paint paint = mTextPaint;
-            final int width = mWidth;
-            final int height = mHeight;
-            final int diff = mDiff;
-            canvas.clipRect(0, 0, width, height);
-            if (mCurrentLanguage == null) {
-                SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
-                mCurrentLanguage = subtypeSwitcher.getInputLanguageName();
-                mNextLanguage = subtypeSwitcher.getNextInputLanguageName();
-                mPrevLanguage = subtypeSwitcher.getPreviousInputLanguageName();
-            }
-            // Draw language text.
-            final float baseline = mHeight * LatinKeyboard.SPACEBAR_LANGUAGE_BASELINE
-                    - paint.descent();
-            paint.setColor(mSpacebarTextColor);
-            paint.setTextAlign(Align.CENTER);
-            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);
-            if (mDrawArrows) {
-                paint.setTextAlign(Align.LEFT);
-                canvas.drawText(LatinKeyboard.ARROW_LEFT, 0, baseline, paint);
-                paint.setTextAlign(Align.RIGHT);
-                canvas.drawText(LatinKeyboard.ARROW_RIGHT, width, baseline, paint);
-            }
-        }
-        if (mBackground != null) {
-            canvas.translate(mMiddleX, 0);
-            mBackground.draw(canvas);
-        }
-        canvas.restore();
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        // Ignore
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-        // Ignore
-    }
-
-    @Override
-    public int getIntrinsicWidth() {
-        return mWidth;
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        return mHeight;
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 9748d60..6a6a0a4 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -156,10 +156,11 @@
         }
     }
 
+    // proximityInfo may not be null.
     @Override
-    public void getWords(final WordComposer codes, final WordCallback callback) {
-        final int count = getSuggestions(codes, mKeyboardSwitcher.getLatinKeyboard(),
-                mOutputChars, mScores);
+    public void getWords(final WordComposer codes, final WordCallback callback,
+            final ProximityInfo proximityInfo) {
+        final int count = getSuggestions(codes, proximityInfo, mOutputChars, mScores);
 
         for (int j = 0; j < count; ++j) {
             if (mScores[j] < 1) break;
@@ -179,8 +180,9 @@
         return mNativeDict != 0;
     }
 
-    /* package for test */ int getSuggestions(final WordComposer codes, final Keyboard keyboard,
-            char[] outputChars, int[] scores) {
+    // proximityInfo may not be null.
+    /* package for test */ int getSuggestions(final WordComposer codes,
+            final ProximityInfo proximityInfo, char[] outputChars, int[] scores) {
         if (!isValidDictionary()) return -1;
 
         final int codesSize = codes.size();
@@ -196,9 +198,8 @@
         Arrays.fill(outputChars, (char) 0);
         Arrays.fill(scores, 0);
 
-        final int proximityInfo = keyboard == null ? 0 : keyboard.getProximityInfo();
         return getSuggestionsNative(
-                mNativeDict, proximityInfo,
+                mNativeDict, proximityInfo.getNativeProximityInfo(),
                 codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize,
                 mFlags, outputChars, scores);
     }
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 76a230f..f4ba0bc 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -19,8 +19,11 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.database.Cursor;
 import android.net.Uri;
 import android.text.TextUtils;
+import android.util.Log;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -28,7 +31,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 
@@ -37,47 +41,111 @@
  * file from the dictionary provider
  */
 public class BinaryDictionaryFileDumper {
+    private static final String TAG = BinaryDictionaryFileDumper.class.getSimpleName();
+
     /**
      * The size of the temporary buffer to copy files.
      */
     static final int FILE_READ_BUFFER_SIZE = 1024;
 
+    private static final String DICTIONARY_PROJECTION[] = { "id" };
+
     // 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.
+     * Escapes a string for any characters that may be suspicious for a file or directory name.
+     *
+     * Concretely this does a sort of URL-encoding except it will encode everything that's not
+     * alphanumeric or underscore. (true URL-encoding leaves alone characters like '*', which
+     * we cannot allow here)
+     */
+    // TODO: create a unit test for this method
+    private static String replaceFileNameDangerousCharacters(String name) {
+        // This assumes '%' is fully available as a non-separator, normal
+        // character in a file name. This is probably true for all file systems.
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < name.length(); ++i) {
+            final int codePoint = name.codePointAt(i);
+            if (Character.isLetterOrDigit(codePoint) || '_' == codePoint) {
+                sb.appendCodePoint(codePoint);
+            } else {
+                sb.append('%');
+                sb.append(Integer.toHexString(codePoint));
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Find out the cache directory associated with a specific locale.
+     */
+    private static String getCacheDirectoryForLocale(Locale locale, Context context) {
+        final String relativeDirectoryName = replaceFileNameDangerousCharacters(locale.toString());
+        final String absoluteDirectoryName = context.getFilesDir() + File.separator
+                + relativeDirectoryName;
+        final File directory = new File(absoluteDirectoryName);
+        if (!directory.exists()) {
+            if (!directory.mkdirs()) {
+                Log.e(TAG, "Could not create the directory for locale" + locale);
+            }
+        }
+        return absoluteDirectoryName;
+    }
+
+    /**
+     * Generates a file name for the id and locale passed as an argument.
+     *
+     * In the current implementation the file name returned will always be unique for
+     * any id/locale pair, but please do not expect that the id can be the same for
+     * different dictionaries with different locales. An id should be unique for any
+     * dictionary.
+     * The file name is pretty much an URL-encoded version of the id inside a directory
+     * named like the locale, except it will also escape characters that look dangerous
+     * to some file systems.
+     * @param id the id of the dictionary for which to get a file name
      * @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;
+    private static String getCacheFileName(String id, Locale locale, Context context) {
+        final String fileName = replaceFileNameDangerousCharacters(id);
+        return getCacheDirectoryForLocale(locale, context) + File.separator + fileName;
     }
 
     /**
-     * Return for a given locale the provider URI to query to get the dictionary.
+     * Return for a given locale or dictionary id the provider URI to get the dictionary.
      */
-    public static Uri getProviderUri(Locale locale) {
+    private static Uri getProviderUri(String path) {
         return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(BinaryDictionary.DICTIONARY_PACK_AUTHORITY).appendPath(
-                        locale.toString()).build();
+                        path).build();
+    }
+
+    /**
+     * Queries a content provider for the list of dictionaries for a specific locale
+     * available to copy into Latin IME.
+     */
+    private static List<String> getDictIdList(final Locale locale, final Context context) {
+        final ContentResolver resolver = context.getContentResolver();
+        final Uri dictionaryPackUri = getProviderUri(locale.toString());
+
+        final Cursor c = resolver.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null, null);
+        if (null == c) return Collections.<String>emptyList();
+        if (c.getCount() <= 0 || !c.moveToFirst()) {
+            c.close();
+            return Collections.<String>emptyList();
+        }
+
+        final List<String> list = new ArrayList<String>();
+        do {
+            final String id = c.getString(0);
+            if (TextUtils.isEmpty(id)) continue;
+            list.add(id);
+        } while (c.moveToNext());
+        c.close();
+        return list;
     }
 
     /**
@@ -94,31 +162,26 @@
      * @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.
+    public static List<AssetFileAddress> getDictSetFromContentProvider(final Locale locale,
+            final Context context) throws FileNotFoundException, IOException {
         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));
+        final List<String> idList = getDictIdList(locale, context);
+        final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>();
+        for (String id : idList) {
+            final Uri wordListUri = getProviderUri(id);
+            final AssetFileDescriptor afd =
+                    resolver.openAssetFileDescriptor(wordListUri, "r");
+            if (null == afd) continue;
+            final String fileName = copyFileTo(afd.createInputStream(),
+                    getCacheFileName(id, locale, context));
+            afd.close();
+            if (0 >= resolver.delete(wordListUri, null, null)) {
+                // I'd rather not print the word list ID to the log here out of security concerns
+                Log.e(TAG, "Could not have the dictionary pack delete a word list");
+            }
+            fileAddressList.add(AssetFileAddress.makeFromFileName(fileName));
+        }
+        return fileAddressList;
     }
 
     /**
@@ -129,8 +192,11 @@
      */
     public static String getDictionaryFileFromResource(int resource, Locale locale,
             Context context) throws FileNotFoundException, IOException {
-        return copyFileTo(context.getResources().openRawResource(resource),
-                getCacheFileNameForLocale(locale, context));
+        final Resources res = context.getResources();
+        final Locale savedLocale = Utils.setSystemLocale(res, locale);
+        final InputStream stream = res.openRawResource(resource);
+        Utils.setSystemLocale(res, savedLocale);
+        return copyFileTo(stream, getCacheFileName(Integer.toString(resource), locale, context));
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 7ce9292..4b1c05a 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
 import android.util.Log;
 
 import java.io.FileNotFoundException;
@@ -42,8 +43,13 @@
     /**
      * 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);
+    private static AssetFileAddress loadFallbackResource(final Context context,
+            final int fallbackResId, final Locale locale) {
+        final Resources res = context.getResources();
+        final Locale savedLocale = Utils.setSystemLocale(res, locale);
+        final AssetFileDescriptor afd = res.openRawResourceFd(fallbackResId);
+        Utils.setSystemLocale(res, savedLocale);
+
         if (afd == null) {
             Log.e(TAG, "Found the resource but cannot read it. Is it compressed? resId="
                     + fallbackResId);
@@ -57,9 +63,6 @@
      * 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:
@@ -70,28 +73,23 @@
      */
     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");
+        try {
+            List<AssetFileAddress> listFromContentProvider =
+                    BinaryDictionaryFileDumper.getDictSetFromContentProvider(locale, context);
+            if (null != listFromContentProvider) {
+                return listFromContentProvider;
             }
-            return Arrays.asList(loadFallbackResource(context, fallbackResId));
+            // 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");
         }
+        final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId,
+                locale);
+        if (null == fallbackAsset) return null;
+        return Arrays.asList(fallbackAsset);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index 96225f2..d779c85 100644
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -52,14 +52,11 @@
 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();
     // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
     private static final int MAX_SUGGESTIONS = 18;
     private static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -68,11 +65,6 @@
     private static final boolean DBG = LatinImeLogger.sDBG;
 
     private final ViewGroup mCandidatesStrip;
-    private final int mCandidateCountInStrip;
-    private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3;
-    private final ViewGroup mCandidatesPaneControl;
-    private final TextView mExpandCandidatesPane;
-    private final TextView mCloseCandidatesPane;
     private ViewGroup mCandidatesPane;
     private ViewGroup mCandidatesPaneContainer;
     private View mKeyboardView;
@@ -81,17 +73,6 @@
     private final ArrayList<TextView> mInfos = new ArrayList<TextView>();
     private final ArrayList<View> mDividers = new ArrayList<View>();
 
-    private final int mCandidateStripHeight;
-    private final CharacterStyle mInvertedForegroundColorSpan;
-    private final CharacterStyle mInvertedBackgroundColorSpan;
-    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;
 
@@ -103,9 +84,9 @@
     private boolean mShowingAutoCorrectionInverted;
     private boolean mShowingAddToDictionary;
 
-    private final CandidateViewLayoutParams mParams;
-    private static final int PUNCTUATIONS_IN_STRIP = 6;
-    private static final float MIN_TEXT_XSCALE = 0.75f;
+    private final SuggestionsStripParams mStripParams;
+    private final SuggestionsPaneParams mPaneParams;
+    private static final float MIN_TEXT_XSCALE = 0.70f;
 
     private final UiHandler mHandler = new UiHandler(this);
 
@@ -158,118 +139,355 @@
         }
     }
 
-    private static class CandidateViewLayoutParams {
-        public final TextPaint mPaint;
+    private static class CandidateViewParams {
         public final int mPadding;
         public final int mDividerWidth;
         public final int mDividerHeight;
-        public final int mControlWidth;
-        private final int mAutoCorrectHighlight;
+        public final int mCandidateStripHeight;
 
-        public final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
+        protected final List<TextView> mWords;
+        protected final List<View> mDividers;
+        protected final List<TextView> mInfos;
 
-        public int mCountInStrip;
-        // True if the mCountInStrip suggestions can fit in suggestion strip in equally divided
-        // width without squeezing the text.
-        public boolean mCanUseFixedWidthColumns;
-        public int mMaxWidth;
-        public int mAvailableWidthForWords;
-        public int mConstantWidthForPaddings;
-        public int mVariableWidthForWords;
-        public float mScaleX;
+        protected CandidateViewParams(List<TextView> words, List<View> dividers,
+                List<TextView> infos) {
+            mWords = words;
+            mDividers = dividers;
+            mInfos = infos;
 
-        public CandidateViewLayoutParams(Resources res, TextView word, View divider, View control,
-                int autoCorrectHighlight) {
-            mPaint = new TextPaint();
-            final float textSize = res.getDimension(R.dimen.candidate_text_size);
-            mPaint.setTextSize(textSize);
+            final TextView word = words.get(0);
+            final View divider = dividers.get(0);
             mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight();
             divider.measure(WRAP_CONTENT, MATCH_PARENT);
             mDividerWidth = divider.getMeasuredWidth();
             mDividerHeight = divider.getMeasuredHeight();
-            mControlWidth = control.getMeasuredWidth();
-            mAutoCorrectHighlight = autoCorrectHighlight;
+
+            final Resources res = word.getResources();
+            mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height);
+        }
+    }
+
+    private static class SuggestionsPaneParams extends CandidateViewParams {
+        public SuggestionsPaneParams(List<TextView> words, List<View> dividers,
+                List<TextView> infos) {
+            super(words, dividers, infos);
         }
 
-        public void layoutStrip(SuggestedWords suggestions, int maxWidth, int maxCount) {
-            final int size = suggestions.size();
-            if (size == 0) return;
-            setupTexts(suggestions, size, mAutoCorrectHighlight);
-            mCountInStrip = Math.min(maxCount, size);
-            mScaleX = 1.0f;
+        public int layout(SuggestedWords suggestions, ViewGroup paneView, int from, int textColor,
+                int paneWidth) {
+            final int count = Math.min(mWords.size(), suggestions.size());
+            View centeringFrom = null, lastView = null;
+            int x = 0, y = 0;
+            for (int index = from; index < count; index++) {
+                final int pos = index;
+                final TextView word = mWords.get(pos);
+                final View divider = mDividers.get(pos);
+                final TextPaint paint = word.getPaint();
+                word.setTextColor(textColor);
+                final CharSequence styled = suggestions.getWord(pos);
 
-            do {
-                mMaxWidth = maxWidth;
-                if (size > mCountInStrip) {
-                    mMaxWidth -= mControlWidth;
+                final TextView info;
+                if (DBG) {
+                    final CharSequence debugInfo = getDebugInfo(suggestions, index);
+                    if (debugInfo != null) {
+                        info = mInfos.get(index);
+                        info.setText(debugInfo);
+                    } else {
+                        info = null;
+                    }
+                } else {
+                    info = null;
                 }
 
-                tryLayout();
-
-                if (mCanUseFixedWidthColumns) {
-                    return;
+                final CharSequence text;
+                final float scaleX;
+                paint.setTextScaleX(1.0f);
+                final int textWidth = getTextWidth(styled, paint);
+                int available = paneWidth - x - mPadding;
+                if (textWidth >= available) {
+                    // Needs new row, centering previous row.
+                    centeringCandidates(paneView, centeringFrom, lastView, x, paneWidth);
+                    x = 0;
+                    y += mCandidateStripHeight;
                 }
-                if (mVariableWidthForWords <= mAvailableWidthForWords) {
-                    return;
+                if (x != 0) {
+                    // Add divider if this isn't the left most suggestion in current row.
+                    paneView.addView(divider);
+                    FrameLayoutCompatUtils.placeViewAt(divider, x, y
+                            + (mCandidateStripHeight - mDividerHeight) / 2, mDividerWidth,
+                            mDividerHeight);
+                    x += mDividerWidth;
                 }
-
-                final float scaleX = mAvailableWidthForWords / (float)mVariableWidthForWords;
-                if (scaleX >= MIN_TEXT_XSCALE) {
-                    mScaleX = scaleX;
-                    return;
+                available = paneWidth - x - mPadding;
+                text = getEllipsizedText(styled, available, paint);
+                scaleX = paint.getTextScaleX();
+                word.setText(text);
+                word.setTextScaleX(scaleX);
+                paneView.addView(word);
+                lastView = word;
+                if (x == 0)
+                    centeringFrom = word;
+                word.measure(WRAP_CONTENT,
+                        MeasureSpec.makeMeasureSpec(mCandidateStripHeight, MeasureSpec.EXACTLY));
+                final int width = word.getMeasuredWidth();
+                final int height = word.getMeasuredHeight();
+                FrameLayoutCompatUtils.placeViewAt(word, x, y + (mCandidateStripHeight - height)
+                        / 2, width, height);
+                x += width;
+                if (info != null) {
+                    paneView.addView(info);
+                    lastView = info;
+                    info.measure(WRAP_CONTENT, WRAP_CONTENT);
+                    final int infoWidth = info.getMeasuredWidth();
+                    FrameLayoutCompatUtils.placeViewAt(info, x - infoWidth, y, infoWidth,
+                            info.getMeasuredHeight());
                 }
+            }
+            if (x != 0) {
+                // Centering last candidates row.
+                centeringCandidates(paneView, centeringFrom, lastView, x, paneWidth);
+            }
 
-                mCountInStrip--;
-            } while (mCountInStrip > 1);
+            return count - from;
+        }
+    }
+
+    private static class SuggestionsStripParams extends CandidateViewParams {
+        private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3;
+        private static final int DEFAULT_CENTER_CANDIDATE_PERCENTILE = 40;
+        private static final int PUNCTUATIONS_IN_STRIP = 6;
+
+        private final int mColorTypedWord;
+        private final int mColorAutoCorrect;
+        private final int mColorSuggestedCandidate;
+        private final int mCandidateCountInStrip;
+        private final float mCenterCandidateWeight;
+        private final int mCenterCandidateIndex;
+        private final Drawable mMoreCandidateHint;
+
+        private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
+        private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
+        private final CharacterStyle mInvertedForegroundColorSpan;
+        private final CharacterStyle mInvertedBackgroundColorSpan;
+        private static final int AUTO_CORRECT_BOLD = 0x01;
+        private static final int AUTO_CORRECT_UNDERLINE = 0x02;
+        private static final int AUTO_CORRECT_INVERT = 0x04;
+
+        private final TextPaint mPaint;
+        private final int mAutoCorrectHighlight;
+
+        private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
+
+        public boolean mMoreSuggestionsAvailable;
+
+        public SuggestionsStripParams(Context context, AttributeSet attrs, int defStyle,
+                List<TextView> words, List<View> dividers, List<TextView> infos) {
+            super(words, dividers, infos);
+            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);
+            mCandidateCountInStrip = a.getInt(
+                    R.styleable.CandidateView_candidateCountInStrip,
+                    DEFAULT_CANDIDATE_COUNT_IN_STRIP);
+            mCenterCandidateWeight = a.getInt(
+                    R.styleable.CandidateView_centerCandidatePercentile,
+                    DEFAULT_CENTER_CANDIDATE_PERCENTILE) / 100.0f;
+            a.recycle();
+
+            mCenterCandidateIndex = mCandidateCountInStrip / 2;
+            final Resources res = context.getResources();
+            mMoreCandidateHint = res.getDrawable(R.drawable.more_suggestions_hint);
+
+            mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff);
+            mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord);
+
+            mPaint = new TextPaint();
+            final float textSize = res.getDimension(R.dimen.candidate_text_size);
+            mPaint.setTextSize(textSize);
         }
 
-        public void tryLayout() {
-            final int maxCount = mCountInStrip;
-            final int dividers = mDividerWidth * (maxCount - 1);
-            mConstantWidthForPaddings = dividers + mPadding * maxCount;
-            mAvailableWidthForWords = mMaxWidth - mConstantWidthForPaddings;
+        public int getTextColor() {
+            return mColorTypedWord;
+        }
 
-            mPaint.setTextScaleX(mScaleX);
-            final int maxFixedWidthForWord = (mMaxWidth - dividers) / maxCount - mPadding;
-            mCanUseFixedWidthColumns = true;
-            mVariableWidthForWords = 0;
-            for (int i = 0; i < maxCount; i++) {
-                final int width = getTextWidth(mTexts.get(i), mPaint);
-                if (width > maxFixedWidthForWord)
-                    mCanUseFixedWidthColumns = false;
-                mVariableWidthForWords += width;
+        private CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect) {
+            if (!isAutoCorrect)
+                return word;
+            final int len = word.length();
+            final Spannable spannedWord = new SpannableString(word);
+            if ((mAutoCorrectHighlight & AUTO_CORRECT_BOLD) != 0)
+                spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+            if ((mAutoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0)
+                spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+            return spannedWord;
+        }
+
+        private static boolean willAutoCorrect(SuggestedWords suggestions) {
+            return !suggestions.mTypedWordValid && suggestions.mHasMinimalSuggestion;
+        }
+
+        private int getWordPosition(int index, SuggestedWords suggestions) {
+            // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more
+            // suggestions.
+            final int centerPos = willAutoCorrect(suggestions) ? 1 : 0;
+            if (index == mCenterCandidateIndex) {
+                return centerPos;
+            } else if (index == centerPos) {
+                return mCenterCandidateIndex;
+            } else {
+                return index;
             }
         }
 
-        private void setupTexts(SuggestedWords suggestions, int count, int autoCorrectHighlight) {
-            mTexts.clear();
-            for (int i = 0; i < count; i++) {
-                final CharSequence suggestion = suggestions.getWord(i);
-                if (suggestion == null) {
-                    // Skip an empty suggestion, but we need to add a place-holder for it in order
-                    // to avoid an exception in the loop in updateSuggestions().
-                    mTexts.add("");
-                    continue;
+        private int getCandidateTextColor(int index, SuggestedWords suggestions, int pos) {
+            // TODO: Need to revisit this logic with bigram suggestions
+            final boolean isSuggestedCandidate = (pos != 0);
+
+            final int color;
+            if (index == mCenterCandidateIndex && willAutoCorrect(suggestions)) {
+                color = mColorAutoCorrect;
+            } else if (isSuggestedCandidate) {
+                color = mColorSuggestedCandidate;
+            } else {
+                color = mColorTypedWord;
+            }
+
+            final SuggestedWordInfo info = (pos < suggestions.size())
+                    ? suggestions.getInfo(pos) : null;
+            if (info != null && info.isPreviousSuggestedWord()) {
+                return applyAlpha(color, 0.5f);
+            } else {
+                return color;
+            }
+        }
+
+        private static int applyAlpha(final int color, final float alpha) {
+            final int newAlpha = (int)(Color.alpha(color) * alpha);
+            return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color));
+        }
+
+        public CharSequence getInvertedText(CharSequence text) {
+            if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0)
+                return null;
+            final int len = text.length();
+            final Spannable word = new SpannableString(text);
+            word.setSpan(mInvertedBackgroundColorSpan, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+            word.setSpan(mInvertedForegroundColorSpan, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+            return word;
+        }
+
+        public int layout(SuggestedWords suggestions, ViewGroup stripView, ViewGroup paneView,
+                int stripWidth) {
+            if (suggestions.isPunctuationSuggestions()) {
+                return layoutPunctuationSuggestions(suggestions, stripView);
+            }
+
+            final int countInStrip = mCandidateCountInStrip;
+            setupTexts(suggestions, countInStrip);
+            mMoreSuggestionsAvailable = (suggestions.size() > countInStrip);
+            int x = 0;
+            for (int index = 0; index < countInStrip; index++) {
+                final int pos = getWordPosition(index, suggestions);
+
+                if (index != 0) {
+                    final View divider = mDividers.get(pos);
+                    // Add divider if this isn't the left most suggestion in candidate strip.
+                    stripView.addView(divider);
                 }
 
-                final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion
-                        && ((i == 1 && !suggestions.mTypedWordValid)
-                                || (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 CharSequence styled = getStyledCandidateWord(suggestion, isAutoCorrect,
-                        autoCorrectHighlight);
+                final CharSequence styled = mTexts.get(pos);
+                final TextView word = mWords.get(pos);
+                if (index == mCenterCandidateIndex && mMoreSuggestionsAvailable) {
+                    // TODO: This "more suggestions hint" should have nicely designed icon.
+                    word.setCompoundDrawablesWithIntrinsicBounds(
+                            null, null, null, mMoreCandidateHint);
+                } else {
+                    word.setCompoundDrawables(null, null, null, null);
+                }
+
+                // Disable this candidate if the suggestion is null or empty.
+                word.setEnabled(!TextUtils.isEmpty(styled));
+                word.setTextColor(getCandidateTextColor(index, suggestions, pos));
+                final int width = getCandidateWidth(index, stripWidth);
+                final CharSequence text = getEllipsizedText(styled, width, word.getPaint());
+                final float scaleX = word.getTextScaleX();
+                word.setText(text); // TextView.setText() resets text scale x to 1.0.
+                word.setTextScaleX(scaleX);
+                stripView.addView(word);
+                setLayoutWeight(word, getCandidateWeight(index), mCandidateStripHeight);
+
+                if (DBG) {
+                    final CharSequence debugInfo = getDebugInfo(suggestions, pos);
+                    if (debugInfo != null) {
+                        final TextView info = mInfos.get(pos);
+                        info.setText(debugInfo);
+                        paneView.addView(info);
+                        info.measure(WRAP_CONTENT, WRAP_CONTENT);
+                        final int infoWidth = info.getMeasuredWidth();
+                        final int y = info.getMeasuredHeight();
+                        FrameLayoutCompatUtils.placeViewAt(info, x, 0, infoWidth, y);
+                        x += infoWidth * 2;
+                    }
+                }
+            }
+
+            return countInStrip;
+        }
+
+        private int getCandidateWidth(int index, int maxWidth) {
+            final int paddings = mPadding * mCandidateCountInStrip;
+            final int dividers = mDividerWidth * (mCandidateCountInStrip - 1);
+            final int availableWidth = maxWidth - paddings - dividers;
+            return (int)(availableWidth * getCandidateWeight(index));
+        }
+
+        private float getCandidateWeight(int index) {
+            if (index == mCenterCandidateIndex) {
+                return mCenterCandidateWeight;
+            } else {
+                // TODO: Revisit this for cases of 5 or more suggestions
+                return (1.0f - mCenterCandidateWeight) / (mCandidateCountInStrip - 1);
+            }
+        }
+
+        private void setupTexts(SuggestedWords suggestions, int countInStrip) {
+            mTexts.clear();
+            final int count = Math.min(suggestions.size(), countInStrip);
+            for (int pos = 0; pos < count; pos++) {
+                final CharSequence word = suggestions.getWord(pos);
+                final boolean isAutoCorrect = pos == 1 && willAutoCorrect(suggestions);
+                final CharSequence styled = getStyledCandidateWord(word, isAutoCorrect);
                 mTexts.add(styled);
             }
+            for (int pos = count; pos < countInStrip; pos++) {
+                // Make this inactive for touches in layout().
+                mTexts.add(null);
+            }
         }
 
-        @Override
-        public String toString() {
-            return String.format(
-                    "count=%d width=%d avail=%d fixcol=%s scaleX=%4.2f const=%d var=%d",
-                    mCountInStrip, mMaxWidth, mAvailableWidthForWords, mCanUseFixedWidthColumns,
-                    mScaleX, mConstantWidthForPaddings, mVariableWidthForWords);
+        private int layoutPunctuationSuggestions(SuggestedWords suggestions, ViewGroup stripView) {
+            final int countInStrip = Math.min(suggestions.size(), PUNCTUATIONS_IN_STRIP);
+            for (int index = 0; index < countInStrip; index++) {
+                if (index != 0) {
+                    // Add divider if this isn't the left most suggestion in candidate strip.
+                    stripView.addView(mDividers.get(index));
+                }
+
+                final TextView word = mWords.get(index);
+                word.setEnabled(true);
+                word.setTextColor(mColorTypedWord);
+                final CharSequence text = suggestions.getWord(index);
+                word.setText(text);
+                word.setTextScaleX(1.0f);
+                word.setCompoundDrawables(null, null, null, null);
+                stripView.addView(word);
+                setLayoutWeight(word, 1.0f, mCandidateStripHeight);
+            }
+            mMoreSuggestionsAvailable = false;
+            return countInStrip;
         }
     }
 
@@ -296,18 +514,7 @@
         setBackgroundDrawable(LinearLayoutCompatUtils.getBackgroundDrawable(
                 context, attrs, defStyle, R.style.CandidateViewStyle));
 
-        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);
-        mCandidateCountInStrip = a.getInt(
-                R.styleable.CandidateView_candidateCountInStrip, DEFAULT_CANDIDATE_COUNT_IN_STRIP);
-        a.recycle();
-
-        Resources res = context.getResources();
-        LayoutInflater inflater = LayoutInflater.from(context);
+        final LayoutInflater inflater = LayoutInflater.from(context);
         inflater.inflate(R.layout.candidates_strip, this);
 
         mPreviewPopup = new PopupWindow(context);
@@ -318,58 +525,26 @@
         mPreviewPopup.setBackgroundDrawable(null);
 
         mCandidatesStrip = (ViewGroup)findViewById(R.id.candidates_strip);
-        mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height);
-        for (int i = 0; i < MAX_SUGGESTIONS; i++) {
+        for (int pos = 0; pos < MAX_SUGGESTIONS; pos++) {
             final TextView word = (TextView)inflater.inflate(R.layout.candidate_word, null);
-            word.setTag(i);
+            word.setTag(pos);
             word.setOnClickListener(this);
-            if (i == 0)
-                word.setOnLongClickListener(this);
+            word.setOnLongClickListener(this);
             mWords.add(word);
+            final View divider = inflater.inflate(R.layout.candidate_divider, null);
+            divider.setTag(pos);
+            divider.setOnClickListener(this);
+            mDividers.add(divider);
             mInfos.add((TextView)inflater.inflate(R.layout.candidate_info, null));
-            mDividers.add(inflater.inflate(R.layout.candidate_divider, null));
         }
 
         mTouchToSave = findViewById(R.id.touch_to_save);
         mWordToSave = (TextView)findViewById(R.id.word_to_save);
         mWordToSave.setOnClickListener(this);
 
-        mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff);
-        mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord);
-
-        final TypedArray keyboardViewAttr = context.obtainStyledAttributes(
-                attrs, R.styleable.KeyboardView, R.attr.keyboardViewStyle, R.style.KeyboardView);
-        final Drawable expandBackground = keyboardViewAttr.getDrawable(
-                R.styleable.KeyboardView_keyBackground);
-        final Drawable closeBackground = keyboardViewAttr.getDrawable(
-                R.styleable.KeyboardView_keyBackground);
-        final int keyTextColor = keyboardViewAttr.getColor(
-                R.styleable.KeyboardView_keyTextColor, 0xFF000000);
-        keyboardViewAttr.recycle();
-
-        mCandidatesPaneControl = (ViewGroup)findViewById(R.id.candidates_pane_control);
-        mExpandCandidatesPane = (TextView)findViewById(R.id.expand_candidates_pane);
-        mExpandCandidatesPane.setBackgroundDrawable(expandBackground);
-        mExpandCandidatesPane.setTextColor(keyTextColor);
-        mExpandCandidatesPane.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                expandCandidatesPane();
-            }
-        });
-        mCloseCandidatesPane = (TextView)findViewById(R.id.close_candidates_pane);
-        mCloseCandidatesPane.setBackgroundDrawable(closeBackground);
-        mCloseCandidatesPane.setTextColor(keyTextColor);
-        mCloseCandidatesPane.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                closeCandidatesPane();
-            }
-        });
-        mCandidatesPaneControl.measure(WRAP_CONTENT, WRAP_CONTENT);
-
-        mParams = new CandidateViewLayoutParams(res,
-                mWords.get(0), mDividers.get(0), mCandidatesPaneControl, mAutoCorrectHighlight);
+        mStripParams = new SuggestionsStripParams(context, attrs, defStyle, mWords, mDividers,
+                mInfos);
+        mPaneParams = new SuggestionsPaneParams(mWords, mDividers, mInfos);
     }
 
     /**
@@ -390,7 +565,6 @@
         if (suggestions == null)
             return;
         mSuggestions = suggestions;
-        mExpandCandidatesPane.setEnabled(false);
         if (mShowingAutoCorrectionInverted) {
             mHandler.postUpdateSuggestions();
         } else {
@@ -398,181 +572,30 @@
         }
     }
 
-    private static CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect,
-            int autoCorrectHighlight) {
-        if (!isAutoCorrect)
-            return word;
-        final Spannable spannedWord = new SpannableString(word);
-        if ((autoCorrectHighlight & AUTO_CORRECT_BOLD) != 0)
-            spannedWord.setSpan(BOLD_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-        if ((autoCorrectHighlight & 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;
-        final int paneWidth = getWidth();
-        final CandidateViewLayoutParams params = mParams;
-
         clear();
         closeCandidatesPane();
-        if (suggestions.size() == 0)
+        if (mSuggestions.size() == 0)
             return;
 
-        params.layoutStrip(suggestions, paneWidth, suggestions.isPunctuationSuggestions()
-                ? PUNCTUATIONS_IN_STRIP : mCandidateCountInStrip);
+        final int width = getWidth();
+        final int countInStrip = mStripParams.layout(
+                mSuggestions, mCandidatesStrip, mCandidatesPane, width);
+        final int countInPane = mPaneParams.layout(
+                mSuggestions, mCandidatesPane, countInStrip, mStripParams.getTextColor(), width);
+    }
 
-        final int count = Math.min(mWords.size(), suggestions.size());
-        if (count <= params.mCountInStrip && !DBG) {
-            mCandidatesPaneControl.setVisibility(GONE);
-        } else {
-            mCandidatesPaneControl.setVisibility(VISIBLE);
-            mExpandCandidatesPane.setVisibility(VISIBLE);
-            mExpandCandidatesPane.setEnabled(true);
-        }
-
-        final int countInStrip = params.mCountInStrip;
-        View centeringFrom = null, lastView = null;
-        int x = 0, y = 0, infoX = 0;
-        for (int i = 0; i < count; i++) {
-            final int pos;
-            if (i <= 1) {
-                final boolean willAutoCorrect = !suggestions.mTypedWordValid
-                        && suggestions.mHasMinimalSuggestion;
-                pos = willAutoCorrect ? 1 - i : i;
-            } else {
-                pos = i;
-            }
-            final CharSequence suggestion = suggestions.getWord(pos);
-            if (suggestion == null) continue;
-
-            final SuggestedWordInfo suggestionInfo = (suggestedWordInfoList != null)
-                    ? suggestedWordInfoList.get(pos) : null;
-            final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion
-                    && ((pos == 1 && !suggestions.mTypedWordValid)
-                            || (pos == 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 = (pos != 0);
-            final boolean isPunctuationSuggestions = (suggestion.length() == 1 && count > 1);
-
-            final TextView word = mWords.get(pos);
-            final TextPaint paint = word.getPaint();
-            // 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).
-            word.setTextColor(getCandidateTextColor(isAutoCorrect,
-                    isSuggestedCandidate || isPunctuationSuggestions, suggestionInfo));
-            final CharSequence styled = params.mTexts.get(pos);
-
-            final TextView info;
-            if (DBG && suggestionInfo != null
-                    && !TextUtils.isEmpty(suggestionInfo.getDebugString())) {
-                info = mInfos.get(i);
-                info.setText(suggestionInfo.getDebugString());
-            } else {
-                info = null;
-            }
-
-            final CharSequence text;
-            final float scaleX;
-            if (i < countInStrip) {
-                if (i == 0 && params.mCountInStrip == 1) {
-                    text = getEllipsizedText(styled, params.mMaxWidth, paint);
-                    scaleX = paint.getTextScaleX();
-                } else {
-                    text = styled;
-                    scaleX = params.mScaleX;
-                }
-                word.setText(text);
-                word.setTextScaleX(scaleX);
-                if (i != 0) {
-                    // Add divider if this isn't the left most suggestion in candidate strip.
-                    mCandidatesStrip.addView(mDividers.get(i));
-                }
-                mCandidatesStrip.addView(word);
-                if (params.mCanUseFixedWidthColumns) {
-                    setLayoutWeight(word, 1.0f, mCandidateStripHeight);
-                } else {
-                    final int width = getTextWidth(text, paint) + params.mPadding;
-                    setLayoutWeight(word, width, mCandidateStripHeight);
-                }
-                if (info != null) {
-                    mCandidatesPane.addView(info);
-                    info.measure(WRAP_CONTENT, WRAP_CONTENT);
-                    final int width = info.getMeasuredWidth();
-                    y = info.getMeasuredHeight();
-                    FrameLayoutCompatUtils.placeViewAt(info, infoX, 0, width, y);
-                    infoX += width * 2;
-                }
-            } else {
-                paint.setTextScaleX(1.0f);
-                final int textWidth = getTextWidth(styled, paint);
-                int available = paneWidth - x - params.mPadding;
-                if (textWidth >= available) {
-                    // Needs new row, centering previous row.
-                    centeringCandidates(centeringFrom, lastView, x, paneWidth);
-                    x = 0;
-                    y += mCandidateStripHeight;
-                }
-                if (x != 0) {
-                    // Add divider if this isn't the left most suggestion in current row.
-                    final View divider = mDividers.get(i);
-                    mCandidatesPane.addView(divider);
-                    FrameLayoutCompatUtils.placeViewAt(
-                            divider, x, y + (mCandidateStripHeight - params.mDividerHeight) / 2,
-                            params.mDividerWidth, params.mDividerHeight);
-                    x += params.mDividerWidth;
-                }
-                available = paneWidth - x - params.mPadding;
-                text = getEllipsizedText(styled, available, paint);
-                scaleX = paint.getTextScaleX();
-                word.setText(text);
-                word.setTextScaleX(scaleX);
-                mCandidatesPane.addView(word);
-                lastView = word;
-                if (x == 0) centeringFrom = word;
-                word.measure(WRAP_CONTENT,
-                        MeasureSpec.makeMeasureSpec(mCandidateStripHeight, MeasureSpec.EXACTLY));
-                final int width = word.getMeasuredWidth();
-                final int height = word.getMeasuredHeight();
-                FrameLayoutCompatUtils.placeViewAt(
-                        word, x, y + (mCandidateStripHeight - height) / 2, width, height);
-                x += width;
-                if (info != null) {
-                    mCandidatesPane.addView(info);
-                    lastView = info;
-                    info.measure(WRAP_CONTENT, WRAP_CONTENT);
-                    final int infoWidth = info.getMeasuredWidth();
-                    FrameLayoutCompatUtils.placeViewAt(
-                            info, x - infoWidth, y, infoWidth, info.getMeasuredHeight());
+    private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) {
+        if (DBG && pos < suggestions.size()) {
+            final SuggestedWordInfo wordInfo = suggestions.getInfo(pos);
+            if (wordInfo != null) {
+                final CharSequence debugInfo = wordInfo.getDebugString();
+                if (!TextUtils.isEmpty(debugInfo)) {
+                    return debugInfo;
                 }
             }
         }
-        if (x != 0) {
-            // Centering last candidates row.
-            centeringCandidates(centeringFrom, lastView, x, paneWidth);
-        }
+        return null;
     }
 
     private static void setLayoutWeight(View v, float weight, int height) {
@@ -585,13 +608,13 @@
         }
     }
 
-    private void centeringCandidates(View from, View to, int width, int paneWidth) {
-        final ViewGroup pane = mCandidatesPane;
-        final int fromIndex = pane.indexOfChild(from);
-        final int toIndex = pane.indexOfChild(to);
-        final int offset = (paneWidth - width) / 2;
+    private static void centeringCandidates(ViewGroup parent, View from, View to, int width,
+            int parentWidth) {
+        final int fromIndex = parent.indexOfChild(from);
+        final int toIndex = parent.indexOfChild(to);
+        final int offset = (parentWidth - width) / 2;
         for (int index = fromIndex; index <= toIndex; index++) {
-            offsetMargin(pane.getChildAt(index), offset, 0);
+            offsetMargin(parent.getChildAt(index), offset, 0);
         }
     }
 
@@ -609,14 +632,17 @@
             TextPaint paint) {
         paint.setTextScaleX(1.0f);
         final int width = getTextWidth(text, paint);
-        final float scaleX = Math.min(maxWidth / (float)width, 1.0f);
+        if (width <= maxWidth) {
+            return text;
+        }
+        final float scaleX = maxWidth / (float)width;
         if (scaleX >= MIN_TEXT_XSCALE) {
             paint.setTextScaleX(scaleX);
             return text;
         }
 
         // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To get
-        // squeezed and ellipsezed text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
+        // squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
         final CharSequence ellipsized = TextUtils.ellipsize(
                 text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE);
         paint.setTextScaleX(MIN_TEXT_XSCALE);
@@ -655,31 +681,30 @@
     }
 
     private void expandCandidatesPane() {
-        mExpandCandidatesPane.setVisibility(GONE);
-        mCloseCandidatesPane.setVisibility(VISIBLE);
         mCandidatesPaneContainer.setMinimumHeight(mKeyboardView.getMeasuredHeight());
         mCandidatesPaneContainer.setVisibility(VISIBLE);
         mKeyboardView.setVisibility(GONE);
     }
 
     private void closeCandidatesPane() {
-        mExpandCandidatesPane.setVisibility(VISIBLE);
-        mCloseCandidatesPane.setVisibility(GONE);
         mCandidatesPaneContainer.setVisibility(GONE);
         mKeyboardView.setVisibility(VISIBLE);
     }
 
+    private void toggleCandidatesPane() {
+        if (mCandidatesPaneContainer.getVisibility() == VISIBLE) {
+            closeCandidatesPane();
+        } else {
+            expandCandidatesPane();
+        }
+    }
+
     public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) {
-        if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0)
+        final CharSequence inverted = mStripParams.getInvertedText(autoCorrectedWord);
+        if (inverted == null)
             return;
         final TextView tv = mWords.get(1);
-        final Spannable word = new SpannableString(autoCorrectedWord);
-        final int wordLength = word.length();
-        word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength,
-                Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-        word.setSpan(mInvertedForegroundColorSpan, 0, wordLength,
-                Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-        tv.setText(word);
+        tv.setText(inverted);
         mShowingAutoCorrectionInverted = true;
     }
 
@@ -691,7 +716,6 @@
         mWordToSave.setText(word);
         mShowingAddToDictionary = true;
         mCandidatesStrip.setVisibility(GONE);
-        mCandidatesPaneControl.setVisibility(GONE);
         mTouchToSave.setVisibility(VISIBLE);
     }
 
@@ -724,7 +748,7 @@
             return;
 
         final TextView previewText = mPreviewText;
-        previewText.setTextColor(mColorTypedWord);
+        previewText.setTextColor(mStripParams.mColorTypedWord);
         previewText.setText(word);
         previewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
@@ -751,18 +775,11 @@
 
     @Override
     public boolean onLongClick(View view) {
-        final Object tag = view.getTag();
-        if (!(tag instanceof Integer))
+        if (mStripParams.mMoreSuggestionsAvailable) {
+            toggleCandidatesPane();
             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;
-        addToDictionary(word);
-        return true;
+        }
+        return false;
     }
 
     @Override
@@ -773,6 +790,11 @@
             return;
         }
 
+        if (view == mCandidatesPane) {
+            closeCandidatesPane();
+            return;
+        }
+
         final Object tag = view.getTag();
         if (!(tag instanceof Integer))
             return;
diff --git a/java/src/com/android/inputmethod/latin/ContactsDictionary.java b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
index 66a0415..8a7dfb8 100644
--- a/java/src/com/android/inputmethod/latin/ContactsDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
@@ -49,20 +49,28 @@
 
     private long mLastLoadedContacts;
 
-    public ContactsDictionary(Context context, int dicTypeId) {
+    public ContactsDictionary(final Context context, final int dicTypeId) {
         super(context, dicTypeId);
+        registerObserver(context);
+        loadDictionary();
+    }
+
+    private synchronized void registerObserver(final Context context) {
         // Perform a managed query. The Activity will handle closing and requerying the cursor
         // when needed.
+        if (mObserver != null) return;
         ContentResolver cres = context.getContentResolver();
-
         cres.registerContentObserver(
-                Contacts.CONTENT_URI, true,mObserver = new ContentObserver(null) {
+                Contacts.CONTENT_URI, true, mObserver = new ContentObserver(null) {
                     @Override
                     public void onChange(boolean self) {
                         setRequiresReload(true);
                     }
                 });
-        loadDictionary();
+    }
+
+    public void reopen(final Context context) {
+        registerObserver(context);
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java
index fd62d61..2f1e7c2 100644
--- a/java/src/com/android/inputmethod/latin/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/DebugSettings.java
@@ -33,7 +33,6 @@
 
     private boolean mServiceNeedsRestart = false;
     private CheckBoxPreference mDebugMode;
-    private CheckBoxPreference mUseSpacebarLanguageSwitch;
 
     @Override
     protected void onCreate(Bundle icicle) {
@@ -61,13 +60,6 @@
                 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 c7737b9..c35b428 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -1,12 +1,12 @@
 /*
  * Copyright (C) 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
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.keyboard.ProximityInfo;
+
 /**
  * Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key
  * strokes.
@@ -25,7 +27,7 @@
      * Whether or not to replicate the typed word in the suggested list, even if it's valid.
      */
     protected static final boolean INCLUDE_TYPED_WORD_IF_VALID = false;
-    
+
     /**
      * The weight to give to a word if it's length is the same as the number of typed characters.
      */
@@ -57,13 +59,15 @@
     }
 
     /**
-     * Searches for words in the dictionary that match the characters in the composer. Matched 
+     * Searches for words in the dictionary that match the characters in the composer. Matched
      * 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
+     * @param proximityInfo the object for key proximity. May be ignored by some implementations.
      * @see WordCallback#addWord(char[], int, int, int, int, DataType)
      */
-    abstract public void getWords(final WordComposer composer, final WordCallback callback);
+    abstract public void getWords(final WordComposer composer, final WordCallback callback,
+            final ProximityInfo proximityInfo);
 
     /**
      * Searches for pairs in the bigram dictionary that matches the previous word and all the
@@ -83,7 +87,7 @@
      * @return true if the word exists, false otherwise
      */
     abstract public boolean isValidWord(CharSequence word);
-    
+
     /**
      * Compares the contents of the character array with the typed word and returns true if they
      * are the same.
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index 5e7de3e..7391530 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -16,7 +16,10 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.keyboard.ProximityInfo;
+
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -32,17 +35,24 @@
     }
 
     public DictionaryCollection(Dictionary... dictionaries) {
-        mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
+        if (null == dictionaries) {
+            mDictionaries = new CopyOnWriteArrayList<Dictionary>();
+        } else {
+            mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
+            mDictionaries.removeAll(Collections.singleton(null));
+        }
     }
 
     public DictionaryCollection(Collection<Dictionary> dictionaries) {
         mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
+        mDictionaries.removeAll(Collections.singleton(null));
     }
 
     @Override
-    public void getWords(final WordComposer composer, final WordCallback callback) {
+    public void getWords(final WordComposer composer, final WordCallback callback,
+            final ProximityInfo proximityInfo) {
         for (final Dictionary dict : mDictionaries)
-            dict.getWords(composer, callback);
+            dict.getWords(composer, callback, proximityInfo);
     }
 
     @Override
@@ -66,6 +76,6 @@
     }
 
     public void addDictionary(Dictionary newDict) {
-        mDictionaries.add(newDict);
+        if (null != newDict) mDictionaries.add(newDict);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
index bba3318..39b4f63 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -48,29 +48,61 @@
             int fallbackResId) {
         if (null == locale) {
             Log.e(TAG, "No locale defined for dictionary");
-            return new DictionaryCollection(createBinaryDictionary(context, fallbackResId));
+            return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, locale));
         }
 
         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));
+        final List<AssetFileAddress> assetFileList =
+                BinaryDictionaryGetter.getDictionaryFiles(locale, context, fallbackResId);
+        if (null != assetFileList) {
+            for (final AssetFileAddress f : assetFileList) {
+                final BinaryDictionary binaryDictionary =
+                        new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength, null);
+                if (binaryDictionary.isValidDictionary()) {
+                    dictList.add(binaryDictionary);
+                }
+            }
         }
 
-        if (null == dictList) return null;
-        return new DictionaryCollection(dictList);
+        // null == dictList is not supposed to be possible, but better safe than sorry and it's
+        // safer for future extension. In this case, rather than returning null, it should be safer
+        // to return an empty DictionaryCollection.
+        if (null == dictList) {
+            return new DictionaryCollection();
+        } else {
+            if (dictList.isEmpty()) {
+                // The list may be empty if no dictionaries have been added. The getter should not
+                // return an empty list, but if it does we end up here. Likewise, if the files
+                // we found could not be opened by the native code for any reason (format mismatch,
+                // file too big to fit in memory, etc) then we could have an empty list. In this
+                // case we want to fall back on the resource.
+                return new DictionaryCollection(createBinaryDictionary(context, fallbackResId,
+                        locale));
+            } else {
+                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
+     * @param locale the locale to use for the resource
      * @return an initialized instance of BinaryDictionary
      */
-    protected static BinaryDictionary createBinaryDictionary(Context context, int resId) {
+    protected static BinaryDictionary createBinaryDictionary(final Context context,
+            final int resId, final Locale locale) {
         AssetFileDescriptor afd = null;
         try {
-            afd = context.getResources().openRawResourceFd(resId);
+            final Resources res = context.getResources();
+            if (null != locale) {
+                final Locale savedLocale = Utils.setSystemLocale(res, locale);
+                afd = res.openRawResourceFd(resId);
+                Utils.setSystemLocale(res, savedLocale);
+            } else {
+                afd = res.openRawResourceFd(resId);
+            }
             if (afd == null) {
                 Log.e(TAG, "Found the resource but it is compressed. resId=" + resId);
                 return null;
diff --git a/java/src/com/android/inputmethod/latin/EditingUtils.java b/java/src/com/android/inputmethod/latin/EditingUtils.java
index e56aa69..634dbbd 100644
--- a/java/src/com/android/inputmethod/latin/EditingUtils.java
+++ b/java/src/com/android/inputmethod/latin/EditingUtils.java
@@ -33,6 +33,7 @@
      * Number of characters we want to look back in order to identify the previous word
      */
     private static final int LOOKBACK_CHARACTER_NUM = 15;
+    private static final int INVALID_CURSOR_POSITION = -1;
 
     private EditingUtils() {
         // Unintentional empty constructor for singleton.
@@ -63,10 +64,11 @@
     }
 
     private static int getCursorPosition(InputConnection connection) {
+        if (null == connection) return INVALID_CURSOR_POSITION;
         ExtractedText extracted = connection.getExtractedText(
             new ExtractedTextRequest(), 0);
         if (extracted == null) {
-          return -1;
+            return INVALID_CURSOR_POSITION;
         }
         return extracted.startOffset + extracted.selectionStart;
     }
@@ -79,6 +81,7 @@
      *   represents the cursor, then "hello " will be returned.
      */
     public static String getWordAtCursor(InputConnection connection, String separators) {
+        // getWordRangeAtCursor returns null if the connection is null
         Range r = getWordRangeAtCursor(connection, separators);
         return (r == null) ? null : r.mWord;
     }
@@ -88,6 +91,7 @@
      * getWordAtCursor.
      */
     public static void deleteWordAtCursor(InputConnection connection, String separators) {
+        // getWordRangeAtCursor returns null if the connection is null
         Range range = getWordRangeAtCursor(connection, separators);
         if (range == null) return;
 
@@ -165,6 +169,7 @@
     public static CharSequence getPreviousWord(InputConnection connection,
             String sentenceSeperators) {
         //TODO: Should fix this. This could be slow!
+        if (null == connection) return null;
         CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
         return getPreviousWord(prev, sentenceSeperators);
     }
@@ -194,6 +199,7 @@
     }
 
     public static CharSequence getThisWord(InputConnection connection, String sentenceSeperators) {
+        if (null == connection) return null;
         final CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
         return getThisWord(prev, sentenceSeperators);
     }
@@ -256,12 +262,14 @@
             int selStart, int selEnd, String wordSeparators) {
         if (selStart == selEnd) {
             // There is just a cursor, so get the word at the cursor
+            // getWordRangeAtCursor returns null if the connection is null
             EditingUtils.Range range = getWordRangeAtCursor(ic, wordSeparators);
             if (range != null && !TextUtils.isEmpty(range.mWord)) {
                 return new SelectedWord(selStart - range.mCharsBefore, selEnd + range.mCharsAfter,
                         range.mWord);
             }
         } else {
+            if (null == ic) return null;
             // Is the previous character empty or a word separator? If not, return null.
             CharSequence charsBefore = ic.getTextBeforeCursor(1, 0);
             if (!isWordBoundary(charsBefore, wordSeparators)) {
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index 97a4a18..35d1541 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -20,6 +20,7 @@
 import android.os.AsyncTask;
 
 import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.ProximityInfo;
 
 import java.util.LinkedList;
 
@@ -193,7 +194,8 @@
     }
 
     @Override
-    public void getWords(final WordComposer codes, final WordCallback callback) {
+    public void getWords(final WordComposer codes, final WordCallback callback,
+            final ProximityInfo proximityInfo) {
         synchronized (mUpdatingLock) {
             // If we need to update, start off a background task
             if (mRequiresReload) startDictionaryLoadingTaskLocked();
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index b7a7952..c28e40d 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -29,7 +29,6 @@
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
 import android.os.Debug;
-import android.os.IBinder;
 import android.os.Message;
 import android.os.SystemClock;
 import android.preference.PreferenceActivity;
@@ -45,8 +44,6 @@
 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.EditorInfo;
 import android.view.inputmethod.ExtractedText;
@@ -63,9 +60,11 @@
 import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
 import com.android.inputmethod.deprecated.VoiceProxy;
 import com.android.inputmethod.deprecated.recorrection.Recorrection;
+import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardActionListener;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.keyboard.KeyboardSwitcher.KeyboardLayoutState;
 import com.android.inputmethod.keyboard.KeyboardView;
 import com.android.inputmethod.keyboard.LatinKeyboard;
 import com.android.inputmethod.keyboard.LatinKeyboardView;
@@ -114,6 +113,10 @@
     // Key events coming any faster than this are long-presses.
     private static final int QUICK_PRESS = 200;
 
+    private static final int SCREEN_ORIENTATION_CHANGE_DETECTION_DELAY = 2;
+    private static final int ACCUMULATE_START_INPUT_VIEW_DELAY = 20;
+    private static final int RESTORE_KEYBOARD_STATE_DELAY = 500;
+
     /**
      * The name of the scheme used by the Package Manager to warn of a new package installation,
      * replacement or removal.
@@ -142,8 +145,6 @@
     private Suggest mSuggest;
     private CompletionInfo[] mApplicationSpecifiedCompletions;
 
-    private AlertDialog mOptionsDialog;
-
     private InputMethodManagerCompatWrapper mImm;
     private Resources mResources;
     private SharedPreferences mPrefs;
@@ -156,6 +157,7 @@
     private UserDictionary mUserDictionary;
     private UserBigramDictionary mUserBigramDictionary;
     private UserUnigramDictionary mUserUnigramDictionary;
+    private boolean mIsUserDictionaryAvaliable;
 
     // TODO: Create an inner class to group options and pseudo-options to improve readability.
     // These variables are initialized according to the {@link EditorInfo#inputType}.
@@ -168,7 +170,6 @@
     private WordComposer mWordComposer = new WordComposer();
     private CharSequence mBestWord;
     private boolean mHasUncommittedTypedChars;
-    private boolean mHasDictionary;
     // 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.
@@ -178,7 +179,6 @@
 
     private int mCorrectionMode;
     private int mCommittedLength;
-    private int mOrientation;
     // Keep track of the last selection range to decide if we need to show word alternatives
     private int mLastSelectionStart;
     private int mLastSelectionEnd;
@@ -197,6 +197,11 @@
     // TODO: Move this flag to VoiceProxy
     private boolean mConfigurationChanging;
 
+    // Member variables for remembering the current device orientation.
+    private int mDisplayOrientation;
+    private int mDisplayWidth;
+    private int mDisplayHeight;
+
     // Object for reacting to adding/removing a dictionary pack.
     private BroadcastReceiver mDictionaryPackInstallReceiver =
             new DictionaryPackInstallBroadcastReceiver(this);
@@ -204,7 +209,6 @@
     // Keeps track of most recently inserted text (multi-character key) for reverting
     private CharSequence mEnteredText;
 
-
     public final UIHandler mHandler = new UIHandler(this);
 
     public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
@@ -216,6 +220,30 @@
         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;
+        private static final int MSG_CONFIRM_ORIENTATION_CHANGE = 8;
+        private static final int MSG_START_INPUT_VIEW = 9;
+        private static final int MSG_RESTORE_KEYBOARD_LAYOUT = 10;
+
+        private static class OrientationChangeArgs {
+            public final int mOldWidth;
+            public final int mOldHeight;
+            private int mRetryCount;
+
+            public OrientationChangeArgs(int oldw, int oldh) {
+                mOldWidth = oldw;
+                mOldHeight = oldh;
+                mRetryCount = 0;
+            }
+
+            public boolean hasTimedOut() {
+                mRetryCount++;
+                return mRetryCount >= 10;
+            }
+
+            public boolean hasOrientationChangeFinished(DisplayMetrics dm) {
+                return dm.widthPixels != mOldWidth && dm.heightPixels != mOldHeight;
+            }
+        }
 
         public UIHandler(LatinIME outerInstance) {
             super(outerInstance);
@@ -264,6 +292,25 @@
                             (LatinKeyboard)msg.obj);
                 }
                 break;
+            case MSG_CONFIRM_ORIENTATION_CHANGE: {
+                final OrientationChangeArgs args = (OrientationChangeArgs)msg.obj;
+                final Resources res = latinIme.mResources;
+                final DisplayMetrics dm = res.getDisplayMetrics();
+                if (args.hasTimedOut() || args.hasOrientationChangeFinished(dm)) {
+                    latinIme.setDisplayGeometry(res.getConfiguration(), dm);
+                } else {
+                    // It seems orientation changing is on going.
+                    postConfirmOrientationChange(args);
+                }
+                break;
+            }
+            case MSG_START_INPUT_VIEW:
+                latinIme.onStartInputView((EditorInfo)msg.obj, false);
+                break;
+            case MSG_RESTORE_KEYBOARD_LAYOUT:
+                removeMessages(MSG_UPDATE_SHIFT_STATE);
+                ((KeyboardLayoutState)msg.obj).restore();
+                break;
             }
         }
 
@@ -353,6 +400,49 @@
         public boolean isAcceptingDoubleSpaces() {
             return hasMessages(MSG_SPACE_TYPED);
         }
+
+        public void postRestoreKeyboardLayout() {
+            final LatinIME latinIme = getOuterInstance();
+            final KeyboardLayoutState state = latinIme.mKeyboardSwitcher.getKeyboardState();
+            if (state.isValid()) {
+                removeMessages(MSG_RESTORE_KEYBOARD_LAYOUT);
+                sendMessageDelayed(
+                        obtainMessage(MSG_RESTORE_KEYBOARD_LAYOUT, state),
+                        RESTORE_KEYBOARD_STATE_DELAY);
+            }
+        }
+
+        private void postConfirmOrientationChange(OrientationChangeArgs args) {
+            removeMessages(MSG_CONFIRM_ORIENTATION_CHANGE);
+            // Will confirm whether orientation change has finished or not again.
+            sendMessageDelayed(obtainMessage(MSG_CONFIRM_ORIENTATION_CHANGE, args),
+                    SCREEN_ORIENTATION_CHANGE_DETECTION_DELAY);
+        }
+
+        public void startOrientationChanging(int oldw, int oldh) {
+            postConfirmOrientationChange(new OrientationChangeArgs(oldw, oldh));
+            final LatinIME latinIme = getOuterInstance();
+            latinIme.mKeyboardSwitcher.getKeyboardState().save();
+            postRestoreKeyboardLayout();
+        }
+
+        public boolean postStartInputView(EditorInfo attribute) {
+            if (hasMessages(MSG_CONFIRM_ORIENTATION_CHANGE) || hasMessages(MSG_START_INPUT_VIEW)) {
+                removeMessages(MSG_START_INPUT_VIEW);
+                // Postpone onStartInputView by ACCUMULATE_START_INPUT_VIEW_DELAY and see if
+                // orientation change has finished.
+                sendMessageDelayed(obtainMessage(MSG_START_INPUT_VIEW, attribute),
+                        ACCUMULATE_START_INPUT_VIEW_DELAY);
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private void setDisplayGeometry(Configuration conf, DisplayMetrics metric) {
+        mDisplayOrientation = conf.orientation;
+        mDisplayWidth = metric.widthPixels;
+        mDisplayHeight = metric.heightPixels;
     }
 
     @Override
@@ -361,14 +451,15 @@
         mPrefs = prefs;
         LatinImeLogger.init(this, prefs);
         LanguageSwitcherProxy.init(this, prefs);
-        SubtypeSwitcher.init(this, prefs);
+        InputMethodManagerCompatWrapper.init(this);
+        SubtypeSwitcher.init(this);
         KeyboardSwitcher.init(this, prefs);
         Recorrection.init(this, prefs);
         AccessibilityUtils.init(this, prefs);
 
         super.onCreate();
 
-        mImm = InputMethodManagerCompatWrapper.getInstance(this);
+        mImm = InputMethodManagerCompatWrapper.getInstance();
         mInputMethodId = Utils.getInputMethodId(mImm, getPackageName());
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         mKeyboardSwitcher = KeyboardSwitcher.getInstance();
@@ -391,7 +482,7 @@
             }
         }
 
-        mOrientation = res.getConfiguration().orientation;
+        setDisplayGeometry(res.getConfiguration(), res.getDisplayMetrics());
 
         // Register to receive ringer mode change and network state change.
         // Also receive installation and removal of a dictionary pack.
@@ -418,7 +509,7 @@
         if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
         if (null == mSubtypeSwitcher) mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         mSettingsValues = new Settings.Values(mPrefs, this, mSubtypeSwitcher.getInputLocaleStr());
-        resetContactsDictionary();
+        resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
     }
 
     private void initSuggest() {
@@ -427,8 +518,12 @@
 
         final Resources res = mResources;
         final Locale savedLocale = Utils.setSystemLocale(res, keyboardLocale);
+        final ContactsDictionary oldContactsDictionary;
         if (mSuggest != null) {
+            oldContactsDictionary = mSuggest.getContactsDictionary();
             mSuggest.close();
+        } else {
+            oldContactsDictionary = null;
         }
 
         int mainDicResId = Utils.getMainDictionaryResourceId(res);
@@ -440,8 +535,9 @@
 
         mUserDictionary = new UserDictionary(this, localeStr);
         mSuggest.setUserDictionary(mUserDictionary);
+        mIsUserDictionaryAvaliable = mUserDictionary.isEnabled();
 
-        resetContactsDictionary();
+        resetContactsDictionary(oldContactsDictionary);
 
         mUserUnigramDictionary
                 = new UserUnigramDictionary(this, this, localeStr, Suggest.DIC_USER_UNIGRAM);
@@ -456,11 +552,36 @@
         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);
+    /**
+     * Resets the contacts dictionary in mSuggest according to the user settings.
+     *
+     * This method takes an optional contacts dictionary to use. Since the contacts dictionary
+     * does not depend on the locale, it can be reused across different instances of Suggest.
+     * The dictionary will also be opened or closed as necessary depending on the settings.
+     *
+     * @param oldContactsDictionary an optional dictionary to use, or null
+     */
+    private void resetContactsDictionary(final ContactsDictionary oldContactsDictionary) {
+        final boolean shouldSetDictionary = (null != mSuggest && mSettingsValues.mUseContactsDict);
+
+        final ContactsDictionary dictionaryToUse;
+        if (!shouldSetDictionary) {
+            // Make sure the dictionary is closed. If it is already closed, this is a no-op,
+            // so it's safe to call it anyways.
+            if (null != oldContactsDictionary) oldContactsDictionary.close();
+            dictionaryToUse = null;
+        } else if (null != oldContactsDictionary) {
+            // Make sure the old contacts dictionary is opened. If it is already open, this is a
+            // no-op, so it's safe to call it anyways.
+            oldContactsDictionary.reopen(this);
+            dictionaryToUse = oldContactsDictionary;
+        } else {
+            dictionaryToUse = new ContactsDictionary(this, Suggest.DIC_CONTACTS);
+        }
+
+        if (null != mSuggest) {
+            mSuggest.setContactsDictionary(dictionaryToUse);
+        }
     }
 
     /* package private */ void resetSuggestMainDict() {
@@ -488,11 +609,11 @@
     public void onConfigurationChanged(Configuration conf) {
         mSubtypeSwitcher.onConfigurationChanged(conf);
         // If orientation changed while predicting, commit the change
-        if (conf.orientation != mOrientation) {
-            InputConnection ic = getCurrentInputConnection();
+        if (conf.orientation != mDisplayOrientation) {
+            mHandler.startOrientationChanging(mDisplayWidth, mDisplayHeight);
+            final InputConnection ic = getCurrentInputConnection();
             commitTyped(ic);
             if (ic != null) ic.finishComposingText(); // For voice input
-            mOrientation = conf.orientation;
             if (isShowingOptionDialog())
                 mOptionsDialog.dismiss();
         }
@@ -529,6 +650,11 @@
 
     @Override
     public void onStartInputView(EditorInfo attribute, boolean restarting) {
+        mHandler.postRestoreKeyboardLayout();
+        if (mHandler.postStartInputView(attribute)) {
+            return;
+        }
+
         final KeyboardSwitcher switcher = mKeyboardSwitcher;
         LatinKeyboardView inputView = switcher.getKeyboardView();
 
@@ -550,8 +676,9 @@
         // know now whether this is a password text field, because we need to know now whether we
         // want to enable the voice button.
         final VoiceProxy voiceIme = mVoiceProxy;
-        voiceIme.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(attribute.inputType)
-                || InputTypeCompatUtils.isVisiblePasswordInputType(attribute.inputType));
+        final int inputType = (attribute != null) ? attribute.inputType : 0;
+        voiceIme.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(inputType)
+                || InputTypeCompatUtils.isVisiblePasswordInputType(inputType));
 
         initializeInputAttributes(attribute);
 
@@ -576,10 +703,7 @@
         LanguageSwitcherProxy.loadSettings();
 
         if (mSubtypeSwitcher.isKeyboardMode()) {
-            switcher.loadKeyboard(attribute,
-                    mSubtypeSwitcher.isShortcutImeEnabled() && voiceIme.isVoiceButtonEnabled(),
-                    voiceIme.isVoiceButtonOnPrimary());
-            switcher.updateShiftState();
+            switcher.loadKeyboard(attribute, mSettingsValues);
         }
 
         if (mCandidateView != null)
@@ -588,8 +712,6 @@
         // Delay updating suggestions because keyboard input view may not be shown at this point.
         mHandler.postUpdateSuggestions();
 
-        updateCorrectionMode();
-
         inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn,
                 mSettingsValues.mKeyPreviewPopupDismissDelay);
         inputView.setProximityCorrectionEnabled(true);
@@ -668,7 +790,6 @@
         super.onFinishInput();
 
         LatinImeLogger.commit();
-        mKeyboardSwitcher.onAutoCorrectionStateChanged(false);
 
         mVoiceProxy.flushVoiceInputLogs(mConfigurationChanging);
 
@@ -681,6 +802,7 @@
     @Override
     public void onFinishInputView(boolean finishingInput) {
         super.onFinishInputView(finishingInput);
+        mKeyboardSwitcher.onFinishInputView();
         KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
         if (inputView != null) inputView.cancelAllMessages();
         // Remove pending messages related to update suggestions
@@ -719,7 +841,8 @@
         final boolean selectionChanged = (newSelStart != candidatesEnd
                 || newSelEnd != candidatesEnd) && mLastSelectionStart != newSelStart;
         final boolean candidatesCleared = candidatesStart == -1 && candidatesEnd == -1;
-        if (((mComposingStringBuilder.length() > 0 && mHasUncommittedTypedChars)
+        if (!mExpectingUpdateSelection
+                && ((mComposingStringBuilder.length() > 0 && mHasUncommittedTypedChars)
                 || mVoiceProxy.isVoiceInputHighlighted())
                 && (selectionChanged || candidatesCleared)) {
             if (candidatesCleared) {
@@ -737,7 +860,7 @@
                 setPunctuationSuggestions();
             }
             TextEntryState.reset();
-            InputConnection ic = getCurrentInputConnection();
+            final InputConnection ic = getCurrentInputConnection();
             if (ic != null) {
                 ic.finishComposingText();
             }
@@ -802,7 +925,7 @@
     @Override
     public void hideWindow() {
         LatinImeLogger.commit();
-        mKeyboardSwitcher.onAutoCorrectionStateChanged(false);
+        mKeyboardSwitcher.onHideWindow();
 
         if (TRACE) Debug.stopMethodTracing();
         if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
@@ -847,7 +970,7 @@
         if (onEvaluateInputViewShown() && mCandidateViewContainer != null) {
             final boolean shouldShowCandidates = shown
                     && (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true);
-            if (isExtractViewShown()) {
+            if (isFullscreenMode()) {
                 // No need to have extra space to show the key preview.
                 mCandidateViewContainer.setMinimumHeight(0);
                 mCandidateViewContainer.setVisibility(
@@ -942,7 +1065,7 @@
                         event.getAction(), event.getKeyCode(), event.getRepeatCount(),
                         event.getDeviceId(), event.getScanCode(),
                         KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON);
-                InputConnection ic = getCurrentInputConnection();
+                final InputConnection ic = getCurrentInputConnection();
                 if (ic != null)
                     ic.sendKeyEvent(newEvent);
                 return true;
@@ -952,12 +1075,12 @@
         return super.onKeyUp(keyCode, event);
     }
 
-    public void commitTyped(InputConnection inputConnection) {
+    public void commitTyped(final InputConnection ic) {
         if (!mHasUncommittedTypedChars) return;
         mHasUncommittedTypedChars = false;
         if (mComposingStringBuilder.length() > 0) {
-            if (inputConnection != null) {
-                inputConnection.commitText(mComposingStringBuilder, 1);
+            if (ic != null) {
+                ic.commitText(mComposingStringBuilder, 1);
             }
             mCommittedLength = mComposingStringBuilder.length();
             TextEntryState.acceptedTyped(mComposingStringBuilder);
@@ -968,7 +1091,7 @@
     }
 
     public boolean getCurrentAutoCapsState() {
-        InputConnection ic = getCurrentInputConnection();
+        final InputConnection ic = getCurrentInputConnection();
         EditorInfo ei = getCurrentInputEditorInfo();
         if (mSettingsValues.mAutoCap && ic != null && ei != null
                 && ei.inputType != InputType.TYPE_NULL) {
@@ -992,25 +1115,13 @@
         }
     }
 
-    private static boolean canBeFollowedByPeriod(final int codePoint) {
-        // TODO: Check again whether there really ain't a better way to check this.
-        // TODO: This should probably be language-dependant...
-        return Character.isLetterOrDigit(codePoint)
-                || codePoint == Keyboard.CODE_SINGLE_QUOTE
-                || codePoint == Keyboard.CODE_DOUBLE_QUOTE
-                || codePoint == Keyboard.CODE_CLOSING_PARENTHESIS
-                || codePoint == Keyboard.CODE_CLOSING_SQUARE_BRACKET
-                || codePoint == Keyboard.CODE_CLOSING_CURLY_BRACKET
-                || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET;
-    }
-
     private void maybeDoubleSpace() {
         if (mCorrectionMode == Suggest.CORRECTION_NONE) return;
         final InputConnection ic = getCurrentInputConnection();
         if (ic == null) return;
         final CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
         if (lastThree != null && lastThree.length() == 3
-                && canBeFollowedByPeriod(lastThree.charAt(0))
+                && Utils.canBeFollowedByPeriod(lastThree.charAt(0))
                 && lastThree.charAt(1) == Keyboard.CODE_SPACE
                 && lastThree.charAt(2) == Keyboard.CODE_SPACE
                 && mHandler.isAcceptingDoubleSpaces()) {
@@ -1026,10 +1137,8 @@
         }
     }
 
-    private void maybeRemovePreviousPeriod(CharSequence text) {
-        final InputConnection ic = getCurrentInputConnection();
-        if (ic == null) return;
-
+    // "ic" must not null
+    private void maybeRemovePreviousPeriod(final InputConnection ic, CharSequence text) {
         // When the text's first character is '.', remove the previous period
         // if there is one.
         CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
@@ -1069,25 +1178,31 @@
     }
 
     private void onSettingsKeyPressed() {
-        if (isShowingOptionDialog())
-            return;
+        if (isShowingOptionDialog()) return;
         if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) {
             showSubtypeSelectorAndSettings();
-        } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) {
+        } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm, false /* exclude aux subtypes */)) {
             showOptionsMenu();
         } else {
             launchSettings();
         }
     }
 
-    private void onSettingsKeyLongPressed() {
-        if (!isShowingOptionDialog()) {
-            if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) {
+    // Virtual codes representing custom requests.  These are used in onCustomRequest() below.
+    public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1;
+
+    @Override
+    public boolean onCustomRequest(int requestCode) {
+        if (isShowingOptionDialog()) return false;
+        switch (requestCode) {
+        case CODE_SHOW_INPUT_METHOD_PICKER:
+            if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm, true /* include aux subtypes */)) {
                 mImm.showInputMethodPicker();
-            } else {
-                launchSettings();
+                return true;
             }
+            return false;
         }
+        return false;
     }
 
     private boolean isShowingOptionDialog() {
@@ -1131,15 +1246,6 @@
         case Keyboard.CODE_SETTINGS:
             onSettingsKeyPressed();
             break;
-        case Keyboard.CODE_SETTINGS_LONGPRESS:
-            onSettingsKeyLongPressed();
-            break;
-        case LatinKeyboard.CODE_NEXT_LANGUAGE:
-            toggleLanguage(true);
-            break;
-        case LatinKeyboard.CODE_PREV_LANGUAGE:
-            toggleLanguage(false);
-            break;
         case Keyboard.CODE_CAPSLOCK:
             switcher.toggleCapsLock();
             break;
@@ -1174,12 +1280,12 @@
     @Override
     public void onTextInput(CharSequence text) {
         mVoiceProxy.commitVoiceInput();
-        InputConnection ic = getCurrentInputConnection();
+        final InputConnection ic = getCurrentInputConnection();
         if (ic == null) return;
         mRecorrection.abortRecorrection(false);
         ic.beginBatchEdit();
         commitTyped(ic);
-        maybeRemovePreviousPeriod(text);
+        maybeRemovePreviousPeriod(ic, text);
         ic.commitText(text, 1);
         ic.endBatchEdit();
         mKeyboardSwitcher.updateShiftState();
@@ -1229,14 +1335,14 @@
 
         TextEntryState.backspace();
         if (TextEntryState.isUndoCommit()) {
-            revertLastWord(deleteChar);
+            revertLastWord(ic);
             ic.endBatchEdit();
             return;
         }
         if (justReplacedDoubleSpace) {
-            if (revertDoubleSpace()) {
-              ic.endBatchEdit();
-              return;
+            if (revertDoubleSpace(ic)) {
+                ic.endBatchEdit();
+                return;
             }
         }
 
@@ -1251,7 +1357,7 @@
                 // different behavior only in the case of picking the first
                 // suggestion (typed word).  It's intentional to have made this
                 // inconsistent with backspacing after selecting other suggestions.
-                revertLastWord(true /* deleteChar */);
+                revertLastWord(ic);
             } else {
                 sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
                 if (mDeleteCount > DELETE_ACCELERATE_AT) {
@@ -1297,7 +1403,8 @@
         }
 
         int code = primaryCode;
-        if (isAlphabet(code) && isSuggestionsRequested() && !isCursorTouchingWord()) {
+        if ((isAlphabet(code) || mSettingsValues.isSymbolExcludedFromWordSeparators(code))
+                && isSuggestionsRequested() && !isCursorTouchingWord()) {
             if (!mHasUncommittedTypedChars) {
                 mHasUncommittedTypedChars = true;
                 mComposingStringBuilder.setLength(0);
@@ -1334,7 +1441,7 @@
             }
             mComposingStringBuilder.append((char) code);
             mWordComposer.add(code, keyCodes, x, y);
-            InputConnection ic = getCurrentInputConnection();
+            final InputConnection ic = getCurrentInputConnection();
             if (ic != null) {
                 // If it's the first letter, make note of auto-caps state
                 if (mWordComposer.size() == 1) {
@@ -1378,9 +1485,8 @@
             // 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.
-            final boolean shouldAutoCorrect =
-                    (mSettingsValues.mAutoCorrectEnabled || mSettingsValues.mQuickFixes)
-                    && !mInputTypeNoAutoCorrect && mHasDictionary;
+            final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
+                    && !mInputTypeNoAutoCorrect;
             if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
                 pickedDefault = pickDefaultSuggestion(primaryCode);
             } else {
@@ -1455,7 +1561,7 @@
     public boolean isShowingSuggestionsStrip() {
         return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
                 || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
-                        && mOrientation == Configuration.ORIENTATION_PORTRAIT);
+                        && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT);
     }
 
     public boolean isCandidateStripVisible() {
@@ -1514,10 +1620,17 @@
 
         final WordComposer wordComposer = mWordComposer;
         // TODO: May need a better way of retrieving previous word
-        CharSequence prevWord = EditingUtils.getPreviousWord(getCurrentInputConnection(),
-                mSettingsValues.mWordSeparators);
-        SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
-                mKeyboardSwitcher.getKeyboardView(), wordComposer, prevWord);
+        final InputConnection ic = getCurrentInputConnection();
+        final CharSequence prevWord;
+        if (null == ic) {
+            prevWord = null;
+        } else {
+            prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
+        }
+        // getSuggestedWordBuilder handles gracefully a null value of prevWord
+        final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
+                mKeyboardSwitcher.getKeyboardView(), wordComposer, prevWord,
+                mKeyboardSwitcher.getLatinKeyboard().getProximityInfo());
 
         boolean autoCorrectionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection();
         final CharSequence typedWord = wordComposer.getTypedWord();
@@ -1594,15 +1707,15 @@
                 mSettingsValues.mWordSeparators);
 
         final boolean recorrecting = TextEntryState.isRecorrecting();
-        InputConnection ic = getCurrentInputConnection();
+        final InputConnection ic = getCurrentInputConnection();
         if (ic != null) {
             ic.beginBatchEdit();
         }
         if (mApplicationSpecifiedCompletionOn && mApplicationSpecifiedCompletions != null
                 && index >= 0 && index < mApplicationSpecifiedCompletions.length) {
-            CompletionInfo ci = mApplicationSpecifiedCompletions[index];
             if (ic != null) {
-                ic.commitCompletion(ci);
+                final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
+                ic.commitCompletion(completionInfo);
             }
             mCommittedLength = suggestion.length();
             if (mCandidateView != null) {
@@ -1626,7 +1739,12 @@
             // 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 int rawPrimaryCode = suggestion.charAt(0);
+            // Maybe apply the "bidi mirrored" conversions for parentheses
+            final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard();
+            final int primaryCode = keyboard.mIsRtlKeyboard
+                    ? Key.getRtlParenthesisCode(rawPrimaryCode) : rawPrimaryCode;
+
             final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : "";
             final int toLeft = (ic == null || TextUtils.isEmpty(beforeText))
                     ? 0 : beforeText.charAt(0);
@@ -1663,9 +1781,10 @@
             sendMagicSpace();
         }
 
-        // We should show the hint if the user pressed the first entry AND either:
+        // We should show the "Touch again to save" hint if the user pressed the first entry
+        // AND either:
         // - There is no dictionary (we know that because we tried to load it => null != mSuggest
-        //   AND mHasDictionary is false)
+        //   AND mSuggest.hasMainDictionary() is false)
         // - There is a dictionary and the word is not in it
         // Please note that if mSuggest is null, it means that everything is off: suggestion
         // and correction, so we shouldn't try to show the hint
@@ -1673,7 +1792,7 @@
         // to do with the autocorrection setting.
         final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
                 // If there is no dictionary the hint should be shown.
-                && (!mHasDictionary
+                && (!mSuggest.hasMainDictionary()
                         // If "suggestion" is not in the dictionary, the hint should be shown.
                         || !AutoCorrection.isValidWord(
                                 mSuggest.getUnigramDictionaries(), suggestion, true));
@@ -1694,7 +1813,11 @@
             // take a noticeable delay to update them which may feel uneasy.
         }
         if (showingAddToDictionaryHint) {
-            mCandidateView.showAddToDictionaryHint(suggestion);
+            if (mIsUserDictionaryAvaliable) {
+                mCandidateView.showAddToDictionaryHint(suggestion);
+            } else {
+                mHandler.postUpdateSuggestions();
+            }
         }
         if (ic != null) {
             ic.endBatchEdit();
@@ -1705,10 +1828,10 @@
      * Commits the chosen word to the text field and saves it for later retrieval.
      */
     private void commitBestWord(CharSequence bestWord) {
-        KeyboardSwitcher switcher = mKeyboardSwitcher;
+        final KeyboardSwitcher switcher = mKeyboardSwitcher;
         if (!switcher.isKeyboardAvailable())
             return;
-        InputConnection ic = getCurrentInputConnection();
+        final InputConnection ic = getCurrentInputConnection();
         if (ic != null) {
             mVoiceProxy.rememberReplacedWord(bestWord, mSettingsValues.mWordSeparators);
             SuggestedWords suggestedWords = mCandidateView.getSuggestions();
@@ -1733,7 +1856,8 @@
         final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(),
                 mSettingsValues.mWordSeparators);
         SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
-                mKeyboardSwitcher.getKeyboardView(), sEmptyWordComposer, prevWord);
+                mKeyboardSwitcher.getKeyboardView(), sEmptyWordComposer, prevWord,
+                mKeyboardSwitcher.getLatinKeyboard().getProximityInfo());
 
         if (builder.size() > 0) {
             // Explicitly supply an empty typed word (the no-second-arg version of
@@ -1788,16 +1912,19 @@
             // 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(),
-                    mSettingsValues.mWordSeparators);
-            if (!TextUtils.isEmpty(prevWord)) {
-                mUserBigramDictionary.addBigrams(prevWord.toString(), suggestion.toString());
+            final InputConnection ic = getCurrentInputConnection();
+            if (null != ic) {
+                final CharSequence prevWord =
+                        EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
+                if (!TextUtils.isEmpty(prevWord)) {
+                    mUserBigramDictionary.addBigrams(prevWord.toString(), suggestion.toString());
+                }
             }
         }
     }
 
     public boolean isCursorTouchingWord() {
-        InputConnection ic = getCurrentInputConnection();
+        final InputConnection ic = getCurrentInputConnection();
         if (ic == null) return false;
         CharSequence toLeft = ic.getTextBeforeCursor(1, 0);
         CharSequence toRight = ic.getTextAfterCursor(1, 0);
@@ -1814,36 +1941,34 @@
         return false;
     }
 
-    private boolean sameAsTextBeforeCursor(InputConnection ic, CharSequence text) {
+    // "ic" must not null
+    private boolean sameAsTextBeforeCursor(final InputConnection ic, CharSequence text) {
         CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0);
         return TextUtils.equals(text, beforeText);
     }
 
-    private void revertLastWord(boolean deleteChar) {
+    // "ic" must not null
+    private void revertLastWord(final InputConnection ic) {
         if (mHasUncommittedTypedChars || mComposingStringBuilder.length() <= 0) {
             sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
             return;
         }
 
-        final InputConnection ic = getCurrentInputConnection();
-        final CharSequence punctuation = ic.getTextBeforeCursor(1, 0);
-        if (deleteChar) ic.deleteSurroundingText(1, 0);
+        final CharSequence separator = ic.getTextBeforeCursor(1, 0);
+        ic.deleteSurroundingText(1, 0);
         final CharSequence textToTheLeft = ic.getTextBeforeCursor(mCommittedLength, 0);
-        final int toDeleteLength = (!TextUtils.isEmpty(textToTheLeft)
-                && mSettingsValues.isWordSeparator(textToTheLeft.charAt(0)))
-                ? mCommittedLength - 1 : mCommittedLength;
-        ic.deleteSurroundingText(toDeleteLength, 0);
+        ic.deleteSurroundingText(mCommittedLength, 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)
-                && mSettingsValues.isWordSeparator(punctuation.charAt(0))
+        // Re-insert "separator" only when the deleted character was word separator and the
+        // composing text wasn't equal to the auto-corrected text which can be found before
+        // the cursor.
+        if (!TextUtils.isEmpty(separator)
+                && mSettingsValues.isWordSeparator(separator.charAt(0))
                 && !TextUtils.equals(mComposingStringBuilder, textToTheLeft)) {
             ic.commitText(mComposingStringBuilder, 1);
             TextEntryState.acceptedTyped(mComposingStringBuilder);
-            ic.commitText(punctuation, 1);
-            TextEntryState.typedCharacter(punctuation.charAt(0), true,
+            ic.commitText(separator, 1);
+            TextEntryState.typedCharacter(separator.charAt(0), true,
                     WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
             // Clear composing text
             mComposingStringBuilder.setLength(0);
@@ -1856,9 +1981,9 @@
         mHandler.postUpdateSuggestions();
     }
 
-    private boolean revertDoubleSpace() {
+    // "ic" must not null
+    private boolean revertDoubleSpace(final InputConnection ic) {
         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);
@@ -1894,32 +2019,19 @@
             setInputView(mKeyboardSwitcher.onCreateInputView());
         }
         // Reload keyboard because the current language has been changed.
-        mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(),
-                mSubtypeSwitcher.isShortcutImeEnabled() && mVoiceProxy.isVoiceButtonEnabled(),
-                mVoiceProxy.isVoiceButtonOnPrimary());
+        mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettingsValues);
         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 onPress(int primaryCode, boolean withSliding) {
-        if (mKeyboardSwitcher.isVibrateAndSoundFeedbackRequired()) {
+        final KeyboardSwitcher switcher = mKeyboardSwitcher;
+        switcher.registerWindowWidth();
+        if (switcher.isVibrateAndSoundFeedbackRequired()) {
             vibrate();
             playKeyClick(primaryCode);
         }
-        KeyboardSwitcher switcher = mKeyboardSwitcher;
         final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
         if (distinctMultiTouch && primaryCode == Keyboard.CODE_SHIFT) {
             switcher.onPressShift(withSliding);
@@ -2015,9 +2127,8 @@
 
     private void updateCorrectionMode() {
         // TODO: cleanup messy flags
-        mHasDictionary = mSuggest != null ? mSuggest.hasMainDictionary() : false;
-        final boolean shouldAutoCorrect = (mSettingsValues.mAutoCorrectEnabled
-                || mSettingsValues.mQuickFixes) && !mInputTypeNoAutoCorrect && mHasDictionary;
+        final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
+                && !mInputTypeNoAutoCorrect;
         mCorrectionMode = (shouldAutoCorrect && mSettingsValues.mAutoCorrectEnabled)
                 ? Suggest.CORRECTION_FULL
                 : (shouldAutoCorrect ? Suggest.CORRECTION_BASIC : Suggest.CORRECTION_NONE);
@@ -2031,7 +2142,13 @@
 
     private void updateAutoTextEnabled() {
         if (mSuggest == null) return;
-        mSuggest.setQuickFixesEnabled(mSettingsValues.mQuickFixes
+        // We want to use autotext if the settings are asking for auto corrections, and if
+        // the input language is the same as the system language (because autotext will only
+        // work in the system language so if we are entering text in a different language we
+        // do not want it on).
+        // We used to look at the "quick fixes" option instead of mAutoCorrectEnabled, but
+        // this option was redundant and confusing and therefore removed.
+        mSuggest.setQuickFixesEnabled(mSettingsValues.mAutoCorrectEnabled
                 && SubtypeSwitcher.getInstance().isSystemLanguageSameAsInputLanguage());
     }
 
@@ -2048,14 +2165,14 @@
     }
 
     protected void launchSettings() {
-        launchSettings(Settings.class);
+        launchSettingsClass(Settings.class);
     }
 
     public void launchDebugSettings() {
-        launchSettings(DebugSettings.class);
+        launchSettingsClass(DebugSettings.class);
     }
 
-    protected void launchSettings(Class<? extends PreferenceActivity> settingsClass) {
+    protected void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
         handleClose();
         Intent intent = new Intent();
         intent.setClass(LatinIME.this, settingsClass);
@@ -2088,7 +2205,10 @@
                 }
             }
         };
-        showOptionsMenuInternal(title, items, listener);
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this)
+                .setItems(items, listener)
+                .setTitle(title);
+        showOptionDialogInternal(builder.create());
     }
 
     private void showOptionsMenu() {
@@ -2111,28 +2231,10 @@
                 }
             }
         };
-        showOptionsMenuInternal(title, items, listener);
-    }
-
-    private void showOptionsMenuInternal(CharSequence title, CharSequence[] items,
-            DialogInterface.OnClickListener listener) {
-        final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
-        if (windowToken == null) return;
-        AlertDialog.Builder builder = new AlertDialog.Builder(this);
-        builder.setCancelable(true);
-        builder.setIcon(R.drawable.ic_dialog_keyboard);
-        builder.setNegativeButton(android.R.string.cancel, null);
-        builder.setItems(items, listener);
-        builder.setTitle(title);
-        mOptionsDialog = builder.create();
-        mOptionsDialog.setCanceledOnTouchOutside(true);
-        Window window = mOptionsDialog.getWindow();
-        WindowManager.LayoutParams lp = window.getAttributes();
-        lp.token = windowToken;
-        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
-        window.setAttributes(lp);
-        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
-        mOptionsDialog.show();
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this)
+                .setItems(items, listener)
+                .setTitle(title);
+        showOptionDialogInternal(builder.create());
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java
deleted file mode 100644
index eb740e1..0000000
--- a/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java
+++ /dev/null
@@ -1,29 +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 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 33e9bc3..4c2627b 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -16,13 +16,6 @@
 
 package com.android.inputmethod.latin;
 
-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 com.android.inputmethodcommon.InputMethodSettingsActivity;
-
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -40,12 +33,20 @@
 import android.preference.Preference.OnPreferenceClickListener;
 import android.preference.PreferenceGroup;
 import android.preference.PreferenceScreen;
-import android.speech.SpeechRecognizer;
 import android.text.TextUtils;
 import android.text.method.LinkMovementMethod;
 import android.util.Log;
+import android.view.inputmethod.EditorInfo;
 import android.widget.TextView;
 
+import com.android.inputmethod.compat.CompatUtils;
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
+import com.android.inputmethod.compat.InputTypeCompatUtils;
+import com.android.inputmethod.compat.VibratorCompatWrapper;
+import com.android.inputmethod.deprecated.VoiceProxy;
+import com.android.inputmethodcommon.InputMethodSettingsActivity;
+
 import java.util.Arrays;
 import java.util.Locale;
 
@@ -60,7 +61,7 @@
     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";
+    public static final String PREF_SHOW_SETTINGS_KEY = "show_settings_key";
     public static final String PREF_VOICE_SETTINGS_KEY = "voice_mode";
     public static final String PREF_INPUT_LANGUAGE = "input_language";
     public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
@@ -68,7 +69,6 @@
 
     public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key";
     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";
@@ -103,6 +103,7 @@
         public final String mMagicSpaceSwappers;
         public final String mSuggestPuncs;
         public final SuggestedWords mSuggestPuncList;
+        private final String mSymbolsExcludedFromWordSeparators;
 
         // From preferences:
         public final boolean mSoundOn; // Sound setting private to Latin IME (see mSilentModeOn)
@@ -110,7 +111,6 @@
         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
@@ -119,6 +119,10 @@
         public final boolean mBigramPredictionEnabled;
         public final boolean mUseContactsDict;
 
+        private final boolean mShowSettingsKey;
+        private final boolean mVoiceKeyEnabled;
+        private final boolean mVoiceKeyOnMain;
+
         public Values(final SharedPreferences prefs, final Context context,
                 final String localeStr) {
             final Resources res = context.getResources();
@@ -149,10 +153,13 @@
             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), "");
+            final String symbolsExcludedFromWordSeparators =
+                    res.getString(R.string.symbols_excluded_from_word_separators);
+            for (int i = symbolsExcludedFromWordSeparators.length() - 1; i >= 0; --i) {
+                wordSeparators = wordSeparators.replace(
+                        symbolsExcludedFromWordSeparators.substring(i, i + 1), "");
             }
+            mSymbolsExcludedFromWordSeparators = symbolsExcludedFromWordSeparators;
             mWordSeparators = wordSeparators;
             mSuggestPuncs = res.getString(R.string.suggested_punctuations);
             // TODO: it would be nice not to recreate this each time we change the configuration
@@ -163,21 +170,25 @@
             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);
+            final boolean defaultShowSettingsKey = res.getBoolean(
+                    R.bool.config_default_show_settings_key);
+            mShowSettingsKey = prefs.getBoolean(Settings.PREF_SHOW_SETTINGS_KEY,
+                    defaultShowSettingsKey);
+            final String voiceModeMain = res.getString(R.string.voice_mode_main);
+            final String voiceModeOff = res.getString(R.string.voice_mode_off);
+            final String voiceMode = prefs.getString(PREF_VOICE_SETTINGS_KEY, voiceModeMain);
+            mVoiceKeyEnabled = voiceMode != null && !voiceMode.equals(voiceModeOff);
+            mVoiceKeyOnMain = voiceMode != null && voiceMode.equals(voiceModeMain);
 
             Utils.setSystemLocale(res, savedLocale);
         }
@@ -190,6 +201,10 @@
             return mWordSeparators.contains(String.valueOf((char)code));
         }
 
+        public boolean isSymbolExcludedFromWordSeparators(int code) {
+            return mSymbolsExcludedFromWordSeparators.contains(String.valueOf((char)code));
+        }
+
         public boolean isMagicSpaceStripper(int code) {
             return mMagicSpaceStrippers.contains(String.valueOf((char)code));
         }
@@ -198,17 +213,6 @@
             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,
@@ -287,13 +291,28 @@
             }
             return builder.setIsPunctuationSuggestions().build();
         }
+
+        public boolean isSettingsKeyEnabled(EditorInfo attribute) {
+            return mShowSettingsKey;
+        }
+
+        public boolean isVoiceKeyEnabled(EditorInfo attribute) {
+            final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
+            final int inputType = (attribute != null) ? attribute.inputType : 0;
+            return shortcutImeEnabled && mVoiceKeyEnabled
+                    && !InputTypeCompatUtils.isPasswordInputType(inputType);
+        }
+
+        public boolean isVoiceKeyOnMain() {
+            return mVoiceKeyOnMain;
+        }
     }
 
     private PreferenceScreen mInputLanguageSelection;
     private ListPreference mVoicePreference;
-    private ListPreference mSettingsKeyPreference;
+    private CheckBoxPreference mShowSettingsKeyPreference;
     private ListPreference mShowCorrectionSuggestionsPreference;
-    private ListPreference mAutoCorrectionThreshold;
+    private ListPreference mAutoCorrectionThresholdPreference;
     private ListPreference mKeyPreviewPopupDismissDelay;
     // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
     private CheckBoxPreference mBigramSuggestion;
@@ -304,15 +323,13 @@
 
     private AlertDialog mDialog;
 
-    private VoiceProxy.VoiceLoggerWrapper mVoiceLogger;
-
     private boolean mOkClicked = false;
     private String mVoiceModeOff;
 
     private void ensureConsistencyOfAutoCorrectionSettings() {
         final String autoCorrectionOff = getResources().getString(
                 R.string.auto_correction_threshold_mode_index_off);
-        final String currentSetting = mAutoCorrectionThreshold.getValue();
+        final String currentSetting = mAutoCorrectionThresholdPreference.getValue();
         mBigramSuggestion.setEnabled(!currentSetting.equals(autoCorrectionOff));
         mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff));
     }
@@ -340,7 +357,7 @@
         mInputLanguageSelection = (PreferenceScreen) findPreference(PREF_SUBTYPES);
         mInputLanguageSelection.setOnPreferenceClickListener(this);
         mVoicePreference = (ListPreference) findPreference(PREF_VOICE_SETTINGS_KEY);
-        mSettingsKeyPreference = (ListPreference) findPreference(PREF_SETTINGS_KEY);
+        mShowSettingsKeyPreference = (CheckBoxPreference) findPreference(PREF_SHOW_SETTINGS_KEY);
         mShowCorrectionSuggestionsPreference =
                 (ListPreference) findPreference(PREF_SHOW_SUGGESTIONS_SETTING);
         SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
@@ -349,9 +366,9 @@
         mVoiceModeOff = getString(R.string.voice_mode_off);
         mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff)
                 .equals(mVoiceModeOff));
-        mVoiceLogger = VoiceProxy.VoiceLoggerWrapper.getInstance(context);
 
-        mAutoCorrectionThreshold = (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD);
+        mAutoCorrectionThresholdPreference =
+                (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD);
         mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTIONS);
         mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS);
         mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS);
@@ -372,7 +389,7 @@
         final boolean showSettingsKeyOption = res.getBoolean(
                 R.bool.config_enable_show_settings_key_option);
         if (!showSettingsKeyOption) {
-            generalSettings.removePreference(mSettingsKeyPreference);
+            generalSettings.removePreference(mShowSettingsKeyPreference);
         }
 
         final boolean showVoiceKeyOption = res.getBoolean(
@@ -401,12 +418,6 @@
             generalSettings.removePreference(findPreference(PREF_RECORRECTION_ENABLED));
         }
 
-        final boolean showQuickFixesOption = res.getBoolean(
-                R.bool.config_enable_quick_fixes_option);
-        if (!showQuickFixesOption) {
-            textCorrectionGroup.removePreference(findPreference(PREF_QUICK_FIXES));
-        }
-
         final boolean showBigramSuggestionsOption = res.getBoolean(
                 R.bool.config_enable_bigram_suggestions_option);
         if (!showBigramSuggestionsOption) {
@@ -447,16 +458,18 @@
         }
     }
 
+    @SuppressWarnings("unused")
     @Override
     public void onResume() {
         super.onResume();
-        if (!VoiceProxy.VOICE_INSTALLED
-                || !SpeechRecognizer.isRecognitionAvailable(getActivityInternal())) {
-            getPreferenceScreen().removePreference(mVoicePreference);
-        } else {
+        final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
+        if (isShortcutImeEnabled
+                || (VoiceProxy.VOICE_INSTALLED
+                        && VoiceProxy.isRecognitionAvailable(getActivityInternal()))) {
             updateVoiceModeSummary();
+        } else {
+            getPreferenceScreen().removePreference(mVoicePreference);
         }
-        updateSettingsKeySummary();
         updateShowCorrectionSuggestionsSummary();
         updateKeyPreviewPopupDelaySummary();
     }
@@ -488,7 +501,6 @@
         mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff)
                 .equals(mVoiceModeOff));
         updateVoiceModeSummary();
-        updateSettingsKeySummary();
         updateShowCorrectionSuggestionsSummary();
         updateKeyPreviewPopupDelaySummary();
     }
@@ -498,7 +510,7 @@
         if (pref == mInputLanguageSelection) {
             startActivity(CompatUtils.getInputLanguageSelectionIntent(
                     Utils.getInputMethodId(
-                            InputMethodManagerCompatWrapper.getInstance(getActivityInternal()),
+                            InputMethodManagerCompatWrapper.getInstance(),
                             getActivityInternal().getApplicationInfo().packageName), 0));
             return true;
         }
@@ -512,12 +524,6 @@
                         mShowCorrectionSuggestionsPreference.getValue())]);
     }
 
-    private void updateSettingsKeySummary() {
-        mSettingsKeyPreference.setSummary(
-                getResources().getStringArray(R.array.settings_key_modes)
-                [mSettingsKeyPreference.findIndexOfValue(mSettingsKeyPreference.getValue())]);
-    }
-
     private void updateKeyPreviewPopupDelaySummary() {
         final ListPreference lp = mKeyPreviewPopupDismissDelay;
         lp.setSummary(lp.getEntries()[lp.findIndexOfValue(lp.getValue())]);
@@ -541,6 +547,7 @@
                 [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
     }
 
+    @Override
     protected Dialog onCreateDialog(int id) {
         switch (id) {
             case VOICE_INPUT_CONFIRM_DIALOG:
@@ -549,12 +556,9 @@
                     public void onClick(DialogInterface dialog, int whichButton) {
                         if (whichButton == DialogInterface.BUTTON_NEGATIVE) {
                             mVoicePreference.setValue(mVoiceModeOff);
-                            mVoiceLogger.settingsWarningDialogCancel();
                         } else if (whichButton == DialogInterface.BUTTON_POSITIVE) {
                             mOkClicked = true;
-                            mVoiceLogger.settingsWarningDialogOk();
                         }
-                        updateVoicePreference();
                     }
                 };
                 AlertDialog.Builder builder = new AlertDialog.Builder(getActivityInternal())
@@ -583,7 +587,6 @@
                 AlertDialog dialog = builder.create();
                 mDialog = dialog;
                 dialog.setOnDismissListener(this);
-                mVoiceLogger.settingsWarningDialogShown();
                 return dialog;
             default:
                 Log.e(TAG, "unknown dialog " + id);
@@ -593,16 +596,10 @@
 
     @Override
     public void onDismiss(DialogInterface dialog) {
-        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.
             mVoicePreference.setValue(mVoiceModeOff);
         }
     }
-
-    private void updateVoicePreference() {
-        boolean isChecked = !mVoicePreference.getValue().equals(mVoiceModeOff);
-        mVoiceLogger.voiceInputSettingEnabled(isChecked);
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 8fc19ae..0a391a7 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -16,16 +16,8 @@
 
 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 android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -37,6 +29,13 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+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 java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -52,7 +51,6 @@
     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);
@@ -62,13 +60,10 @@
     private /* final */ InputMethodManagerCompatWrapper mImm;
     private /* final */ Resources mResources;
     private /* final */ ConnectivityManager mConnectivityManager;
-    private /* final */ boolean mConfigUseSpacebarLanguageSwitcher;
-    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.
@@ -81,7 +76,6 @@
     private Locale mSystemLocale;
     private Locale mInputLocale;
     private String mInputLocaleStr;
-    private String mInputMethodId;
     private VoiceProxy.VoiceInputWrapper mVoiceInputWrapper;
     /*-----------------------------------------------------------*/
 
@@ -91,9 +85,9 @@
         return sInstance;
     }
 
-    public static void init(LatinIME service, SharedPreferences prefs) {
+    public static void init(LatinIME service) {
         SubtypeLocale.init(service);
-        sInstance.initialize(service, prefs);
+        sInstance.initialize(service);
         sInstance.updateAllParameters();
     }
 
@@ -101,10 +95,10 @@
         // Intentional empty constructor for singleton.
     }
 
-    private void initialize(LatinIME service, SharedPreferences prefs) {
+    private void initialize(LatinIME service) {
         mService = service;
         mResources = service.getResources();
-        mImm = InputMethodManagerCompatWrapper.getInstance(service);
+        mImm = InputMethodManagerCompatWrapper.getInstance();
         mConnectivityManager = (ConnectivityManager) service.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
         mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
@@ -115,11 +109,9 @@
         mCurrentSubtype = null;
         mAllEnabledSubtypesOfCurrentInputMethod = null;
         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.
@@ -133,9 +125,6 @@
     // Update parameters which are changed outside LatinIME. This parameters affect UI so they
     // should be updated every time onStartInputview.
     public void updateParametersOnStartInputView() {
-        mConfigUseSpacebarLanguageSwitcher = mPrefs.getBoolean(USE_SPACEBAR_LANGUAGE_SWITCH_KEY,
-                mService.getResources().getBoolean(
-                        R.bool.config_use_spacebar_language_switcher));
         updateEnabledSubtypes();
         updateShortcutIME();
     }
@@ -170,10 +159,6 @@
                 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();
         }
     }
 
@@ -273,7 +258,6 @@
                 mVoiceInputWrapper.reset();
             }
         }
-        mLanguageBarInfo.update();
     }
 
     // Update the current input locale from Locale string.
@@ -334,7 +318,7 @@
                 // when the API level is 10 or previous.
                 mService.notifyOnCurrentInputMethodSubtypeChanged(subtype);
             }
-        }.execute();
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
     public Drawable getShortcutIcon() {
@@ -377,13 +361,17 @@
     }
 
     public boolean isShortcutImeEnabled() {
-        if (mShortcutInputMethodInfo == null)
+        if (mShortcutInputMethodInfo == null) {
             return false;
-        if (mShortcutSubtype == null)
+        }
+        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;
+        if (!mShortcutSubtype.hasOriginalObject()) {
+            return true;
+        }
         final boolean allowsImplicitlySelectedSubtypes = true;
         for (final InputMethodSubtypeCompatWrapper enabledSubtype :
                 mImm.getEnabledInputMethodSubtypeList(
@@ -427,10 +415,6 @@
         return mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
     }
 
-    public boolean useSpacebarLanguageSwitcher() {
-        return mConfigUseSpacebarLanguageSwitcher;
-    }
-
     public boolean needsToDisplayLanguage() {
         return mNeedsToDisplayLanguage;
     }
@@ -508,75 +492,6 @@
                 KeyboardSwitcher.getInstance().getKeyboardView().getWindowToken());
     }
 
-    //////////////////////////////////////
-    // Spacebar Language Switch support //
-    //////////////////////////////////////
-
-    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), locale);
@@ -609,14 +524,6 @@
         return getDisplayLanguage(getInputLocale());
     }
 
-    public String getNextInputLanguageName() {
-        return mLanguageBarInfo.getNextLanguage();
-    }
-
-    public String getPreviousInputLanguageName() {
-        return mLanguageBarInfo.getPreviousLanguage();
-    }
-
     /////////////////////////////
     // Other utility functions //
     /////////////////////////////
@@ -653,24 +560,4 @@
                 supportedLocalesString.split("\\s+"));
         return voiceInputSupportedLocales.contains(locale);
     }
-
-    private void changeToNextSubtype() {
-        final InputMethodSubtypeCompatWrapper subtype =
-                mLanguageBarInfo.getNextKeyboardSubtype();
-        switchToTargetIME(mInputMethodId, subtype);
-    }
-
-    private void changeToPreviousSubtype() {
-        final InputMethodSubtypeCompatWrapper subtype =
-                mLanguageBarInfo.getPreviousKeyboardSubtype();
-        switchToTargetIME(mInputMethodId, subtype);
-    }
-
-    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 8ae653f..a2d66f3 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -22,6 +22,8 @@
 import android.util.Log;
 import android.view.View;
 
+import com.android.inputmethod.keyboard.ProximityInfo;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -86,6 +88,7 @@
     private AutoCorrection mAutoCorrection;
 
     private Dictionary mMainDict;
+    private ContactsDictionary mContactsDict;
     private WhitelistDictionary mWhiteListDictionary;
     private final Map<String, Dictionary> mUnigramDictionaries = new HashMap<String, Dictionary>();
     private final Map<String, Dictionary> mBigramDictionaries = new HashMap<String, Dictionary>();
@@ -102,6 +105,8 @@
 
     private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
     ArrayList<CharSequence> mBigramSuggestions  = new ArrayList<CharSequence>();
+    // TODO: maybe this should be synchronized, it's quite scary as it is.
+    // TODO: if it becomes synchronized, also move initPool in the thread in initAsynchronously
     private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
     private CharSequence mTypedWord;
 
@@ -111,27 +116,39 @@
 
     private int mCorrectionMode = CORRECTION_BASIC;
 
-    public Suggest(Context context, int dictionaryResId, Locale locale) {
-        init(context, DictionaryFactory.createDictionaryFromManager(context, locale,
-                dictionaryResId));
+    public Suggest(final Context context, final int dictionaryResId, final Locale locale) {
+        initAsynchronously(context, dictionaryResId, locale);
     }
 
     /* package for test */ Suggest(Context context, File dictionary, long startOffset, long length,
             Flag[] flagArray) {
-        init(null, DictionaryFactory.createDictionaryForTest(context, dictionary, startOffset,
-                length, flagArray));
+        initSynchronously(null, DictionaryFactory.createDictionaryForTest(context, dictionary,
+                startOffset, length, flagArray));
     }
 
-    private void init(Context context, Dictionary mainDict) {
-        mMainDict = mainDict;
-        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict);
-        addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict);
+    private void initWhitelistAndAutocorrectAndPool(final Context context) {
         mWhiteListDictionary = WhitelistDictionary.init(context);
         addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
         mAutoCorrection = new AutoCorrection();
         initPool();
     }
 
+    private void initAsynchronously(final Context context, final int dictionaryResId,
+            final Locale locale) {
+        resetMainDict(context, dictionaryResId, locale);
+
+        // TODO: read the whitelist and init the pool asynchronously too.
+        // initPool should be done asynchronously but the pool is not thread-safe at the moment.
+        initWhitelistAndAutocorrectAndPool(context);
+    }
+
+    private void initSynchronously(Context context, Dictionary mainDict) {
+        mMainDict = mainDict;
+        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict);
+        addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict);
+        initWhitelistAndAutocorrectAndPool(context);
+    }
+
     private void addOrReplaceDictionary(Map<String, Dictionary> dictionaries, String key,
             Dictionary dict) {
         final Dictionary oldDict = (dict == null)
@@ -142,12 +159,18 @@
         }
     }
 
-    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);
+    public void resetMainDict(final Context context, final int dictionaryResId,
+            final Locale locale) {
+        mMainDict = null;
+        new Thread("InitializeBinaryDictionary") {
+            public void run() {
+                final Dictionary newMainDict = DictionaryFactory.createDictionaryFromManager(
+                        context, locale, dictionaryResId);
+                mMainDict = newMainDict;
+                addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict);
+                addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict);
+            }
+        }.start();
     }
 
     private void initPool() {
@@ -169,10 +192,16 @@
         mCorrectionMode = mode;
     }
 
+    // The main dictionary could have been loaded asynchronously.  Don't cache the return value
+    // of this method.
     public boolean hasMainDictionary() {
         return mMainDict != null;
     }
 
+    public ContactsDictionary getContactsDictionary() {
+        return mContactsDict;
+    }
+
     public Map<String, Dictionary> getUnigramDictionaries() {
         return mUnigramDictionaries;
     }
@@ -194,7 +223,8 @@
      * the contacts dictionary by passing null to this method. In this case no contacts dictionary
      * won't be used.
      */
-    public void setContactsDictionary(Dictionary contactsDictionary) {
+    public void setContactsDictionary(ContactsDictionary contactsDictionary) {
+        mContactsDict = contactsDictionary;
         addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
         addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
     }
@@ -243,9 +273,10 @@
      * @param prevWordForBigram previous word (used only for bigram)
      * @return suggested words object.
      */
-    public SuggestedWords getSuggestions(View view, WordComposer wordComposer,
-            CharSequence prevWordForBigram) {
-        return getSuggestedWordBuilder(view, wordComposer, prevWordForBigram).build();
+    public SuggestedWords getSuggestions(final View view, final WordComposer wordComposer,
+            final CharSequence prevWordForBigram, final ProximityInfo proximityInfo) {
+        return getSuggestedWordBuilder(view, wordComposer, prevWordForBigram,
+                proximityInfo).build();
     }
 
     private CharSequence capitalizeWord(boolean all, boolean first, CharSequence word) {
@@ -279,8 +310,9 @@
     }
 
     // TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder
-    public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer,
-            CharSequence prevWordForBigram) {
+    public SuggestedWords.Builder getSuggestedWordBuilder(final View view,
+            final WordComposer wordComposer, CharSequence prevWordForBigram,
+            final ProximityInfo proximityInfo) {
         LatinImeLogger.onStartSuggestion(prevWordForBigram);
         mAutoCorrection.init();
         mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
@@ -345,7 +377,7 @@
                 if (key.equals(DICT_KEY_USER_UNIGRAM) || key.equals(DICT_KEY_WHITELIST))
                     continue;
                 final Dictionary dictionary = mUnigramDictionaries.get(key);
-                dictionary.getWords(wordComposer, this);
+                dictionary.getWords(wordComposer, this, proximityInfo);
             }
         }
         CharSequence autoText = null;
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 84db175..c1c46fa 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.latin;
 
+import android.text.TextUtils;
 import android.view.inputmethod.CompletionInfo;
 
 import java.util.ArrayList;
@@ -30,7 +31,7 @@
     public final boolean mTypedWordValid;
     public final boolean mHasMinimalSuggestion;
     public final boolean mIsPunctuationSuggestions;
-    public final List<SuggestedWordInfo> mSuggestedWordInfoList;
+    private final List<SuggestedWordInfo> mSuggestedWordInfoList;
 
     private SuggestedWords(List<CharSequence> words, boolean typedWordValid,
             boolean hasMinimalSuggestion, boolean isPunctuationSuggestions,
@@ -54,6 +55,10 @@
         return mWords.get(pos);
     }
 
+    public SuggestedWordInfo getInfo(int pos) {
+        return mSuggestedWordInfoList != null ? mSuggestedWordInfoList.get(pos) : null;
+    }
+
     public boolean hasAutoCorrectionWord() {
         return mHasMinimalSuggestion && size() > 1 && !mTypedWordValid;
     }
@@ -105,14 +110,18 @@
         }
 
         private Builder addWord(CharSequence word, SuggestedWordInfo suggestedWordInfo) {
-            mWords.add(word);
-            mSuggestedWordInfoList.add(suggestedWordInfo);
+            if (!TextUtils.isEmpty(word)) {
+                mWords.add(word);
+                // It's okay if suggestedWordInfo is null since it's checked where it's used.
+                mSuggestedWordInfoList.add(suggestedWordInfo);
+            }
             return this;
         }
 
         public Builder setApplicationSpecifiedCompletions(CompletionInfo[] infos) {
-            for (CompletionInfo info : infos)
-                addWord(info.getText());
+            for (CompletionInfo info : infos) {
+                if (null != info) addWord(info.getText());
+            }
             return this;
         }
 
diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java
index 2aaa26c..6608d82 100644
--- a/java/src/com/android/inputmethod/latin/UserDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserDictionary.java
@@ -26,6 +26,8 @@
 import android.os.RemoteException;
 import android.provider.UserDictionary.Words;
 
+import com.android.inputmethod.keyboard.ProximityInfo;
+
 public class UserDictionary extends ExpandableDictionary {
     
     private static final String[] PROJECTION_QUERY = {
@@ -38,23 +40,24 @@
         Words.FREQUENCY,
         Words.LOCALE,
     };
-    
+
     private ContentObserver mObserver;
     private String mLocale;
 
     public UserDictionary(Context context, String locale) {
         super(context, Suggest.DIC_USER);
         mLocale = locale;
-        // Perform a managed query. The Activity will handle closing and requerying the cursor
+        // Perform a managed query. The Activity will handle closing and re-querying the cursor
         // when needed.
         ContentResolver cres = context.getContentResolver();
-        
-        cres.registerContentObserver(Words.CONTENT_URI, true, mObserver = new ContentObserver(null) {
+
+        mObserver = new ContentObserver(null) {
             @Override
             public void onChange(boolean self) {
                 setRequiresReload(true);
             }
-        });
+        };
+        cres.registerContentObserver(Words.CONTENT_URI, true, mObserver);
 
         loadDictionary();
     }
@@ -76,6 +79,17 @@
         addWords(cursor);
     }
 
+    public boolean isEnabled() {
+        final ContentResolver cr = getContext().getContentResolver();
+        final ContentProviderClient client = cr.acquireContentProviderClient(Words.CONTENT_URI);
+        if (client != null) {
+            client.release();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     /**
      * Adds a word to the dictionary and makes it persistent.
      * @param word the word to add. If the word is capitalized, then the dictionary will
@@ -138,8 +152,9 @@
     }
 
     @Override
-    public synchronized void getWords(final WordComposer codes, final WordCallback callback) {
-        super.getWords(codes, callback);
+    public synchronized void getWords(final WordComposer codes, final WordCallback callback,
+            final ProximityInfo proximityInfo) {
+        super.getWords(codes, callback, proximityInfo);
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 6bdc0a8..1a6260a 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -111,35 +111,43 @@
         }
     }
 
-    public static boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManagerCompatWrapper imm) {
+    public static boolean hasMultipleEnabledIMEsOrSubtypes(
+            final InputMethodManagerCompatWrapper imm,
+            final boolean shouldIncludeAuxiliarySubtypes) {
         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>();
+        // Number of the filtered IMEs
+        int filteredImisCount = 0;
 
-        outerloop:
         for (InputMethodInfoCompatWrapper imi : enabledImis) {
             // We can return true immediately after we find two or more filtered IMEs.
-            if (filteredImis.size() > 1) return true;
+            if (filteredImisCount > 1) return true;
             final List<InputMethodSubtypeCompatWrapper> subtypes =
                     imm.getEnabledInputMethodSubtypeList(imi, true);
-            // IMEs that have no subtypes should be included.
+            // IMEs that have no subtypes should be counted.
             if (subtypes.isEmpty()) {
-                filteredImis.add(imi);
+                ++filteredImisCount;
                 continue;
             }
-            // IMEs that have one or more non-auxiliary subtypes should be included.
+
+            int auxCount = 0;
             for (InputMethodSubtypeCompatWrapper subtype : subtypes) {
-                if (!subtype.isAuxiliary()) {
-                    filteredImis.add(imi);
-                    continue outerloop;
+                if (subtype.isAuxiliary()) {
+                    ++auxCount;
                 }
             }
+            final int nonAuxCount = subtypes.size() - auxCount;
+
+            // IMEs that have one or more non-auxiliary subtypes should be counted.
+            // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
+            // subtypes should be counted as well.
+            if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
+                ++filteredImisCount;
+                continue;
+            }
         }
 
-        return filteredImis.size() > 1
+        return filteredImisCount > 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;
@@ -190,6 +198,18 @@
         }
     }
 
+    public static boolean canBeFollowedByPeriod(final int codePoint) {
+        // TODO: Check again whether there really ain't a better way to check this.
+        // TODO: This should probably be language-dependant...
+        return Character.isLetterOrDigit(codePoint)
+                || codePoint == Keyboard.CODE_SINGLE_QUOTE
+                || codePoint == Keyboard.CODE_DOUBLE_QUOTE
+                || codePoint == Keyboard.CODE_CLOSING_PARENTHESIS
+                || codePoint == Keyboard.CODE_CLOSING_SQUARE_BRACKET
+                || codePoint == Keyboard.CODE_CLOSING_CURLY_BRACKET
+                || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET;
+    }
+
     /* package */ static class RingCharBuffer {
         private static RingCharBuffer sRingCharBuffer = new RingCharBuffer();
         private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC';
@@ -546,11 +566,11 @@
         }
     }
 
-    public static int getKeyboardMode(EditorInfo attribute) {
-        if (attribute == null)
+    public static int getKeyboardMode(EditorInfo editorInfo) {
+        if (editorInfo == null)
             return KeyboardId.MODE_TEXT;
 
-        final int inputType = attribute.inputType;
+        final int inputType = editorInfo.inputType;
         final int variation = inputType & InputType.TYPE_MASK_VARIATION;
 
         switch (inputType & InputType.TYPE_MASK_CLASS) {
@@ -587,11 +607,11 @@
     }
 
     public static boolean inPrivateImeOptions(String packageName, String key,
-            EditorInfo attribute) {
-        if (attribute == null)
+            EditorInfo editorInfo) {
+        if (editorInfo == null)
             return false;
         return containsInCsv(packageName != null ? packageName + "." + key : key,
-                attribute.privateImeOptions);
+                editorInfo.privateImeOptions);
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
index 4377373..639c966 100644
--- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
+++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
@@ -21,6 +21,8 @@
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.inputmethod.keyboard.ProximityInfo;
+
 import java.util.HashMap;
 
 public class WhitelistDictionary extends Dictionary {
@@ -89,7 +91,8 @@
 
     // Not used for WhitelistDictionary.  We use getWhitelistedWord() in Suggest.java instead
     @Override
-    public void getWords(WordComposer composer, WordCallback callback) {
+    public void getWords(final WordComposer composer, final WordCallback callback,
+            final ProximityInfo proximityInfo) {
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
new file mode 100644
index 0000000..44e9995
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -0,0 +1,167 @@
+/*
+ * 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.res.Resources;
+import android.service.textservice.SpellCheckerService;
+import android.service.textservice.SpellCheckerService.Session;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+
+import com.android.inputmethod.compat.ArraysCompatUtils;
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.ProximityInfo;
+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.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Service for spell checking, using LatinIME's dictionaries and mechanisms.
+ */
+public class AndroidSpellCheckerService extends SpellCheckerService {
+    private static final String TAG = AndroidSpellCheckerService.class.getSimpleName();
+    private static final boolean DBG = true;
+
+    private final static String[] emptyArray = new String[0];
+    private final ProximityInfo mProximityInfo = ProximityInfo.getSpellCheckerProximityInfo();
+    private final Map<String, Dictionary> mDictionaries =
+            Collections.synchronizedMap(new TreeMap<String, Dictionary>());
+
+    @Override
+    public Session createSession() {
+        return new AndroidSpellCheckerSession();
+    }
+
+    private static class SuggestionsGatherer implements WordCallback {
+        private final int DEFAULT_SUGGESTION_LENGTH = 16;
+        private final String[] mSuggestions;
+        private final int[] mScores;
+        private final int mMaxLength;
+        private int mLength = 0;
+
+        SuggestionsGatherer(final int maxLength) {
+            mMaxLength = maxLength;
+            mSuggestions = new String[mMaxLength];
+            mScores = new int[mMaxLength];
+        }
+
+        @Override
+        synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score,
+                int dicTypeId, DataType dataType) {
+            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 insertIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1;
+
+            if (mLength < mMaxLength) {
+                final int copyLen = mLength - insertIndex;
+                ++mLength;
+                System.arraycopy(mScores, insertIndex, mScores, insertIndex + 1, copyLen);
+                System.arraycopy(mSuggestions, insertIndex, mSuggestions, insertIndex + 1, copyLen);
+            } else {
+                if (insertIndex == 0) return true;
+                System.arraycopy(mScores, 1, mScores, 0, insertIndex);
+                System.arraycopy(mSuggestions, 1, mSuggestions, 0, insertIndex);
+            }
+            mScores[insertIndex] = score;
+            mSuggestions[insertIndex] = new String(word, wordOffset, wordLength);
+
+            return true;
+        }
+
+        public String[] getGatheredSuggestions() {
+            if (0 == mLength) return null;
+
+            final String[] results = new String[mLength];
+            for (int i = mLength - 1; i >= 0; --i) {
+                results[mLength - i - 1] = mSuggestions[i];
+            }
+            return results;
+        }
+    }
+
+    private Dictionary getDictionary(final String locale) {
+        Dictionary dictionary = mDictionaries.get(locale);
+        if (null == dictionary) {
+            final Resources resources = getResources();
+            final int fallbackResourceId = Utils.getMainDictionaryResourceId(resources);
+            final Locale localeObject = Utils.constructLocaleFromString(locale);
+            dictionary = DictionaryFactory.createDictionaryFromManager(this, localeObject,
+                    fallbackResourceId);
+            mDictionaries.put(locale, dictionary);
+        }
+        return dictionary;
+    }
+
+    private class AndroidSpellCheckerSession extends Session {
+        @Override
+        public void onCreate() {
+        }
+
+        // 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.
+         */
+        @Override
+        public SuggestionsInfo onGetSuggestions(final TextInfo textInfo,
+                final int suggestionsLimit) {
+            final String locale = getLocale();
+            final Dictionary dictionary = getDictionary(locale);
+            final String text = textInfo.getText();
+
+            final SuggestionsGatherer suggestionsGatherer =
+                    new SuggestionsGatherer(suggestionsLimit);
+            final WordComposer composer = new WordComposer();
+            final int length = text.length();
+            for (int i = 0; i < length; ++i) {
+                final int character = text.codePointAt(i);
+                final int proximityIndex = SpellCheckerProximityInfo.getIndexOf(character);
+                final int[] proximities;
+                if (-1 == proximityIndex) {
+                    proximities = new int[] { character };
+                } else {
+                    proximities = Arrays.copyOfRange(SpellCheckerProximityInfo.PROXIMITY,
+                            proximityIndex, proximityIndex + SpellCheckerProximityInfo.ROW_SIZE);
+                }
+                composer.add(character, proximities,
+                        WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
+            }
+            dictionary.getWords(composer, suggestionsGatherer, mProximityInfo);
+            final boolean isInDict = dictionary.isValidWord(text);
+            final String[] suggestions = suggestionsGatherer.getGatheredSuggestions();
+
+            final int flags =
+                    (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY : 0)
+                            | (null != suggestions
+                                    ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0);
+            return new SuggestionsInfo(flags, suggestions);
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java
deleted file mode 100644
index 63c6d69..0000000
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java
+++ /dev/null
@@ -1,115 +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.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/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
new file mode 100644
index 0000000..abcf7e5
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
@@ -0,0 +1,94 @@
+/*
+ * 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 com.android.inputmethod.keyboard.KeyDetector;
+import com.android.inputmethod.keyboard.ProximityInfo;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+public class SpellCheckerProximityInfo {
+    final private static int NUL = KeyDetector.NOT_A_CODE;
+
+    // This must be the same as MAX_PROXIMITY_CHARS_SIZE else it will not work inside
+    // native code - this value is passed at creation of the binary object and reused
+    // as the size of the passed array afterwards so they can't be different.
+    final public static int ROW_SIZE = ProximityInfo.MAX_PROXIMITY_CHARS_SIZE;
+
+    // This is a map from the code point to the index in the PROXIMITY array.
+    // At the time the native code to read the binary dictionary needs the proximity info be passed
+    // as a flat array spaced by MAX_PROXIMITY_CHARS_SIZE columns, one for each input character.
+    // Since we need to build such an array, we want to be able to search in our big proximity data
+    // quickly by character, and a map is probably the best way to do this.
+    final private static TreeMap<Integer, Integer> INDICES = new TreeMap<Integer, Integer>();
+
+    // The proximity here is the union of
+    // - the proximity for a QWERTY keyboard.
+    // - the proximity for an AZERTY keyboard.
+    // - the proximity for a QWERTZ keyboard.
+    // ...plus, add all characters in the ('a', 'e', 'i', 'o', 'u') set to each other.
+    //
+    // The reasoning behind this construction is, almost any alphabetic text we may want
+    // to spell check has been entered with one of the keyboards above. Also, specifically
+    // to English, many spelling errors consist of the last vowel of the word being wrong
+    // because in English vowels tend to merge with each other in pronunciation.
+    final public static int[] PROXIMITY = {
+        'q', 'w', 's', 'a', 'z', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'w', 'q', 'a', 's', 'd', 'e', 'x', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'e', 'w', 's', 'd', 'f', 'r', 'a', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL,
+        'r', 'e', 'd', 'f', 'g', 't', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        't', 'r', 'f', 'g', 'h', 'y', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'y', 't', 'g', 'h', 'j', 'u', 'a', 's', 'd', 'x', NUL, NUL, NUL, NUL, NUL, NUL,
+        'u', 'y', 'h', 'j', 'k', 'i', 'a', 'e', 'o', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'i', 'u', 'j', 'k', 'l', 'o', 'a', 'e', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'o', 'i', 'k', 'l', 'p', 'a', 'e', 'u', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'p', 'o', 'l', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+
+        'a', 'z', 'x', 's', 'w', 'q', 'e', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL,
+        's', 'q', 'a', 'z', 'x', 'c', 'd', 'e', 'w', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'd', 'w', 's', 'x', 'c', 'v', 'f', 'r', 'e', 'w', NUL, NUL, NUL, NUL, NUL, NUL,
+        'f', 'e', 'd', 'c', 'v', 'b', 'g', 't', 'r', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'g', 'r', 'f', 'v', 'b', 'n', 'h', 'y', 't', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'h', 't', 'g', 'b', 'n', 'm', 'j', 'u', 'y', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'j', 'y', 'h', 'n', 'm', 'k', 'i', 'u', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'k', 'u', 'j', 'm', 'l', 'o', 'i', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'l', 'i', 'k', 'p', 'o', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+
+        'z', 'a', 's', 'd', 'x', 't', 'g', 'h', 'j', 'u', 'q', 'e', NUL, NUL, NUL, NUL,
+        'x', 'z', 'a', 's', 'd', 'c', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'c', 'x', 's', 'd', 'f', 'v', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'v', 'c', 'd', 'f', 'g', 'b', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'b', 'v', 'f', 'g', 'h', 'n', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'n', 'b', 'g', 'h', 'j', 'm', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        'm', 'n', 'h', 'j', 'k', 'l', 'o', 'p', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+        NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+    };
+    static {
+        for (int i = 0; i < PROXIMITY.length; i += ROW_SIZE) {
+            if (NUL != PROXIMITY[i]) INDICES.put(PROXIMITY[i], i);
+        }
+    }
+    public static int getIndexOf(int characterCode) {
+        final Integer result = INDICES.get(characterCode);
+        if (null == result) return -1;
+        return result;
+    }
+}
diff --git a/native/Android.mk b/native/Android.mk
index bc246a9..f07be6a 100644
--- a/native/Android.mk
+++ b/native/Android.mk
@@ -8,15 +8,13 @@
 # To suppress compiler warnings for unused variables/functions used for debug features etc.
 LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
 
-# Use the new dictionary format
-LOCAL_CFLAGS += -DNEW_DICTIONARY_FORMAT
-
 LOCAL_SRC_FILES := \
     jni/com_android_inputmethod_keyboard_ProximityInfo.cpp \
     jni/com_android_inputmethod_latin_BinaryDictionary.cpp \
     jni/jni_common.cpp \
     src/bigram_dictionary.cpp \
     src/char_utils.cpp \
+    src/correction.cpp \
     src/dictionary.cpp \
     src/proximity_info.cpp \
     src/unigram_dictionary.cpp
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index ce874d8..18c9724 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -17,6 +17,7 @@
 
 #define LOG_TAG "LatinIME: jni: BinaryDictionary"
 
+#include "binary_format.h"
 #include "com_android_inputmethod_latin_BinaryDictionary.h"
 #include "dictionary.h"
 #include "jni.h"
@@ -38,6 +39,8 @@
 
 namespace latinime {
 
+void releaseDictBuf(void* dictBuf, const size_t length, int fd);
+
 static jint latinime_BinaryDictionary_open(JNIEnv *env, jobject object,
         jstring sourceDir, jlong dictOffset, jlong dictSize,
         jint typedLetterMultiplier, jint fullWordMultiplier, jint maxWordLength, jint maxWords,
@@ -104,8 +107,18 @@
         LOGE("DICT: dictBuf is null");
         return 0;
     }
-    Dictionary *dictionary = new Dictionary(dictBuf, dictSize, fd, adjust, typedLetterMultiplier,
-            fullWordMultiplier, maxWordLength, maxWords, maxAlternatives);
+    Dictionary *dictionary = NULL;
+    if (BinaryFormat::UNKNOWN_FORMAT == BinaryFormat::detectFormat((uint8_t*)dictBuf)) {
+        LOGE("DICT: dictionary format is unknown, bad magic number");
+#ifdef USE_MMAP_FOR_DICTIONARY
+        releaseDictBuf(((char*)dictBuf) - adjust, adjDictSize, fd);
+#else // USE_MMAP_FOR_DICTIONARY
+        releaseDictBuf(dictBuf, 0, 0);
+#endif // USE_MMAP_FOR_DICTIONARY
+    } else {
+        dictionary = new Dictionary(dictBuf, dictSize, fd, adjust, typedLetterMultiplier,
+                fullWordMultiplier, maxWordLength, maxWords, maxAlternatives);
+    }
     PROF_END(66);
     PROF_CLOSE;
     return (jint)dictionary;
@@ -180,19 +193,27 @@
     void *dictBuf = dictionary->getDict();
     if (!dictBuf) return;
 #ifdef USE_MMAP_FOR_DICTIONARY
-    int ret = munmap((void *)((char *)dictBuf - dictionary->getDictBufAdjust()),
-            dictionary->getDictSize() + dictionary->getDictBufAdjust());
+    releaseDictBuf((void *)((char *)dictBuf - dictionary->getDictBufAdjust()),
+            dictionary->getDictSize() + dictionary->getDictBufAdjust(), dictionary->getMmapFd());
+#else // USE_MMAP_FOR_DICTIONARY
+    releaseDictBuf(dictBuf, 0, 0);
+#endif // USE_MMAP_FOR_DICTIONARY
+    delete dictionary;
+}
+
+void releaseDictBuf(void* dictBuf, const size_t length, int fd) {
+#ifdef USE_MMAP_FOR_DICTIONARY
+    int ret = munmap(dictBuf, length);
     if (ret != 0) {
         LOGE("DICT: Failure in munmap. ret=%d errno=%d", ret, errno);
     }
-    ret = close(dictionary->getMmapFd());
+    ret = close(fd);
     if (ret != 0) {
         LOGE("DICT: Failure in close. ret=%d errno=%d", ret, errno);
     }
 #else // USE_MMAP_FOR_DICTIONARY
     free(dictBuf);
 #endif // USE_MMAP_FOR_DICTIONARY
-    delete dictionary;
 }
 
 static JNINativeMethod sMethods[] = {
diff --git a/native/src/bigram_dictionary.cpp b/native/src/bigram_dictionary.cpp
index 6ed4d09..c340c6c 100644
--- a/native/src/bigram_dictionary.cpp
+++ b/native/src/bigram_dictionary.cpp
@@ -21,13 +21,14 @@
 
 #include "bigram_dictionary.h"
 #include "dictionary.h"
+#include "binary_format.h"
 
 namespace latinime {
 
 BigramDictionary::BigramDictionary(const unsigned char *dict, int maxWordLength,
         int maxAlternatives, const bool isLatestDictVersion, const bool hasBigram,
         Dictionary *parentDictionary)
-    : DICT(dict), MAX_WORD_LENGTH(maxWordLength),
+    : DICT(dict + NEW_DICTIONARY_HEADER_SIZE), MAX_WORD_LENGTH(maxWordLength),
     MAX_ALTERNATIVES(maxAlternatives), IS_LATEST_DICT_VERSION(isLatestDictVersion),
     HAS_BIGRAM(hasBigram), mParentDictionary(parentDictionary) {
     if (DEBUG_DICT) {
@@ -82,169 +83,64 @@
     return false;
 }
 
-int BigramDictionary::getBigramAddress(int *pos, bool advance) {
-    int address = 0;
-
-    address += (DICT[*pos] & 0x3F) << 16;
-    address += (DICT[*pos + 1] & 0xFF) << 8;
-    address += (DICT[*pos + 2] & 0xFF);
-
-    if (advance) {
-        *pos += 3;
-    }
-
-    return address;
-}
-
-int BigramDictionary::getBigramFreq(int *pos) {
-    int freq = DICT[(*pos)++] & FLAG_BIGRAM_FREQ;
-
-    return freq;
-}
-
-
+/* Parameters :
+ * prevWord: the word before, the one for which we need to look up bigrams.
+ * prevWordLength: its length.
+ * codes: what user typed, in the same format as for UnigramDictionary::getSuggestions.
+ * codesSize: the size of the codes array.
+ * bigramChars: an array for output, at the same format as outwords for getSuggestions.
+ * bigramFreq: an array to output frequencies.
+ * maxWordLength: the maximum size of a word.
+ * maxBigrams: the maximum number of bigrams fitting in the bigramChars array.
+ * maxAlteratives: unused.
+ * This method returns the number of bigrams this word has, for backward compatibility.
+ * Note: this is not the number of bigrams output in the array, which is the number of
+ * bigrams this word has WHOSE first letter also matches the letter the user typed.
+ * TODO: this may not be a sensible thing to do. It makes sense when the bigrams are
+ * used to match the first letter of the second word, but once the user has typed more
+ * and the bigrams are used to boost unigram result scores, it makes little sense to
+ * reduce their scope to the ones that match the first letter.
+ */
 int BigramDictionary::getBigrams(unsigned short *prevWord, int prevWordLength, int *codes,
         int codesSize, unsigned short *bigramChars, int *bigramFreq, int maxWordLength,
         int maxBigrams, int maxAlternatives) {
+    // TODO: remove unused arguments, and refrain from storing stuff in members of this class
+    // TODO: have "in" arguments before "out" ones, and make out args explicit in the name
     mBigramFreq = bigramFreq;
     mBigramChars = bigramChars;
     mInputCodes = codes;
-    mInputLength = codesSize;
     mMaxBigrams = maxBigrams;
 
-    if (HAS_BIGRAM && IS_LATEST_DICT_VERSION) {
-        int pos = mParentDictionary->getBigramPosition(prevWord, prevWordLength);
-        if (DEBUG_DICT) {
-            LOGI("Pos -> %d", pos);
-        }
-        if (pos < 0) {
-            return 0;
-        }
+    const uint8_t* const root = DICT;
+    int pos = BinaryFormat::getTerminalPosition(root, prevWord, prevWordLength);
 
-        int bigramCount = 0;
-        int bigramExist = (DICT[pos] & FLAG_BIGRAM_READ);
-        if (bigramExist > 0) {
-            int nextBigramExist = 1;
-            while (nextBigramExist > 0 && bigramCount < maxBigrams) {
-                int bigramAddress = getBigramAddress(&pos, true);
-                int frequency = (FLAG_BIGRAM_FREQ & DICT[pos]);
-                // search for all bigrams and store them
-                searchForTerminalNode(bigramAddress, frequency);
-                nextBigramExist = (DICT[pos++] & FLAG_BIGRAM_CONTINUED);
-                bigramCount++;
-            }
-        }
-
-        return bigramCount;
+    if (NOT_VALID_WORD == pos) return 0;
+    const int flags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
+    if (0 == (flags & UnigramDictionary::FLAG_HAS_BIGRAMS)) return 0;
+    if (0 == (flags & UnigramDictionary::FLAG_HAS_MULTIPLE_CHARS)) {
+        BinaryFormat::getCharCodeAndForwardPointer(root, &pos);
+    } else {
+        pos = BinaryFormat::skipOtherCharacters(root, pos);
     }
-    return 0;
-}
+    pos = BinaryFormat::skipChildrenPosition(flags, pos);
+    pos = BinaryFormat::skipFrequency(flags, pos);
+    int bigramFlags;
+    int bigramCount = 0;
+    do {
+        bigramFlags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
+        uint16_t bigramBuffer[MAX_WORD_LENGTH];
+        const int bigramPos = BinaryFormat::getAttributeAddressAndForwardPointer(root, bigramFlags,
+                &pos);
+        const int length = BinaryFormat::getWordAtAddress(root, bigramPos, MAX_WORD_LENGTH,
+                bigramBuffer);
 
-void BigramDictionary::searchForTerminalNode(int addressLookingFor, int frequency) {
-    // track word with such address and store it in an array
-    unsigned short word[MAX_WORD_LENGTH];
-
-    int pos;
-    int followDownBranchAddress = DICTIONARY_HEADER_SIZE;
-    bool found = false;
-    char followingChar = ' ';
-    int depth = -1;
-
-    while(!found) {
-        bool followDownAddressSearchStop = false;
-        bool firstAddress = true;
-        bool haveToSearchAll = true;
-
-        if (depth < MAX_WORD_LENGTH && depth >= 0) {
-            word[depth] = (unsigned short) followingChar;
+        if (checkFirstCharacter(bigramBuffer)) {
+            const int frequency = UnigramDictionary::MASK_ATTRIBUTE_FREQUENCY & bigramFlags;
+            addWordBigram(bigramBuffer, length, frequency);
         }
-        pos = followDownBranchAddress; // pos start at count
-        int count = DICT[pos] & 0xFF;
-        if (DEBUG_DICT) {
-            LOGI("count - %d",count);
-        }
-        pos++;
-        for (int i = 0; i < count; i++) {
-            // pos at data
-            pos++;
-            // pos now at flag
-            if (!getFirstBitOfByte(&pos)) { // non-terminal
-                if (!followDownAddressSearchStop) {
-                    int addr = getBigramAddress(&pos, false);
-                    if (addr > addressLookingFor) {
-                        followDownAddressSearchStop = true;
-                        if (firstAddress) {
-                            firstAddress = false;
-                            haveToSearchAll = true;
-                        } else if (!haveToSearchAll) {
-                            break;
-                        }
-                    } else {
-                        followDownBranchAddress = addr;
-                        followingChar = (char)(0xFF & DICT[pos-1]);
-                        if (firstAddress) {
-                            firstAddress = false;
-                            haveToSearchAll = false;
-                        }
-                    }
-                }
-                pos += 3;
-            } else if (getFirstBitOfByte(&pos)) { // terminal
-                if (addressLookingFor == (pos-1)) { // found !!
-                    depth++;
-                    word[depth] = (0xFF & DICT[pos-1]);
-                    found = true;
-                    break;
-                }
-                if (getSecondBitOfByte(&pos)) { // address + freq (4 byte)
-                    if (!followDownAddressSearchStop) {
-                        int addr = getBigramAddress(&pos, false);
-                        if (addr > addressLookingFor) {
-                            followDownAddressSearchStop = true;
-                            if (firstAddress) {
-                                firstAddress = false;
-                                haveToSearchAll = true;
-                            } else if (!haveToSearchAll) {
-                                break;
-                            }
-                        } else {
-                            followDownBranchAddress = addr;
-                            followingChar = (char)(0xFF & DICT[pos-1]);
-                            if (firstAddress) {
-                                firstAddress = false;
-                                haveToSearchAll = true;
-                            }
-                        }
-                    }
-                    pos += 4;
-                } else { // freq only (2 byte)
-                    pos += 2;
-                }
-
-                // skipping bigram
-                int bigramExist = (DICT[pos] & FLAG_BIGRAM_READ);
-                if (bigramExist > 0) {
-                    int nextBigramExist = 1;
-                    while (nextBigramExist > 0) {
-                        pos += 3;
-                        nextBigramExist = (DICT[pos++] & FLAG_BIGRAM_CONTINUED);
-                    }
-                } else {
-                    pos++;
-                }
-            }
-        }
-        depth++;
-        if (followDownBranchAddress == 0) {
-            if (DEBUG_DICT) {
-                LOGI("ERROR!!! Cannot find bigram!!");
-            }
-            break;
-        }
-    }
-    if (checkFirstCharacter(word)) {
-        addWordBigram(word, depth, frequency);
-    }
+        ++bigramCount;
+    } while (0 != (UnigramDictionary::FLAG_ATTRIBUTE_HAS_NEXT & bigramFlags));
+    return bigramCount;
 }
 
 bool BigramDictionary::checkFirstCharacter(unsigned short *word) {
diff --git a/native/src/binary_format.h b/native/src/binary_format.h
index e9f108e..6f65088 100644
--- a/native/src/binary_format.h
+++ b/native/src/binary_format.h
@@ -17,6 +17,8 @@
 #ifndef LATINIME_BINARY_FORMAT_H
 #define LATINIME_BINARY_FORMAT_H
 
+#include "unigram_dictionary.h"
+
 namespace latinime {
 
 class BinaryFormat {
@@ -26,6 +28,11 @@
     const static int MULTIPLE_BYTE_CHARACTER_ADDITIONAL_SIZE = 2;
 
 public:
+    const static int UNKNOWN_FORMAT = -1;
+    const static int FORMAT_VERSION_1 = 1;
+    const static uint16_t FORMAT_VERSION_1_MAGIC_NUMBER = 0x78B1;
+
+    static int detectFormat(const uint8_t* const dict);
     static int getGroupCountAndForwardPointer(const uint8_t* const dict, int* pos);
     static uint8_t getFlagsAndForwardPointer(const uint8_t* const dict, int* pos);
     static int32_t getCharCodeAndForwardPointer(const uint8_t* const dict, int* pos);
@@ -41,8 +48,18 @@
     static bool hasChildrenInFlags(const uint8_t flags);
     static int getAttributeAddressAndForwardPointer(const uint8_t* const dict, const uint8_t flags,
             int *pos);
+    static int getTerminalPosition(const uint8_t* const root, const uint16_t* const inWord,
+            const int length);
+    static int getWordAtAddress(const uint8_t* const root, const int address, const int maxDepth,
+            uint16_t* outWord);
 };
 
+inline int BinaryFormat::detectFormat(const uint8_t* const dict) {
+    const uint16_t magicNumber = (dict[0] << 8) + dict[1]; // big endian
+    if (FORMAT_VERSION_1_MAGIC_NUMBER == magicNumber) return FORMAT_VERSION_1;
+    return UNKNOWN_FORMAT;
+}
+
 inline int BinaryFormat::getGroupCountAndForwardPointer(const uint8_t* const dict, int* pos) {
     return dict[(*pos)++];
 }
@@ -204,6 +221,222 @@
     }
 }
 
+// This function gets the byte position of the last chargroup of the exact matching word in the
+// dictionary. If no match is found, it returns NOT_VALID_WORD.
+inline int BinaryFormat::getTerminalPosition(const uint8_t* const root,
+        const uint16_t* const inWord, const int length) {
+    int pos = 0;
+    int wordPos = 0;
+
+    while (true) {
+        // If we already traversed the tree further than the word is long, there means
+        // there was no match (or we would have found it).
+        if (wordPos > length) return NOT_VALID_WORD;
+        int charGroupCount = BinaryFormat::getGroupCountAndForwardPointer(root, &pos);
+        const uint16_t wChar = inWord[wordPos];
+        while (true) {
+            // If there are no more character groups in this node, it means we could not
+            // find a matching character for this depth, therefore there is no match.
+            if (0 >= charGroupCount) return NOT_VALID_WORD;
+            const int charGroupPos = pos;
+            const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
+            int32_t character = BinaryFormat::getCharCodeAndForwardPointer(root, &pos);
+            if (character == wChar) {
+                // This is the correct node. Only one character group may start with the same
+                // char within a node, so either we found our match in this node, or there is
+                // no match and we can return NOT_VALID_WORD. So we will check all the characters
+                // in this character group indeed does match.
+                if (UnigramDictionary::FLAG_HAS_MULTIPLE_CHARS & flags) {
+                    character = BinaryFormat::getCharCodeAndForwardPointer(root, &pos);
+                    while (NOT_A_CHARACTER != character) {
+                        ++wordPos;
+                        // If we shoot the length of the word we search for, or if we find a single
+                        // character that does not match, as explained above, it means the word is
+                        // not in the dictionary (by virtue of this chargroup being the only one to
+                        // match the word on the first character, but not matching the whole word).
+                        if (wordPos > length) return NOT_VALID_WORD;
+                        if (inWord[wordPos] != character) return NOT_VALID_WORD;
+                        character = BinaryFormat::getCharCodeAndForwardPointer(root, &pos);
+                    }
+                }
+                // If we come here we know that so far, we do match. Either we are on a terminal
+                // and we match the length, in which case we found it, or we traverse children.
+                // If we don't match the length AND don't have children, then a word in the
+                // dictionary fully matches a prefix of the searched word but not the full word.
+                ++wordPos;
+                if (UnigramDictionary::FLAG_IS_TERMINAL & flags) {
+                    if (wordPos == length) {
+                        return charGroupPos;
+                    }
+                    pos = BinaryFormat::skipFrequency(UnigramDictionary::FLAG_IS_TERMINAL, pos);
+                }
+                if (UnigramDictionary::FLAG_GROUP_ADDRESS_TYPE_NOADDRESS
+                        == (UnigramDictionary::MASK_GROUP_ADDRESS_TYPE & flags)) {
+                    return NOT_VALID_WORD;
+                }
+                // We have children and we are still shorter than the word we are searching for, so
+                // we need to traverse children. Put the pointer on the children position, and
+                // break
+                pos = BinaryFormat::readChildrenPosition(root, flags, pos);
+                break;
+            } else {
+                // This chargroup does not match, so skip the remaining part and go to the next.
+                if (UnigramDictionary::FLAG_HAS_MULTIPLE_CHARS & flags) {
+                    pos = BinaryFormat::skipOtherCharacters(root, pos);
+                }
+                pos = BinaryFormat::skipFrequency(flags, pos);
+                pos = BinaryFormat::skipChildrenPosAndAttributes(root, flags, pos);
+            }
+            --charGroupCount;
+        }
+    }
+}
+
+// This function searches for a terminal in the dictionary by its address.
+// Due to the fact that words are ordered in the dictionary in a strict breadth-first order,
+// it is possible to check for this with advantageous complexity. For each node, we search
+// for groups with children and compare the children address with the address we look for.
+// When we shoot the address we look for, it means the word we look for is in the children
+// of the previous group. The only tricky part is the fact that if we arrive at the end of a
+// node with the last group's children address still less than what we are searching for, we
+// must descend the last group's children (for example, if the word we are searching for starts
+// with a z, it's the last group of the root node, so all children addresses will be smaller
+// than the address we look for, and we have to descend the z node).
+/* Parameters :
+ * root: the dictionary buffer
+ * address: the byte position of the last chargroup of the word we are searching for (this is
+ *   what is stored as the "bigram address" in each bigram)
+ * outword: an array to write the found word, with MAX_WORD_LENGTH size.
+ * Return value : the length of the word, of 0 if the word was not found.
+ */
+inline int BinaryFormat::getWordAtAddress(const uint8_t* const root, const int address,
+        const int maxDepth, uint16_t* outWord) {
+    int pos = 0;
+    int wordPos = 0;
+
+    // One iteration of the outer loop iterates through nodes. As stated above, we will only
+    // traverse nodes that are actually a part of the terminal we are searching, so each time
+    // we enter this loop we are one depth level further than last time.
+    // The only reason we count nodes is because we want to reduce the probability of infinite
+    // looping in case there is a bug. Since we know there is an upper bound to the depth we are
+    // supposed to traverse, it does not hurt to count iterations.
+    for (int loopCount = maxDepth; loopCount > 0; --loopCount) {
+        int lastCandidateGroupPos = 0;
+        // Let's loop through char groups in this node searching for either the terminal
+        // or one of its ascendants.
+        for (int charGroupCount = getGroupCountAndForwardPointer(root, &pos); charGroupCount > 0;
+                 --charGroupCount) {
+            const int startPos = pos;
+            const uint8_t flags = getFlagsAndForwardPointer(root, &pos);
+            const int32_t character = getCharCodeAndForwardPointer(root, &pos);
+            if (address == startPos) {
+                // We found the address. Copy the rest of the word in the buffer and return
+                // the length.
+                outWord[wordPos] = character;
+                if (UnigramDictionary::FLAG_HAS_MULTIPLE_CHARS & flags) {
+                    int32_t nextChar = getCharCodeAndForwardPointer(root, &pos);
+                    // We count chars in order to avoid infinite loops if the file is broken or
+                    // if there is some other bug
+                    int charCount = maxDepth;
+                    while (-1 != nextChar && --charCount > 0) {
+                        outWord[++wordPos] = nextChar;
+                        nextChar = getCharCodeAndForwardPointer(root, &pos);
+                    }
+                }
+                return ++wordPos;
+            }
+            // We need to skip past this char group, so skip any remaining chars after the
+            // first and possibly the frequency.
+            if (UnigramDictionary::FLAG_HAS_MULTIPLE_CHARS & flags) {
+                pos = skipOtherCharacters(root, pos);
+            }
+            pos = skipFrequency(flags, pos);
+
+            // The fact that this group has children is very important. Since we already know
+            // that this group does not match, if it has no children we know it is irrelevant
+            // to what we are searching for.
+            const bool hasChildren = (UnigramDictionary::FLAG_GROUP_ADDRESS_TYPE_NOADDRESS !=
+                    (UnigramDictionary::MASK_GROUP_ADDRESS_TYPE & flags));
+            // We will write in `found' whether we have passed the children address we are
+            // searching for. For example if we search for "beer", the children of b are less
+            // than the address we are searching for and the children of c are greater. When we
+            // come here for c, we realize this is too big, and that we should descend b.
+            bool found;
+            if (hasChildren) {
+                // Here comes the tricky part. First, read the children position.
+                const int childrenPos = readChildrenPosition(root, flags, pos);
+                if (childrenPos > address) {
+                    // If the children pos is greater than address, it means the previous chargroup,
+                    // which address is stored in lastCandidateGroupPos, was the right one.
+                    found = true;
+                } else if (1 >= charGroupCount) {
+                    // However if we are on the LAST group of this node, and we have NOT shot the
+                    // address we should descend THIS node. So we trick the lastCandidateGroupPos
+                    // so that we will descend this node, not the previous one.
+                    lastCandidateGroupPos = startPos;
+                    found = true;
+                } else {
+                    // Else, we should continue looking.
+                    found = false;
+                }
+            } else {
+                // Even if we don't have children here, we could still be on the last group of this
+                // node. If this is the case, we should descend the last group that had children,
+                // and their address is already in lastCandidateGroup.
+                found = (1 >= charGroupCount);
+            }
+
+            if (found) {
+                // Okay, we found the group we should descend. Its address is in
+                // the lastCandidateGroupPos variable, so we just re-read it.
+                if (0 != lastCandidateGroupPos) {
+                    const uint8_t lastFlags =
+                            getFlagsAndForwardPointer(root, &lastCandidateGroupPos);
+                    const int32_t lastChar =
+                            getCharCodeAndForwardPointer(root, &lastCandidateGroupPos);
+                    // We copy all the characters in this group to the buffer
+                    outWord[wordPos] = lastChar;
+                    if (UnigramDictionary::FLAG_HAS_MULTIPLE_CHARS & lastFlags) {
+                        int32_t nextChar =
+                                getCharCodeAndForwardPointer(root, &lastCandidateGroupPos);
+                        int charCount = maxDepth;
+                        while (-1 != nextChar && --charCount > 0) {
+                            outWord[++wordPos] = nextChar;
+                            nextChar = getCharCodeAndForwardPointer(root, &lastCandidateGroupPos);
+                        }
+                    }
+                    ++wordPos;
+                    // Now we only need to branch to the children address. Skip the frequency if
+                    // it's there, read pos, and break to resume the search at pos.
+                    lastCandidateGroupPos = skipFrequency(lastFlags, lastCandidateGroupPos);
+                    pos = readChildrenPosition(root, lastFlags, lastCandidateGroupPos);
+                    break;
+                } else {
+                    // Here is a little tricky part: we come here if we found out that all children
+                    // addresses in this group are bigger than the address we are searching for.
+                    // Should we conclude the word is not in the dictionary? No! It could still be
+                    // one of the remaining chargroups in this node, so we have to keep looking in
+                    // this node until we find it (or we realize it's not there either, in which
+                    // case it's actually not in the dictionary). Pass the end of this group, ready
+                    // to start the next one.
+                    pos = skipChildrenPosAndAttributes(root, flags, pos);
+                }
+            } else {
+                // If we did not find it, we should record the last children address for the next
+                // iteration.
+                if (hasChildren) lastCandidateGroupPos = startPos;
+                // Now skip the end of this group (children pos and the attributes if any) so that
+                // our pos is after the end of this char group, at the start of the next one.
+                pos = skipChildrenPosAndAttributes(root, flags, pos);
+            }
+
+        }
+    }
+    // If we have looked through all the chargroups and found no match, the address is
+    // not the address of a terminal in this dictionary.
+    return 0;
+}
+
 } // namespace latinime
 
 #endif // LATINIME_BINARY_FORMAT_H
diff --git a/native/src/correction.cpp b/native/src/correction.cpp
new file mode 100644
index 0000000..654d471
--- /dev/null
+++ b/native/src/correction.cpp
@@ -0,0 +1,492 @@
+/*
+ * 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.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#define LOG_TAG "LatinIME: correction.cpp"
+
+#include "correction.h"
+#include "proximity_info.h"
+
+namespace latinime {
+
+//////////////////////
+// inline functions //
+//////////////////////
+static const char QUOTE = '\'';
+
+inline bool Correction::isQuote(const unsigned short c) {
+    const unsigned short userTypedChar = mProximityInfo->getPrimaryCharAt(mInputIndex);
+    return (c == QUOTE && userTypedChar != QUOTE);
+}
+
+////////////////
+// Correction //
+////////////////
+
+Correction::Correction(const int typedLetterMultiplier, const int fullWordMultiplier)
+        : TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier) {
+}
+
+void Correction::initCorrection(const ProximityInfo *pi, const int inputLength,
+        const int maxDepth) {
+    mProximityInfo = pi;
+    mInputLength = inputLength;
+    mMaxDepth = maxDepth;
+    mMaxEditDistance = mInputLength < 5 ? 2 : mInputLength / 2;
+}
+
+void Correction::initCorrectionState(
+        const int rootPos, const int childCount, const bool traverseAll) {
+    latinime::initCorrectionState(mCorrectionStates, rootPos, childCount, traverseAll);
+}
+
+void Correction::setCorrectionParams(const int skipPos, const int excessivePos,
+        const int transposedPos, const int spaceProximityPos, const int missingSpacePos) {
+    mSkipPos = skipPos;
+    mExcessivePos = excessivePos;
+    mTransposedPos = transposedPos;
+    mSpaceProximityPos = spaceProximityPos;
+    mMissingSpacePos = missingSpacePos;
+}
+
+void Correction::checkState() {
+    if (DEBUG_DICT) {
+        int inputCount = 0;
+        if (mSkipPos >= 0) ++inputCount;
+        if (mExcessivePos >= 0) ++inputCount;
+        if (mTransposedPos >= 0) ++inputCount;
+        // TODO: remove this assert
+        assert(inputCount <= 1);
+    }
+}
+
+int Correction::getFreqForSplitTwoWords(const int firstFreq, const int secondFreq) {
+    return Correction::RankingAlgorithm::calcFreqForSplitTwoWords(firstFreq, secondFreq, this);
+}
+
+int Correction::getFinalFreq(const int freq, unsigned short **word, int *wordLength) {
+    const int outputIndex = mTerminalOutputIndex;
+    const int inputIndex = mTerminalInputIndex;
+    *wordLength = outputIndex + 1;
+    if (mProximityInfo->sameAsTyped(mWord, outputIndex + 1) || outputIndex < MIN_SUGGEST_DEPTH) {
+        return -1;
+    }
+
+    // TODO: Remove this
+    if (mSkipPos >= 0 && mSkippedCount <= 0) {
+        return -1;
+    }
+
+    *word = mWord;
+    const bool sameLength = (mExcessivePos == mInputLength - 1) ? (mInputLength == inputIndex + 2)
+            : (mInputLength == inputIndex + 1);
+    return Correction::RankingAlgorithm::calculateFinalFreq(
+            inputIndex, outputIndex, mMatchedCharCount, freq, sameLength, this);
+}
+
+bool Correction::initProcessState(const int outputIndex) {
+    if (mCorrectionStates[outputIndex].mChildCount <= 0) {
+        return false;
+    }
+    mOutputIndex = outputIndex;
+    --(mCorrectionStates[outputIndex].mChildCount);
+    mMatchedCharCount = mCorrectionStates[outputIndex].mMatchedCount;
+    mInputIndex = mCorrectionStates[outputIndex].mInputIndex;
+    mNeedsToTraverseAllNodes = mCorrectionStates[outputIndex].mNeedsToTraverseAllNodes;
+    mDiffs = mCorrectionStates[outputIndex].mDiffs;
+    mSkippedCount = mCorrectionStates[outputIndex].mSkippedCount;
+    mSkipping = false;
+    mMatching = false;
+    return true;
+}
+
+int Correction::goDownTree(
+        const int parentIndex, const int childCount, const int firstChildPos) {
+    mCorrectionStates[mOutputIndex].mParentIndex = parentIndex;
+    mCorrectionStates[mOutputIndex].mChildCount = childCount;
+    mCorrectionStates[mOutputIndex].mSiblingPos = firstChildPos;
+    return mOutputIndex;
+}
+
+void Correction::charMatched() {
+    ++mMatchedCharCount;
+}
+
+// TODO: remove
+int Correction::getOutputIndex() {
+    return mOutputIndex;
+}
+
+// TODO: remove
+int Correction::getInputIndex() {
+    return mInputIndex;
+}
+
+// TODO: remove
+bool Correction::needsToTraverseAllNodes() {
+    return mNeedsToTraverseAllNodes;
+}
+
+void Correction::incrementInputIndex() {
+    ++mInputIndex;
+}
+
+void Correction::incrementOutputIndex() {
+    ++mOutputIndex;
+    mCorrectionStates[mOutputIndex].mParentIndex = mCorrectionStates[mOutputIndex - 1].mParentIndex;
+    mCorrectionStates[mOutputIndex].mChildCount = mCorrectionStates[mOutputIndex - 1].mChildCount;
+    mCorrectionStates[mOutputIndex].mSiblingPos = mCorrectionStates[mOutputIndex - 1].mSiblingPos;
+    mCorrectionStates[mOutputIndex].mMatchedCount = mMatchedCharCount;
+    mCorrectionStates[mOutputIndex].mInputIndex = mInputIndex;
+    mCorrectionStates[mOutputIndex].mNeedsToTraverseAllNodes = mNeedsToTraverseAllNodes;
+    mCorrectionStates[mOutputIndex].mDiffs = mDiffs;
+    mCorrectionStates[mOutputIndex].mSkippedCount = mSkippedCount;
+    mCorrectionStates[mOutputIndex].mSkipping = mSkipping;
+    mCorrectionStates[mOutputIndex].mMatching = mMatching;
+}
+
+void Correction::startToTraverseAllNodes() {
+    mNeedsToTraverseAllNodes = true;
+}
+
+bool Correction::needsToPrune() const {
+    return (mOutputIndex - 1 >= (mTransposedPos >= 0 ? mInputLength - 1 : mMaxDepth)
+            || mDiffs > mMaxEditDistance);
+}
+
+Correction::CorrectionType Correction::processSkipChar(
+        const int32_t c, const bool isTerminal) {
+    mWord[mOutputIndex] = c;
+    if (needsToTraverseAllNodes() && isTerminal) {
+        mTerminalInputIndex = mInputIndex;
+        mTerminalOutputIndex = mOutputIndex;
+        incrementOutputIndex();
+        return TRAVERSE_ALL_ON_TERMINAL;
+    } else {
+        incrementOutputIndex();
+        return TRAVERSE_ALL_NOT_ON_TERMINAL;
+    }
+}
+
+Correction::CorrectionType Correction::processCharAndCalcState(
+        const int32_t c, const bool isTerminal) {
+    CorrectionType currentStateType = NOT_ON_TERMINAL;
+    // This has to be done for each virtual char (this forwards the "inputIndex" which
+    // is the index in the user-inputted chars, as read by proximity chars.
+    if (mExcessivePos == mOutputIndex && mInputIndex < mInputLength - 1) {
+        incrementInputIndex();
+    }
+
+    bool skip = false;
+    if (mSkipPos >= 0) {
+        skip = mSkipPos == mOutputIndex;
+        mSkipping = true;
+    }
+
+    if (mNeedsToTraverseAllNodes || isQuote(c)) {
+        return processSkipChar(c, isTerminal);
+    } else {
+        int inputIndexForProximity = mInputIndex;
+
+        if (mTransposedPos >= 0) {
+            if (mInputIndex == mTransposedPos) {
+                ++inputIndexForProximity;
+            }
+            if (mInputIndex == (mTransposedPos + 1)) {
+                --inputIndexForProximity;
+            }
+        }
+
+        const bool checkProximityChars =
+                !(mSkipPos >= 0 || mExcessivePos >= 0 || mTransposedPos >= 0);
+        int matchedProximityCharId = mProximityInfo->getMatchedProximityId(
+                inputIndexForProximity, c, checkProximityChars);
+
+        const bool unrelated = ProximityInfo::UNRELATED_CHAR == matchedProximityCharId;
+        if (unrelated) {
+            if (skip) {
+                // Skip this letter and continue deeper
+                ++mSkippedCount;
+                return processSkipChar(c, isTerminal);
+            } else {
+                return UNRELATED;
+            }
+        }
+
+        // TODO: remove after allowing combination errors
+        if (skip) {
+            return UNRELATED;
+        }
+
+        mWord[mOutputIndex] = c;
+        // If inputIndex is greater than mInputLength, that means there is no
+        // proximity chars. So, we don't need to check proximity.
+        if (ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR == matchedProximityCharId) {
+            mMatching = true;
+            charMatched();
+        }
+
+        if (ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) {
+            incrementDiffs();
+        }
+
+        const bool isSameAsUserTypedLength = mInputLength
+                == getInputIndex() + 1
+                        || (mExcessivePos == mInputLength - 1
+                                    && getInputIndex() == mInputLength - 2);
+        if (isSameAsUserTypedLength && isTerminal) {
+            mTerminalInputIndex = mInputIndex;
+            mTerminalOutputIndex = mOutputIndex;
+            currentStateType = ON_TERMINAL;
+        }
+        // Start traversing all nodes after the index exceeds the user typed length
+        if (isSameAsUserTypedLength) {
+            startToTraverseAllNodes();
+        }
+
+        // Finally, we are ready to go to the next character, the next "virtual node".
+        // We should advance the input index.
+        // We do this in this branch of the 'if traverseAllNodes' because we are still matching
+        // characters to input; the other branch is not matching them but searching for
+        // completions, this is why it does not have to do it.
+        incrementInputIndex();
+    }
+
+    // Also, the next char is one "virtual node" depth more than this char.
+    incrementOutputIndex();
+
+    return currentStateType;
+}
+
+Correction::~Correction() {
+}
+
+/////////////////////////
+// static inline utils //
+/////////////////////////
+
+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;
+        }
+    }
+}
+
+inline static int powerIntCapped(const int base, const int n) {
+    if (n == 0) return 1;
+    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;
+    }
+}
+
+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;
+        }
+    }
+}
+
+//////////////////////
+// RankingAlgorithm //
+//////////////////////
+
+/* static */
+int Correction::RankingAlgorithm::calculateFinalFreq(
+        const int inputIndex, const int outputIndex,
+        const int matchCount, const int freq, const bool sameLength,
+        const Correction* correction) {
+    const int skipPos = correction->getSkipPos();
+    const int excessivePos = correction->getExcessivePos();
+    const int transposedPos = correction->getTransposedPos();
+    const int inputLength = correction->mInputLength;
+    const int typedLetterMultiplier = correction->TYPED_LETTER_MULTIPLIER;
+    const int fullWordMultiplier = correction->FULL_WORD_MULTIPLIER;
+    const ProximityInfo *proximityInfo = correction->mProximityInfo;
+    const int matchWeight = powerIntCapped(typedLetterMultiplier, matchCount);
+    const unsigned short* word = correction->mWord;
+    const int skippedCount = correction->mSkippedCount;
+
+    // TODO: Demote by edit distance
+    int finalFreq = freq * matchWeight;
+    if (skipPos >= 0) {
+        if (inputLength >= 2) {
+            const int demotionRate = WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE
+                    * (10 * inputLength - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X)
+                    / (10 * inputLength
+                            - 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;
+        }
+    }
+    if (transposedPos >= 0) multiplyRate(
+            WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE, &finalFreq);
+    if (excessivePos >= 0) {
+        multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE, &finalFreq);
+        if (!proximityInfo->existsAdjacentProximityChars(inputIndex)) {
+            // If an excessive character is not adjacent to the left char or the right char,
+            // we will demote this word.
+            multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE, &finalFreq);
+        }
+    }
+    int lengthFreq = typedLetterMultiplier;
+    multiplyIntCapped(powerIntCapped(typedLetterMultiplier, outputIndex), &lengthFreq);
+    if ((outputIndex + 1) == matchCount) {
+        // Full exact match
+        if (outputIndex > 1) {
+            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
+            && outputIndex > 0) {
+        // A word with proximity corrections
+        if (DEBUG_DICT) {
+            LOGI("Found one proximity correction.");
+        }
+        multiplyIntCapped(typedLetterMultiplier, &finalFreq);
+        multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
+    }
+    if (DEBUG_DICT) {
+        LOGI("calc: %d, %d", outputIndex, sameLength);
+    }
+    if (sameLength) multiplyIntCapped(fullWordMultiplier, &finalFreq);
+
+    // TODO: check excessive count and transposed count
+    /*
+     If the last character of the user input word is the same as the next character
+     of the output word, and also all of characters of the user input are matched
+     to the output word, we'll promote that word a bit because
+     that word can be considered the combination of skipped and matched characters.
+     This means that the 'sm' pattern wins over the 'ma' pattern.
+     e.g.)
+     shel -> shell [mmmma] or [mmmsm]
+     hel -> hello [mmmaa] or [mmsma]
+     m ... matching
+     s ... skipping
+     a ... traversing all
+     */
+    if (matchCount == inputLength && matchCount >= 2 && skippedCount == 0
+            && word[matchCount] == word[matchCount - 1]) {
+        multiplyRate(WORDS_WITH_MATCH_SKIP_PROMOTION_RATE, &finalFreq);
+    }
+
+    return finalFreq;
+}
+
+/* static */
+int Correction::RankingAlgorithm::calcFreqForSplitTwoWords(
+        const int firstFreq, const int secondFreq, const Correction* correction) {
+    const int spaceProximityPos = correction->mSpaceProximityPos;
+    const int missingSpacePos = correction->mMissingSpacePos;
+    if (DEBUG_DICT) {
+        int inputCount = 0;
+        if (spaceProximityPos >= 0) ++inputCount;
+        if (missingSpacePos >= 0) ++inputCount;
+        assert(inputCount <= 1);
+    }
+    const bool isSpaceProximity = spaceProximityPos >= 0;
+    const int inputLength = correction->mInputLength;
+    const int firstWordLength = isSpaceProximity ? spaceProximityPos : missingSpacePos;
+    const int secondWordLength = isSpaceProximity
+            ? (inputLength - spaceProximityPos - 1)
+            : (inputLength - missingSpacePos);
+    const int typedLetterMultiplier = correction->TYPED_LETTER_MULTIPLIER;
+
+    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);
+    }
+
+    multiplyRate(WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE, &totalFreq);
+    return totalFreq;
+}
+
+} // namespace latinime
diff --git a/native/src/correction.h b/native/src/correction.h
new file mode 100644
index 0000000..62fe386
--- /dev/null
+++ b/native/src/correction.h
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#ifndef LATINIME_CORRECTION_H
+#define LATINIME_CORRECTION_H
+
+#include <stdint.h>
+#include "correction_state.h"
+
+#include "defines.h"
+
+namespace latinime {
+
+class ProximityInfo;
+
+class Correction {
+
+public:
+    typedef enum {
+        TRAVERSE_ALL_ON_TERMINAL,
+        TRAVERSE_ALL_NOT_ON_TERMINAL,
+        UNRELATED,
+        ON_TERMINAL,
+        NOT_ON_TERMINAL
+    } CorrectionType;
+
+    Correction(const int typedLetterMultiplier, const int fullWordMultiplier);
+    void initCorrection(
+            const ProximityInfo *pi, const int inputLength, const int maxWordLength);
+    void initCorrectionState(const int rootPos, const int childCount, const bool traverseAll);
+
+    // TODO: remove
+    void setCorrectionParams(const int skipPos, const int excessivePos, const int transposedPos,
+            const int spaceProximityPos, const int missingSpacePos);
+    void checkState();
+    bool initProcessState(const int index);
+
+    void getProcessState(int *matchedCount, int *inputIndex, int *outputIndex,
+            bool *traverseAllNodes, int *diffs);
+    int getOutputIndex();
+    int getInputIndex();
+
+    virtual ~Correction();
+    int getSpaceProximityPos() const {
+        return mSpaceProximityPos;
+    }
+    int getMissingSpacePos() const {
+        return mMissingSpacePos;
+    }
+
+    int getSkipPos() const {
+        return mSkipPos;
+    }
+
+    int getExcessivePos() const {
+        return mExcessivePos;
+    }
+
+    int getTransposedPos() const {
+        return mTransposedPos;
+    }
+
+    bool needsToPrune() const;
+
+    int getFreqForSplitTwoWords(const int firstFreq, const int secondFreq);
+    int getFinalFreq(const int freq, unsigned short **word, int* wordLength);
+
+    CorrectionType processCharAndCalcState(const int32_t c, const bool isTerminal);
+
+    int getDiffs() const {
+        return mDiffs;
+    }
+
+    /////////////////////////
+    // Tree helper methods
+    int goDownTree(const int parentIndex, const int childCount, const int firstChildPos);
+
+    inline int getTreeSiblingPos(const int index) const {
+        return mCorrectionStates[index].mSiblingPos;
+    }
+
+    inline void setTreeSiblingPos(const int index, const int pos) {
+        mCorrectionStates[index].mSiblingPos = pos;
+    }
+
+    inline int getTreeParentIndex(const int index) const {
+        return mCorrectionStates[index].mParentIndex;
+    }
+private:
+    inline void charMatched();
+    inline void incrementInputIndex();
+    inline void incrementOutputIndex();
+    inline bool needsToTraverseAllNodes();
+    inline void startToTraverseAllNodes();
+    inline bool isQuote(const unsigned short c);
+    inline CorrectionType processSkipChar(const int32_t c, const bool isTerminal);
+
+    // TODO: remove
+    inline void incrementDiffs() {
+        ++mDiffs;
+    }
+
+    const int TYPED_LETTER_MULTIPLIER;
+    const int FULL_WORD_MULTIPLIER;
+    const ProximityInfo *mProximityInfo;
+
+    int mMaxEditDistance;
+    int mMaxDepth;
+    int mInputLength;
+    int mSkipPos;
+    int mExcessivePos;
+    int mTransposedPos;
+    int mSpaceProximityPos;
+    int mMissingSpacePos;
+    int mTerminalInputIndex;
+    int mTerminalOutputIndex;
+    unsigned short mWord[MAX_WORD_LENGTH_INTERNAL];
+
+    CorrectionState mCorrectionStates[MAX_WORD_LENGTH_INTERNAL];
+
+    // The following member variables are being used as cache values of the correction state.
+    int mOutputIndex;
+    int mInputIndex;
+    int mDiffs;
+    int mMatchedCharCount;
+    int mSkippedCount;
+    bool mNeedsToTraverseAllNodes;
+    bool mMatching;
+    bool mSkipping;
+
+    class RankingAlgorithm {
+    public:
+        static int calculateFinalFreq(const int inputIndex, const int depth,
+                const int matchCount, const int freq, const bool sameLength,
+                const Correction* correction);
+        static int calcFreqForSplitTwoWords(const int firstFreq, const int secondFreq,
+                const Correction* correction);
+    };
+};
+} // namespace latinime
+#endif // LATINIME_CORRECTION_H
diff --git a/native/src/correction_state.h b/native/src/correction_state.h
new file mode 100644
index 0000000..7312226
--- /dev/null
+++ b/native/src/correction_state.h
@@ -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.
+ */
+
+#ifndef LATINIME_CORRECTION_STATE_H
+#define LATINIME_CORRECTION_STATE_H
+
+#include <stdint.h>
+
+#include "defines.h"
+
+namespace latinime {
+
+struct CorrectionState {
+    int mParentIndex;
+    int mSiblingPos;
+    uint16_t mChildCount;
+    uint8_t mInputIndex;
+    uint8_t mDiffs;
+    uint8_t mMatchedCount;
+    uint8_t mSkippedCount;
+    bool mMatching;
+    bool mSkipping;
+    bool mNeedsToTraverseAllNodes;
+
+};
+
+inline static void initCorrectionState(CorrectionState *state, const int rootPos,
+        const uint16_t childCount, const bool traverseAll) {
+    state->mParentIndex = -1;
+    state->mChildCount = childCount;
+    state->mInputIndex = 0;
+    state->mDiffs = 0;
+    state->mSiblingPos = rootPos;
+    state->mMatchedCount = 0;
+    state->mSkippedCount = 0;
+    state->mMatching = false;
+    state->mSkipping = false;
+    state->mNeedsToTraverseAllNodes = traverseAll;
+}
+
+} // namespace latinime
+#endif // LATINIME_CORRECTION_STATE_H
diff --git a/native/src/defines.h b/native/src/defines.h
index bea83b2..c1838d3 100644
--- a/native/src/defines.h
+++ b/native/src/defines.h
@@ -160,6 +160,7 @@
 #define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 60
 #define FULL_MATCHED_WORDS_PROMOTION_RATE 120
 #define WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE 90
+#define WORDS_WITH_MATCH_SKIP_PROMOTION_RATE 105
 
 // 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.
@@ -176,9 +177,6 @@
 #define MIN_USER_TYPED_LENGTH_FOR_MISSING_SPACE_SUGGESTION 3
 #define MIN_USER_TYPED_LENGTH_FOR_EXCESSIVE_CHARACTER_SUGGESTION 3
 
-// The size of next letters frequency array.  Zero will disable the feature.
-#define NEXT_LETTERS_SIZE 0
-
 #define min(a,b) ((a)<(b)?(a):(b))
 
 #endif // LATINIME_DEFINES_H
diff --git a/native/src/dictionary.cpp b/native/src/dictionary.cpp
index 9e32ee8..a49769b 100644
--- a/native/src/dictionary.cpp
+++ b/native/src/dictionary.cpp
@@ -57,12 +57,4 @@
     return mUnigramDictionary->isValidWord(word, length);
 }
 
-int Dictionary::getBigramPosition(unsigned short *word, int length) {
-    if (IS_LATEST_DICT_VERSION) {
-        return mUnigramDictionary->getBigramPosition(DICTIONARY_HEADER_SIZE, word, 0, length);
-    } else {
-        return mUnigramDictionary->getBigramPosition(0, word, 0, length);
-    }
-}
-
 } // namespace latinime
diff --git a/native/src/dictionary.h b/native/src/dictionary.h
index 73e03d8..d5de008 100644
--- a/native/src/dictionary.h
+++ b/native/src/dictionary.h
@@ -64,8 +64,6 @@
             const int pos, unsigned short *c, int *childrenPosition,
             bool *terminal, int *freq);
     static inline unsigned short toBaseLowerCase(unsigned short c);
-    // TODO: delete this
-    int getBigramPosition(unsigned short *word, int length);
 
 private:
     bool hasBigram();
diff --git a/native/src/proximity_info.cpp b/native/src/proximity_info.cpp
index c45393f..d437e25 100644
--- a/native/src/proximity_info.cpp
+++ b/native/src/proximity_info.cpp
@@ -78,7 +78,7 @@
     return getProximityCharsAt(index)[0];
 }
 
-bool ProximityInfo::existsCharInProximityAt(const int index, const int c) const {
+inline bool ProximityInfo::existsCharInProximityAt(const int index, const int c) const {
     const int *chars = getProximityCharsAt(index);
     int i = 0;
     while (chars[i] > 0 && i < MAX_PROXIMITY_CHARS_SIZE) {
@@ -114,8 +114,7 @@
 // in their list. The non-accented version of the character should be considered
 // "close", but not the other keys close to the non-accented version.
 ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId(
-        const int index, const unsigned short c, const int skipPos,
-        const int excessivePos, const int transposedPos) const {
+        const int index, const unsigned short c, const bool checkProximityChars) const {
     const int *currentChars = getProximityCharsAt(index);
     const unsigned short baseLowerC = Dictionary::toBaseLowerCase(c);
 
@@ -124,9 +123,7 @@
     if (currentChars[0] == baseLowerC || currentChars[0] == c)
         return SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR;
 
-    // If one of those is true, we should not check for close characters at all.
-    if (skipPos >= 0 || excessivePos >= 0 || transposedPos >= 0)
-        return UNRELATED_CHAR;
+    if (!checkProximityChars) return UNRELATED_CHAR;
 
     // If the non-accented, lowercased version of that first character matches c,
     // then we have a non-accented version of the accented character the user
diff --git a/native/src/proximity_info.h b/native/src/proximity_info.h
index 435a601..d9ed46f 100644
--- a/native/src/proximity_info.h
+++ b/native/src/proximity_info.h
@@ -23,6 +23,8 @@
 
 namespace latinime {
 
+class Correction;
+
 class ProximityInfo {
 public:
     typedef enum {                             // Used as a return value for character comparison
@@ -42,9 +44,9 @@
     bool existsCharInProximityAt(const int index, const int c) const;
     bool existsAdjacentProximityChars(const int index) const;
     ProximityType getMatchedProximityId(
-            const int index, const unsigned short c, const int skipPos,
-            const int excessivePos, const int transposedPos) const;
+            const int index, const unsigned short c, const bool checkProximityChars) const;
     bool sameAsTyped(const unsigned short *word, int length) const;
+
 private:
     int getStartIndexFromCoordinates(const int x, const int y) const;
     const int MAX_PROXIMITY_CHARS_SIZE;
diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp
index afa8bc5..bbfaea4 100644
--- a/native/src/unigram_dictionary.cpp
+++ b/native/src/unigram_dictionary.cpp
@@ -24,9 +24,7 @@
 #include "dictionary.h"
 #include "unigram_dictionary.h"
 
-#ifdef NEW_DICTIONARY_FORMAT
 #include "binary_format.h"
-#endif // NEW_DICTIONARY_FORMAT
 
 namespace latinime {
 
@@ -39,28 +37,23 @@
 UnigramDictionary::UnigramDictionary(const uint8_t* const streamStart, int typedLetterMultiplier,
         int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars,
         const bool isLatestDictVersion)
-#ifndef NEW_DICTIONARY_FORMAT
-    : DICT_ROOT(streamStart),
-#else // NEW_DICTIONARY_FORMAT
     : DICT_ROOT(streamStart + NEW_DICTIONARY_HEADER_SIZE),
-#endif // NEW_DICTIONARY_FORMAT
     MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords),
     MAX_PROXIMITY_CHARS(maxProximityChars), IS_LATEST_DICT_VERSION(isLatestDictVersion),
     TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier),
-#ifndef NEW_DICTIONARY_FORMAT
-    ROOT_POS(isLatestDictVersion ? DICTIONARY_HEADER_SIZE : 0),
-#else // NEW_DICTIONARY_FORMAT
       // TODO : remove this variable.
     ROOT_POS(0),
-#endif // NEW_DICTIONARY_FORMAT
     BYTES_IN_ONE_CHAR(MAX_PROXIMITY_CHARS * sizeof(int)),
     MAX_UMLAUT_SEARCH_DEPTH(DEFAULT_MAX_UMLAUT_SEARCH_DEPTH) {
     if (DEBUG_DICT) {
         LOGI("UnigramDictionary - constructor");
     }
+    mCorrection = new Correction(typedLetterMultiplier, fullWordMultiplier);
 }
 
-UnigramDictionary::~UnigramDictionary() {}
+UnigramDictionary::~UnigramDictionary() {
+    delete mCorrection;
+}
 
 static inline unsigned int getCodesBufferSize(const int* codes, const int codesSize,
         const int MAX_PROXIMITY_CHARS) {
@@ -174,12 +167,6 @@
             LOGI("%s %i", s, mFrequencies[j]);
 #endif
         }
-        LOGI("Next letters: ");
-        for (int k = 0; k < NEXT_LETTERS_SIZE; k++) {
-            if (mNextLettersFrequency[k] > 0) {
-                LOGI("%c = %d,", k, mNextLettersFrequency[k]);
-            }
-        }
     }
     PROF_END(20);
     PROF_CLOSE;
@@ -196,11 +183,12 @@
             proximityInfo, xcoordinates, ycoordinates, codes, codesSize, outWords, frequencies);
     if (DEBUG_DICT) assert(codesSize == mInputLength);
 
-    const int MAX_DEPTH = min(mInputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH);
+    const int maxDepth = min(mInputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH);
+    mCorrection->initCorrection(mProximityInfo, mInputLength, maxDepth);
     PROF_END(0);
 
     PROF_START(1);
-    getSuggestionCandidates(-1, -1, -1, mNextLettersFrequency, NEXT_LETTERS_SIZE, MAX_DEPTH);
+    getSuggestionCandidates(-1, -1, -1);
     PROF_END(1);
 
     PROF_START(2);
@@ -210,7 +198,7 @@
             if (DEBUG_DICT) {
                 LOGI("--- Suggest missing characters %d", i);
             }
-            getSuggestionCandidates(i, -1, -1, NULL, 0, MAX_DEPTH);
+            getSuggestionCandidates(i, -1, -1);
         }
     }
     PROF_END(2);
@@ -223,7 +211,7 @@
             if (DEBUG_DICT) {
                 LOGI("--- Suggest excessive characters %d", i);
             }
-            getSuggestionCandidates(-1, i, -1, NULL, 0, MAX_DEPTH);
+            getSuggestionCandidates(-1, i, -1);
         }
     }
     PROF_END(3);
@@ -236,7 +224,7 @@
             if (DEBUG_DICT) {
                 LOGI("--- Suggest transposed characters %d", i);
             }
-            getSuggestionCandidates(-1, -1, i, NULL, 0, mInputLength - 1);
+            getSuggestionCandidates(-1, -1, i);
         }
     }
     PROF_END(4);
@@ -249,7 +237,7 @@
             if (DEBUG_DICT) {
                 LOGI("--- Suggest missing space characters %d", i);
             }
-            getMissingSpaceWords(mInputLength, i);
+            getMissingSpaceWords(mInputLength, i, mCorrection);
         }
     }
     PROF_END(5);
@@ -268,7 +256,7 @@
                         i, x, y, proximityInfo->hasSpaceProximity(x, y));
             }
             if (proximityInfo->hasSpaceProximity(x, y)) {
-                getMistypedSpaceWords(mInputLength, i);
+                getMistypedSpaceWords(mInputLength, i, mCorrection);
             }
         }
     }
@@ -284,7 +272,6 @@
     mFrequencies = frequencies;
     mOutputChars = outWords;
     mInputLength = codesSize;
-    mMaxEditDistance = mInputLength < 5 ? 2 : mInputLength / 2;
     proximityInfo->setInputParams(codes, codesSize);
     mProximityInfo = proximityInfo;
 }
@@ -354,70 +341,43 @@
 static const char SPACE = ' ';
 
 void UnigramDictionary::getSuggestionCandidates(const int skipPos,
-        const int excessivePos, const int transposedPos, int *nextLetters,
-        const int nextLettersSize, const int maxDepth) {
+        const int excessivePos, const int transposedPos) {
     if (DEBUG_DICT) {
-        LOGI("getSuggestionCandidates %d", maxDepth);
         assert(transposedPos + 1 < mInputLength);
         assert(excessivePos < mInputLength);
         assert(missingPos < mInputLength);
     }
+    mCorrection->setCorrectionParams(skipPos, excessivePos, transposedPos,
+            -1 /* spaceProximityPos */, -1 /* missingSpacePos */);
     int rootPosition = ROOT_POS;
     // Get the number of children of root, then increment the position
     int childCount = Dictionary::getCount(DICT_ROOT, &rootPosition);
-    int depth = 0;
+    int outputIndex = 0;
 
-    mStackChildCount[0] = childCount;
-    mStackTraverseAll[0] = (mInputLength <= 0);
-    mStackNodeFreq[0] = 1;
-    mStackInputIndex[0] = 0;
-    mStackDiffs[0] = 0;
-    mStackSiblingPos[0] = rootPosition;
-    mStackOutputIndex[0] = 0;
+    mCorrection->initCorrectionState(rootPosition, childCount, (mInputLength <= 0));
 
     // Depth first search
-    while (depth >= 0) {
-        if (mStackChildCount[depth] > 0) {
-            --mStackChildCount[depth];
-            bool traverseAllNodes = mStackTraverseAll[depth];
-            int matchWeight = mStackNodeFreq[depth];
-            int inputIndex = mStackInputIndex[depth];
-            int diffs = mStackDiffs[depth];
-            int siblingPos = mStackSiblingPos[depth];
-            int outputIndex = mStackOutputIndex[depth];
+    while (outputIndex >= 0) {
+        if (mCorrection->initProcessState(outputIndex)) {
+            int siblingPos = mCorrection->getTreeSiblingPos(outputIndex);
             int firstChildPos;
-            // depth will never be greater than maxDepth because in that case,
-            // needsToTraverseChildrenNodes should be false
-            const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos, outputIndex,
-                    maxDepth, traverseAllNodes, matchWeight, inputIndex, diffs, skipPos,
-                    excessivePos, transposedPos, nextLetters, nextLettersSize, &childCount,
-                    &firstChildPos, &traverseAllNodes, &matchWeight, &inputIndex, &diffs,
-                    &siblingPos, &outputIndex);
+
+            const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos,
+                    mCorrection, &childCount, &firstChildPos, &siblingPos);
             // Update next sibling pos
-            mStackSiblingPos[depth] = siblingPos;
+            mCorrection->setTreeSiblingPos(outputIndex, siblingPos);
+
             if (needsToTraverseChildrenNodes) {
                 // Goes to child node
-                ++depth;
-                mStackChildCount[depth] = childCount;
-                mStackTraverseAll[depth] = traverseAllNodes;
-                mStackNodeFreq[depth] = matchWeight;
-                mStackInputIndex[depth] = inputIndex;
-                mStackDiffs[depth] = diffs;
-                mStackSiblingPos[depth] = firstChildPos;
-                mStackOutputIndex[depth] = outputIndex;
+                outputIndex = mCorrection->goDownTree(outputIndex, childCount, firstChildPos);
             }
         } else {
             // Goes to parent sibling node
-            --depth;
+            outputIndex = mCorrection->getTreeParentIndex(outputIndex);
         }
     }
 }
 
-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;
@@ -432,149 +392,18 @@
     }
 }
 
-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;
-    }
+void UnigramDictionary::getMissingSpaceWords(
+        const int inputLength, const int missingSpacePos, Correction *correction) {
+    correction->setCorrectionParams(-1 /* skipPos */, -1 /* excessivePos */,
+            -1 /* transposedPos */, -1 /* spaceProximityPos */, missingSpacePos);
+    getSplitTwoWordsSuggestion(inputLength, correction);
 }
 
-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);
-    }
-
-    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, false);
-}
-
-bool UnigramDictionary::getMistypedSpaceWords(const int inputLength, const int spaceProximityPos) {
-    return getSplitTwoWordsSuggestion(
-            inputLength, 0, spaceProximityPos, spaceProximityPos + 1,
-            inputLength - spaceProximityPos - 1, true);
-}
-
-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 >= 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;
-        }
-    }
-    if (transposedPos >= 0) multiplyRate(
-            WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE, &finalFreq);
-    if (excessivePos >= 0) {
-        multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE, &finalFreq);
-        if (!mProximityInfo->existsAdjacentProximityChars(inputIndex)) {
-            // If an excessive character is not adjacent to the left char or the right char,
-            // we will demote this word.
-            multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE, &finalFreq);
-        }
-    }
-    int 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.");
-            }
-            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 (DEBUG_DICT) {
-        LOGI("calc: %d, %d", depth, sameLength);
-    }
-    if (sameLength) multiplyIntCapped(FULL_WORD_MULTIPLIER, &finalFreq);
-    return finalFreq;
+void UnigramDictionary::getMistypedSpaceWords(
+        const int inputLength, const int spaceProximityPos, Correction *correction) {
+    correction->setCorrectionParams(-1 /* skipPos */, -1 /* excessivePos */,
+            -1 /* transposedPos */, spaceProximityPos, -1 /* missingSpacePos */);
+    getSplitTwoWordsSuggestion(inputLength, correction);
 }
 
 inline bool UnigramDictionary::needsToSkipCurrentNode(const unsigned short c,
@@ -584,35 +413,38 @@
     return (c == QUOTE && userTypedChar != QUOTE) || skipPos == depth;
 }
 
-
-inline void UnigramDictionary::onTerminal(unsigned short int* word, const int depth,
-        const uint8_t* const root, const uint8_t flags, const 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) {
-
-    const bool isSameAsTyped = sameLength ? mProximityInfo->sameAsTyped(word, depth + 1) : false;
-    if (isSameAsTyped) return;
-
-    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 (sameLength && depth >= mInputLength && skipPos < 0) {
-        registerNextLetter(word[mInputLength], nextLetters, nextLettersSize);
+inline void UnigramDictionary::onTerminal(const int freq, Correction *correction) {
+    int wordLength;
+    unsigned short* wordPointer;
+    const int finalFreq = correction->getFinalFreq(freq, &wordPointer, &wordLength);
+    if (finalFreq >= 0) {
+        addWord(wordPointer, wordLength, finalFreq);
     }
 }
 
-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;
+void UnigramDictionary::getSplitTwoWordsSuggestion(
+        const int inputLength, Correction* correction) {
+    const int spaceProximityPos = correction->getSpaceProximityPos();
+    const int missingSpacePos = correction->getMissingSpacePos();
+    if (DEBUG_DICT) {
+        int inputCount = 0;
+        if (spaceProximityPos >= 0) ++inputCount;
+        if (missingSpacePos >= 0) ++inputCount;
+        assert(inputCount <= 1);
+    }
+    const bool isSpaceProximity = spaceProximityPos >= 0;
+    const int firstWordStartPos = 0;
+    const int secondWordStartPos = isSpaceProximity ? (spaceProximityPos + 1) : missingSpacePos;
+    const int firstWordLength = isSpaceProximity ? spaceProximityPos : missingSpacePos;
+    const int secondWordLength = isSpaceProximity
+            ? (inputLength - spaceProximityPos - 1)
+            : (inputLength - missingSpacePos);
+
+    if (inputLength >= MAX_WORD_LENGTH) return;
     if (0 >= firstWordLength || 0 >= secondWordLength || firstWordStartPos >= secondWordStartPos
             || firstWordStartPos < 0 || secondWordStartPos + secondWordLength > inputLength)
-        return false;
+        return;
+
     const int newWordLength = firstWordLength + secondWordLength + 1;
     // Allocating variable length array on stack
     unsigned short word[newWordLength];
@@ -620,7 +452,7 @@
     if (DEBUG_DICT) {
         LOGI("First freq: %d", firstFreq);
     }
-    if (firstFreq <= 0) return false;
+    if (firstFreq <= 0) return;
 
     for (int i = 0; i < firstWordLength; ++i) {
         word[i] = mWord[i];
@@ -630,299 +462,21 @@
     if (DEBUG_DICT) {
         LOGI("Second  freq:  %d", secondFreq);
     }
-    if (secondFreq <= 0) return false;
+    if (secondFreq <= 0) return;
 
     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);
+    const int pairFreq = mCorrection->getFreqForSplitTwoWords(firstFreq, secondFreq);
     if (DEBUG_DICT) {
-        LOGI("Split two words:  %d, %d, %d, %d, %d", firstFreq, secondFreq, pairFreq, inputLength,
-                TYPED_LETTER_MULTIPLIER);
+        LOGI("Split two words:  %d, %d, %d, %d", firstFreq, secondFreq, pairFreq, inputLength);
     }
     addWord(word, newWordLength, pairFreq);
-    return true;
+    return;
 }
 
-#ifndef NEW_DICTIONARY_FORMAT
-// 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::getMostFrequentWordLike(const int startInputIndex,
-        const int inputLength, unsigned short *word) {
-    int pos = ROOT_POS;
-    int count = Dictionary::getCount(DICT_ROOT, &pos);
-    int maxFreq = 0;
-    int depth = 0;
-    unsigned short newWord[MAX_WORD_LENGTH_INTERNAL];
-    bool terminal = false;
-
-    mStackChildCount[0] = count;
-    mStackSiblingPos[0] = pos;
-
-    while (depth >= 0) {
-        if (mStackChildCount[depth] > 0) {
-            --mStackChildCount[depth];
-            int firstChildPos;
-            int newFreq;
-            int siblingPos = mStackSiblingPos[depth];
-            const bool needsToTraverseChildrenNodes = processCurrentNodeForExactMatch(siblingPos,
-                    startInputIndex, depth, newWord, &firstChildPos, &count, &terminal, &newFreq,
-                    &siblingPos);
-            mStackSiblingPos[depth] = siblingPos;
-            if (depth == (inputLength - 1)) {
-                // Traverse sibling node
-                if (terminal) {
-                    if (newFreq > maxFreq) {
-                        for (int i = 0; i < inputLength; ++i) word[i] = newWord[i];
-                        if (DEBUG_DICT && DEBUG_NODE) {
-#ifdef FLAG_DBG
-                            char s[inputLength + 1];
-                            for (int i = 0; i < inputLength; ++i) s[i] = word[i];
-                            s[inputLength] = 0;
-                            LOGI("New missing space word found: %d > %d (%s), %d, %d",
-                                    newFreq, maxFreq, s, inputLength, depth);
-#endif
-                        }
-                        maxFreq = newFreq;
-                    }
-                }
-            } else if (needsToTraverseChildrenNodes) {
-                // Traverse children nodes
-                ++depth;
-                mStackChildCount[depth] = count;
-                mStackSiblingPos[depth] = firstChildPos;
-            }
-        } else {
-            // Traverse parent node
-            --depth;
-        }
-    }
-
-    word[inputLength] = 0;
-    return maxFreq;
-}
-
-inline bool UnigramDictionary::processCurrentNodeForExactMatch(const int firstChildPos,
-        const int startInputIndex, const int depth, unsigned short *word, int *newChildPosition,
-        int *newCount, bool *newTerminal, int *newFreq, int *siblingPos) {
-    const int inputIndex = startInputIndex + depth;
-    unsigned short c;
-    *siblingPos = Dictionary::setDictionaryValues(DICT_ROOT, IS_LATEST_DICT_VERSION, firstChildPos,
-            &c, newChildPosition, newTerminal, newFreq);
-    const unsigned int inputC = mProximityInfo->getPrimaryCharAt(inputIndex);
-    if (DEBUG_DICT) {
-        assert(inputC <= U_SHORT_MAX);
-    }
-    const unsigned short baseLowerC = Dictionary::toBaseLowerCase(c);
-    const bool matched = (inputC == baseLowerC || inputC == c);
-    const bool hasChild = *newChildPosition != 0;
-    if (matched) {
-        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 (hasChild) {
-            *newCount = Dictionary::getCount(DICT_ROOT, newChildPosition);
-            return true;
-        } else {
-            return false;
-        }
-    } else {
-        // If this node is not user typed character, this method treats this word as unmatched.
-        // Thus newTerminal shouldn't be true.
-        *newTerminal = false;
-        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.
-inline bool UnigramDictionary::processCurrentNode(const int initialPos, const int initialDepth,
-        const int maxDepth, const bool initialTraverseAllNodes, int matchWeight, int inputIndex,
-        const int initialDiffs, 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 int pos = initialPos;
-    const int depth = initialDepth;
-    const int traverseAllNodes = initialTraverseAllNodes;
-    const int diffs = initialDiffs;
-
-    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 {
-        int inputIndexForProximity = inputIndex;
-
-        if (transposedPos >= 0) {
-            if (inputIndex == transposedPos) ++inputIndexForProximity;
-            if (inputIndex == (transposedPos + 1)) --inputIndexForProximity;
-        }
-
-        ProximityInfo::ProximityType matchedProximityCharId = mProximityInfo->getMatchedProximityId(
-                inputIndexForProximity, c, skipPos, excessivePos, transposedPos);
-        if (ProximityInfo::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 (ProximityInfo::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
-                + ((ProximityInfo::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
-
 // Wrapper for getMostFrequentWordLikeInner, which matches it to the previous
 // interface.
 inline int UnigramDictionary::getMostFrequentWordLike(const int startInputIndex,
@@ -1055,83 +609,8 @@
     return maxFreq;
 }
 
-// This function gets the frequency of the exact matching word in the dictionary.
-// If no match is found, it returns -1.
-int UnigramDictionary::getFrequency(const uint16_t* const inWord, const int length) const {
-    int pos = 0;
-    int wordPos = 0;
-    const uint8_t* const root = DICT_ROOT;
-
-    while (true) {
-        // If we already traversed the tree further than the word is long, there means
-        // there was no match (or we would have found it).
-        if (wordPos > length) return -1;
-        int charGroupCount = BinaryFormat::getGroupCountAndForwardPointer(root, &pos);
-        const uint16_t wChar = inWord[wordPos];
-        while (true) {
-            // If there are no more character groups in this node, it means we could not
-            // find a matching character for this depth, therefore there is no match.
-            if (0 >= charGroupCount) return -1;
-            const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
-            int32_t character = BinaryFormat::getCharCodeAndForwardPointer(root, &pos);
-            if (character == wChar) {
-                // This is the correct node. Only one character group may start with the same
-                // char within a node, so either we found our match in this node, or there is
-                // no match and we can return -1. So we will check all the characters in this
-                // character group indeed does match.
-                if (FLAG_HAS_MULTIPLE_CHARS & flags) {
-                    character = BinaryFormat::getCharCodeAndForwardPointer(root, &pos);
-                    while (NOT_A_CHARACTER != character) {
-                        ++wordPos;
-                        // If we shoot the length of the word we search for, or if we find a single
-                        // character that does not match, as explained above, it means the word is
-                        // not in the dictionary (by virtue of this chargroup being the only one to
-                        // match the word on the first character, but not matching the whole word).
-                        if (wordPos > length) return -1;
-                        if (inWord[wordPos] != character) return -1;
-                        character = BinaryFormat::getCharCodeAndForwardPointer(root, &pos);
-                    }
-                }
-                // If we come here we know that so far, we do match. Either we are on a terminal
-                // and we match the length, in which case we found it, or we traverse children.
-                // If we don't match the length AND don't have children, then a word in the
-                // dictionary fully matches a prefix of the searched word but not the full word.
-                ++wordPos;
-                if (FLAG_IS_TERMINAL & flags) {
-                    if (wordPos == length) {
-                        return BinaryFormat::readFrequencyWithoutMovingPointer(root, pos);
-                    }
-                    pos = BinaryFormat::skipFrequency(FLAG_IS_TERMINAL, pos);
-                }
-                if (FLAG_GROUP_ADDRESS_TYPE_NOADDRESS == (MASK_GROUP_ADDRESS_TYPE & flags))
-                    return -1;
-                // We have children and we are still shorter than the word we are searching for, so
-                // we need to traverse children. Put the pointer on the children position, and
-                // break
-                pos = BinaryFormat::readChildrenPosition(root, flags, pos);
-                break;
-            } else {
-                // This chargroup does not match, so skip the remaining part and go to the next.
-                if (FLAG_HAS_MULTIPLE_CHARS & flags) {
-                    pos = BinaryFormat::skipOtherCharacters(root, pos);
-                }
-                pos = BinaryFormat::skipFrequency(flags, pos);
-                pos = BinaryFormat::skipChildrenPosAndAttributes(root, flags, pos);
-            }
-            --charGroupCount;
-        }
-    }
-}
-
 bool UnigramDictionary::isValidWord(const uint16_t* const inWord, const int length) const {
-    return -1 != getFrequency(inWord, length);
-}
-
-int UnigramDictionary::getBigrams(unsigned short *word, int length, int *codes, int codesSize,
-        unsigned short *outWords, int *frequencies, int maxWordLength, int maxBigrams,
-        int maxAlternatives) {
-    // TODO: add implementation.
-    return 0;
+    return NOT_VALID_WORD != BinaryFormat::getTerminalPosition(DICT_ROOT, inWord, length);
 }
 
 // TODO: remove this function.
@@ -1154,23 +633,13 @@
 // there aren't any more nodes at this level, it merely returns the address of the first byte after
 // the current node in nextSiblingPosition. Thus, the caller must keep count of the nodes at any
 // given level, as output into newCount when traversing this level's parent.
-inline bool UnigramDictionary::processCurrentNode(const int initialPos, const int initialDepth,
-        const int maxDepth, const bool initialTraverseAllNodes, int matchWeight, int inputIndex,
-        const int initialDiffs, const int skipPos, const int excessivePos, const int transposedPos,
-        int *nextLetters, const int nextLettersSize, int *newCount, int *newChildrenPosition,
-        bool *newTraverseAllNodes, int *newMatchRate, int *newInputIndex, int *newDiffs,
-        int *nextSiblingPosition, int *newOutputIndex) {
+inline bool UnigramDictionary::processCurrentNode(const int initialPos,
+        Correction *correction, int *newCount,
+        int *newChildrenPosition, int *nextSiblingPosition) {
     if (DEBUG_DICT) {
-        int inputCount = 0;
-        if (skipPos >= 0) ++inputCount;
-        if (excessivePos >= 0) ++inputCount;
-        if (transposedPos >= 0) ++inputCount;
-        assert(inputCount <= 1);
+        correction->checkState();
     }
     int pos = initialPos;
-    int depth = initialDepth;
-    int traverseAllNodes = initialTraverseAllNodes;
-    int diffs = initialDiffs;
 
     // Flags contain the following information:
     // - Address type (MASK_GROUP_ADDRESS_TYPE) on two bits:
@@ -1182,6 +651,9 @@
     // - FLAG_HAS_BIGRAMS: whether this node has bigrams or not
     const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer(DICT_ROOT, &pos);
     const bool hasMultipleChars = (0 != (FLAG_HAS_MULTIPLE_CHARS & flags));
+    const bool isTerminalNode = (0 != (FLAG_IS_TERMINAL & flags));
+
+    bool needsToInvokeOnTerminal = false;
 
     // This gets only ONE character from the stream. Next there will be:
     // if FLAG_HAS_MULTIPLE CHARS: the other characters of the same node
@@ -1207,101 +679,21 @@
         const bool isLastChar = (NOT_A_CHARACTER == nextc);
         // If there are more chars in this nodes, then this virtual node is not a terminal.
         // If we are on the last char, this virtual node is a terminal if this node is.
-        const bool isTerminal = isLastChar && (0 != (FLAG_IS_TERMINAL & flags));
-        // If there are more chars in this node, then this virtual node has children.
-        // If we are on the last char, this virtual node has children if this node has.
-        const bool hasChildren = (!isLastChar) || BinaryFormat::hasChildrenInFlags(flags);
+        const bool isTerminal = isLastChar && isTerminalNode;
 
-        // This has to be done for each virtual char (this forwards the "inputIndex" which
-        // is the index in the user-inputted chars, as read by proximity chars.
-        if (excessivePos == depth && inputIndex < mInputLength - 1) ++inputIndex;
-        if (traverseAllNodes || needsToSkipCurrentNode(c, inputIndex, skipPos, depth)) {
-            mWord[depth] = c;
-            if (traverseAllNodes && isTerminal) {
-                // The frequency should be here, because we come here only if this is actually
-                // a terminal node, and we are on its last char.
-                const int freq = BinaryFormat::readFrequencyWithoutMovingPointer(DICT_ROOT, pos);
-                onTerminal(mWord, depth, DICT_ROOT, flags, pos, inputIndex, matchWeight, skipPos,
-                           excessivePos, transposedPos, freq, false, nextLetters, nextLettersSize);
-            }
-            if (!hasChildren) {
-                // If we don't have children here, that means we finished processing all
-                // characters of this node (we are on the last virtual node), AND we are in
-                // traverseAllNodes mode, which means we are searching for *completions*. We
-                // should skip the frequency if we have a terminal, and report the position
-                // of the next sibling. We don't have to return other values because we are
-                // returning false, as in "don't traverse children".
-                if (isTerminal) pos = BinaryFormat::skipFrequency(flags, pos);
-                *nextSiblingPosition =
-                        BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos);
-                return false;
-            }
-        } else {
-            int inputIndexForProximity = inputIndex;
-
-            if (transposedPos >= 0) {
-                if (inputIndex == transposedPos) ++inputIndexForProximity;
-                if (inputIndex == (transposedPos + 1)) --inputIndexForProximity;
-            }
-
-            int matchedProximityCharId = mProximityInfo->getMatchedProximityId(
-                    inputIndexForProximity, c, skipPos, excessivePos, transposedPos);
-            if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) {
-                // We found that this is an unrelated character, so we should give up traversing
-                // this node and its children entirely.
-                // However we may not be on the last virtual node yet so we skip the remaining
-                // characters in this node, the frequency if it's there, read the next sibling
-                // position to output it, then return false.
-                // We don't have to output other values because we return false, as in
-                // "don't traverse children".
-                if (!isLastChar) {
-                    pos = BinaryFormat::skipOtherCharacters(DICT_ROOT, pos);
-                }
-                pos = BinaryFormat::skipFrequency(flags, pos);
-                *nextSiblingPosition =
-                        BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos);
-                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 (ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR == matchedProximityCharId) {
-                multiplyIntCapped(TYPED_LETTER_MULTIPLIER, &matchWeight);
-            }
-            const bool isSameAsUserTypedLength = mInputLength == inputIndex + 1
-                    || (excessivePos == mInputLength - 1 && inputIndex == mInputLength - 2);
-            if (isSameAsUserTypedLength && isTerminal) {
-                const int freq = BinaryFormat::readFrequencyWithoutMovingPointer(DICT_ROOT, pos);
-                onTerminal(mWord, depth, DICT_ROOT, flags, pos, inputIndex, matchWeight, skipPos,
-                        excessivePos, transposedPos, freq, true, nextLetters, nextLettersSize);
-            }
-            // This character matched the typed character (enough to traverse the node at least)
-            // so we just evaluated it. Now we should evaluate this virtual node's children - that
-            // is, if it has any. If it has no children, we're done here - so we skip the end of
-            // the node, output the siblings position, and return false "don't traverse children".
-            // Note that !hasChildren implies isLastChar, so we know we don't have to skip any
-            // remaining char in this group for there can't be any.
-            if (!hasChildren) {
-                pos = BinaryFormat::skipFrequency(flags, pos);
-                *nextSiblingPosition =
-                        BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos);
-                return false;
-            }
-            // Start traversing all nodes after the index exceeds the user typed length
-            traverseAllNodes = isSameAsUserTypedLength;
-            diffs = diffs
-                    + ((ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) ? 1 : 0);
-            // Finally, we are ready to go to the next character, the next "virtual node".
-            // We should advance the input index.
-            // We do this in this branch of the 'if traverseAllNodes' because we are still matching
-            // characters to input; the other branch is not matching them but searching for
-            // completions, this is why it does not have to do it.
-            ++inputIndex;
-        }
-        // Optimization: Prune out words that are too long compared to how much was typed.
-        if (depth >= maxDepth || diffs > mMaxEditDistance) {
-            // We are giving up parsing this node and its children. Skip the rest of the node,
-            // output the sibling position, and return that we don't want to traverse children.
+        Correction::CorrectionType stateType = correction->processCharAndCalcState(
+                c, isTerminal);
+        if (stateType == Correction::TRAVERSE_ALL_ON_TERMINAL
+                || stateType == Correction::ON_TERMINAL) {
+            needsToInvokeOnTerminal = true;
+        } else if (stateType == Correction::UNRELATED) {
+            // We found that this is an unrelated character, so we should give up traversing
+            // this node and its children entirely.
+            // However we may not be on the last virtual node yet so we skip the remaining
+            // characters in this node, the frequency if it's there, read the next sibling
+            // position to output it, then return false.
+            // We don't have to output other values because we return false, as in
+            // "don't traverse children".
             if (!isLastChar) {
                 pos = BinaryFormat::skipOtherCharacters(DICT_ROOT, pos);
             }
@@ -1315,23 +707,41 @@
         // will take care of prefetching the next. If we finally found our last char, nextc will
         // contain NOT_A_CHARACTER.
         c = nextc;
-        // Also, the next char is one "virtual node" depth more than this char.
-        ++depth;
     } while (NOT_A_CHARACTER != c);
 
-    // If inputIndex is greater than mInputLength, that means there are no proximity chars.
-    // Here, that's all we are interested in so we don't need to check for isSameAsUserTypedLength.
-    if (mInputLength <= *newInputIndex) {
-        traverseAllNodes = true;
-    }
+    if (isTerminalNode) {
+        if (needsToInvokeOnTerminal) {
+            // The frequency should be here, because we come here only if this is actually
+            // a terminal node, and we are on its last char.
+            const int freq = BinaryFormat::readFrequencyWithoutMovingPointer(DICT_ROOT, pos);
+            onTerminal(freq, mCorrection);
+        }
 
-    // All the output values that are purely computation by this function are held in local
-    // variables. Output them to the caller.
-    *newTraverseAllNodes = traverseAllNodes;
-    *newMatchRate = matchWeight;
-    *newDiffs = diffs;
-    *newInputIndex = inputIndex;
-    *newOutputIndex = depth;
+        // If there are more chars in this node, then this virtual node has children.
+        // If we are on the last char, this virtual node has children if this node has.
+        const bool hasChildren = BinaryFormat::hasChildrenInFlags(flags);
+
+        // This character matched the typed character (enough to traverse the node at least)
+        // so we just evaluated it. Now we should evaluate this virtual node's children - that
+        // is, if it has any. If it has no children, we're done here - so we skip the end of
+        // the node, output the siblings position, and return false "don't traverse children".
+        // Note that !hasChildren implies isLastChar, so we know we don't have to skip any
+        // remaining char in this group for there can't be any.
+        if (!hasChildren) {
+            pos = BinaryFormat::skipFrequency(flags, pos);
+            *nextSiblingPosition =
+                    BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos);
+            return false;
+        }
+
+        // Optimization: Prune out words that are too long compared to how much was typed.
+        if (correction->needsToPrune()) {
+            pos = BinaryFormat::skipFrequency(flags, pos);
+            *nextSiblingPosition =
+                    BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos);
+            return false;
+        }
+    }
 
     // Now we finished processing this node, and we want to traverse children. If there are no
     // children, we can't come here.
@@ -1351,6 +761,4 @@
     return true;
 }
 
-#endif // NEW_DICTIONARY_FORMAT
-
 } // namespace latinime
diff --git a/native/src/unigram_dictionary.h b/native/src/unigram_dictionary.h
index f6045c6..cfe63ff 100644
--- a/native/src/unigram_dictionary.h
+++ b/native/src/unigram_dictionary.h
@@ -18,6 +18,8 @@
 #define LATINIME_UNIGRAM_DICTIONARY_H
 
 #include <stdint.h>
+#include "correction.h"
+#include "correction_state.h"
 #include "defines.h"
 #include "proximity_info.h"
 
@@ -30,7 +32,6 @@
 class UnigramDictionary {
 
 public:
-#ifdef NEW_DICTIONARY_FORMAT
 
     // Mask and flags for children address type selection.
     static const int MASK_GROUP_ADDRESS_TYPE = 0xC0;
@@ -62,26 +63,19 @@
     static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE = 0x10;
     static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20;
     static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30;
-#endif // NEW_DICTIONARY_FORMAT
 
     UnigramDictionary(const uint8_t* const streamStart, int typedLetterMultipler,
             int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars,
             const bool isLatestDictVersion);
-#ifndef NEW_DICTIONARY_FORMAT
-    bool isValidWord(unsigned short *word, int length);
-#else // NEW_DICTIONARY_FORMAT
     bool isValidWord(const uint16_t* const inWord, const int length) const;
-    int getBigrams(unsigned short *word, int length, int *codes, int codesSize,
-            unsigned short *outWords, int *frequencies, int maxWordLength, int maxBigrams,
-            int maxAlternatives);
-#endif // NEW_DICTIONARY_FORMAT
     int getBigramPosition(int pos, unsigned short *word, int offset, int length) const;
     int getSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
             const int *ycoordinates, const int *codes, const int codesSize, const int flags,
             unsigned short *outWords, int *frequencies);
-    ~UnigramDictionary();
+    virtual ~UnigramDictionary();
 
 private:
+
     void getWordSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
             const int *ycoordinates, const int *codes, const int codesSize,
             unsigned short *outWords, int *frequencies);
@@ -94,51 +88,24 @@
             const int *ycoordinates, const int *codes, const int codesSize,
             unsigned short *outWords, int *frequencies);
     void getSuggestionCandidates(const int skipPos, const int excessivePos,
-            const int transposedPos, int *nextLetters, const int nextLettersSize,
-            const int maxDepth);
+            const int transposedPos);
     bool addWord(unsigned short *word, int length, int frequency);
-    bool getSplitTwoWordsSuggestion(const int inputLength,
-            const int firstWordStartPos, const int firstWordLength,
-            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);
-    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 onTerminal(unsigned short int* word, const int depth,
-            const uint8_t* const root, const uint8_t flags, const 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);
+    void getSplitTwoWordsSuggestion(const int inputLength, Correction *correction);
+    void getMissingSpaceWords(
+            const int inputLength, const int missingSpacePos, Correction *correction);
+    void getMistypedSpaceWords(
+            const int inputLength, const int spaceProximityPos, Correction *correction);
+    void onTerminal(const int freq, Correction *correction);
     bool needsToSkipCurrentNode(const unsigned short c,
             const int inputIndex, const int skipPos, const int depth);
     // Process a node by considering proximity, missing and excessive character
-    bool processCurrentNode(const int initialPos, const int initialDepth,
-            const int maxDepth, const bool initialTraverseAllNodes, const int snr, int inputIndex,
-            const int initialDiffs, 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 *nextOutputIndex);
+    bool processCurrentNode(const int initialPos,
+            Correction *correction, int *newCount,
+            int *newChildPosition, int *nextSiblingPosition);
     int getMostFrequentWordLike(const int startInputIndex, const int inputLength,
             unsigned short *word);
-#ifndef NEW_DICTIONARY_FORMAT
-    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);
-    // 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);
-    // Process a node by considering missing space
-    bool processCurrentNodeForExactMatch(const int firstChildPos,
-            const int startInputIndex, const int depth, unsigned short *word,
-            int *newChildPosition, int *newCount, bool *newTerminal, int *newFreq, int *siblingPos);
-#else // NEW_DICTIONARY_FORMAT
-    int getFrequency(const uint16_t* const inWord, const int length) const;
     int getMostFrequentWordLikeInner(const uint16_t* const inWord, const int length,
             short unsigned int* outWord);
-#endif // NEW_DICTIONARY_FORMAT
 
     const uint8_t* const DICT_ROOT;
     const int MAX_WORD_LENGTH;
@@ -162,20 +129,15 @@
 
     int *mFrequencies;
     unsigned short *mOutputChars;
-    const ProximityInfo *mProximityInfo;
+    ProximityInfo *mProximityInfo;
+    Correction *mCorrection;
     int mInputLength;
     // MAX_WORD_LENGTH_INTERNAL must be bigger than MAX_WORD_LENGTH
     unsigned short mWord[MAX_WORD_LENGTH_INTERNAL];
-    int mMaxEditDistance;
 
-    int mStackChildCount[MAX_WORD_LENGTH_INTERNAL];
-    bool mStackTraverseAll[MAX_WORD_LENGTH_INTERNAL];
-    int mStackNodeFreq[MAX_WORD_LENGTH_INTERNAL];
-    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];
+    int mStackChildCount[MAX_WORD_LENGTH_INTERNAL];// TODO: remove
+    int mStackInputIndex[MAX_WORD_LENGTH_INTERNAL];// TODO: remove
+    int mStackSiblingPos[MAX_WORD_LENGTH_INTERNAL];// TODO: remove
 };
 } // namespace latinime
 
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilderTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilderTests.java
index 0d2802d..1c5661b 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilderTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilderTests.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.keyboard.internal;
 
-import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder.MiniKeyboardLayoutParams;
+import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder.MiniKeyboardParams;
 
 import android.test.AndroidTestCase;
 
@@ -41,9 +41,9 @@
     }
 
     public void testLayoutError() {
-        MiniKeyboardLayoutParams params = null;
+        MiniKeyboardParams params = null;
         try {
-            params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS + 1, WIDTH, HEIGHT, WIDTH * 2,
+            params = new MiniKeyboardParams(10, MAX_COLUMNS + 1, WIDTH, HEIGHT, WIDTH * 2,
                     WIDTH * MAX_COLUMNS);
             fail("Should throw IllegalArgumentException");
         } catch (IllegalArgumentException e) {
@@ -58,7 +58,7 @@
 
     // [1]
     public void testLayout1KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("1 key M0 columns", 1, params.mNumColumns);
         assertEquals("1 key M0 rows", 1, params.mNumRows);
@@ -71,7 +71,7 @@
 
     // |[1]
     public void testLayout1KeyL0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
         assertEquals("1 key L0 columns", 1, params.mNumColumns);
         assertEquals("1 key L0 rows", 1, params.mNumRows);
@@ -84,7 +84,7 @@
 
     // |___ [1]
     public void testLayout1KeyL1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
         assertEquals("1 key L1 columns", 1, params.mNumColumns);
         assertEquals("1 key L1 rows", 1, params.mNumRows);
@@ -97,7 +97,7 @@
 
     // |___ ___ [1]
     public void testLayout1KeyL2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
         assertEquals("1 key L2 columns", 1, params.mNumColumns);
         assertEquals("1 key L2 rows", 1, params.mNumRows);
@@ -110,7 +110,7 @@
 
     // [1]|
     public void testLayout1KeyR0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
         assertEquals("1 key R0 columns", 1, params.mNumColumns);
         assertEquals("1 key R0 rows", 1, params.mNumRows);
@@ -123,7 +123,7 @@
 
     // [1] ___|
     public void testLayout1KeyR1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
         assertEquals("1 key R1 columns", 1, params.mNumColumns);
         assertEquals("1 key R1 rows", 1, params.mNumRows);
@@ -136,7 +136,7 @@
 
     // [1] ___ ___|
     public void testLayout1KeyR2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
         assertEquals("1 key R2 columns", 1, params.mNumColumns);
         assertEquals("1 key R2 rows", 1, params.mNumRows);
@@ -149,7 +149,7 @@
 
     // [1] [2]
     public void testLayout2KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("2 key M0 columns", 2, params.mNumColumns);
         assertEquals("2 key M0 rows", 1, params.mNumRows);
@@ -163,7 +163,7 @@
 
     // |[1] [2]
     public void testLayout2KeyL0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
         assertEquals("2 key L0 columns", 2, params.mNumColumns);
         assertEquals("2 key L0 rows", 1, params.mNumRows);
@@ -177,7 +177,7 @@
 
     // |___ [1] [2]
     public void testLayout2KeyL1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
         assertEquals("2 key L1 columns", 2, params.mNumColumns);
         assertEquals("2 key L1 rows", 1, params.mNumRows);
@@ -191,7 +191,7 @@
 
     // |___ ___ [1] [2]
     public void testLayout2KeyL2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
         assertEquals("2 key L2 columns", 2, params.mNumColumns);
         assertEquals("2 key L2 rows", 1, params.mNumRows);
@@ -205,7 +205,7 @@
 
     // [2] [1]|
     public void testLayout2KeyR0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
         assertEquals("2 key R0 columns", 2, params.mNumColumns);
         assertEquals("2 key R0 rows", 1, params.mNumRows);
@@ -219,7 +219,7 @@
 
     // [2] [1] ___|
     public void testLayout2KeyR1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
         assertEquals("2 key R1 columns", 2, params.mNumColumns);
         assertEquals("2 key R1 rows", 1, params.mNumRows);
@@ -233,7 +233,7 @@
 
     // [1] [2] ___ ___|
     public void testLayout2KeyR2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
         assertEquals("2 key R2 columns", 2, params.mNumColumns);
         assertEquals("2 key R2 rows", 1, params.mNumRows);
@@ -247,7 +247,7 @@
 
     // [3] [1] [2]
     public void testLayout3KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("3 key columns", 3, params.mNumColumns);
         assertEquals("3 key rows", 1, params.mNumRows);
@@ -262,7 +262,7 @@
 
     // |[1] [2] [3]
     public void testLayout3KeyL0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
         assertEquals("3 key L0 columns", 3, params.mNumColumns);
         assertEquals("3 key L0 rows", 1, params.mNumRows);
@@ -277,7 +277,7 @@
 
     // |___ [1] [2] [3]
     public void testLayout3KeyL1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
         assertEquals("3 key L1 columns", 3, params.mNumColumns);
         assertEquals("3 key L1 rows", 1, params.mNumRows);
@@ -292,7 +292,7 @@
 
     // |___ ___ [3] [1] [2]
     public void testLayout3KeyL2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
         assertEquals("3 key L2 columns", 3, params.mNumColumns);
         assertEquals("3 key L2 rows", 1, params.mNumRows);
@@ -307,7 +307,7 @@
 
     // [3] [2] [1]|
     public void testLayout3KeyR0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
         assertEquals("3 key R0 columns", 3, params.mNumColumns);
         assertEquals("3 key R0 rows", 1, params.mNumRows);
@@ -322,7 +322,7 @@
 
     // [3] [2] [1] ___|
     public void testLayout3KeyR1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
         assertEquals("3 key R1 columns", 3, params.mNumColumns);
         assertEquals("3 key R1 rows", 1, params.mNumRows);
@@ -337,7 +337,7 @@
 
     // [3] [1] [2] ___ ___|
     public void testLayout3KeyR2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
         assertEquals("3 key R2 columns", 3, params.mNumColumns);
         assertEquals("3 key R2 rows", 1, params.mNumRows);
@@ -352,7 +352,7 @@
 
     // [3] [1] [2] [4]
     public void testLayout4KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("4 key columns", 4, params.mNumColumns);
         assertEquals("4 key rows", 1, params.mNumRows);
@@ -368,7 +368,7 @@
 
     // |[1] [2] [3] [4]
     public void testLayout4KeyL0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
         assertEquals("4 key L0 columns", 4, params.mNumColumns);
         assertEquals("4 key L0 rows", 1, params.mNumRows);
@@ -384,7 +384,7 @@
 
     // |___ [1] [2] [3] [4]
     public void testLayout4KeyL1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
         assertEquals("4 key L1 columns", 4, params.mNumColumns);
         assertEquals("4 key L1 rows", 1, params.mNumRows);
@@ -400,7 +400,7 @@
 
     // |___ ___ [3] [1] [2] [4]
     public void testLayout4KeyL2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
         assertEquals("4 key L2 columns", 4, params.mNumColumns);
         assertEquals("4 key L2 rows", 1, params.mNumRows);
@@ -416,7 +416,7 @@
 
     // [4] [3] [2] [1]|
     public void testLayout4KeyR0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
         assertEquals("4 key R0 columns", 4, params.mNumColumns);
         assertEquals("4 key R0 rows", 1, params.mNumRows);
@@ -432,7 +432,7 @@
 
     // [4] [3] [2] [1] ___|
     public void testLayout4KeyR1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
         assertEquals("4 key R1 columns", 4, params.mNumColumns);
         assertEquals("4 key R1 rows", 1, params.mNumRows);
@@ -448,7 +448,7 @@
 
     // [4] [3] [1] [2] ___ ___|
     public void testLayout4KeyR2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
         assertEquals("4 key R2 columns", 4, params.mNumColumns);
         assertEquals("4 key R2 rows", 1, params.mNumRows);
@@ -464,7 +464,7 @@
 
     // [5] [3] [1] [2] [4]
     public void testLayout5KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("5 key columns", 5, params.mNumColumns);
         assertEquals("5 key rows", 1, params.mNumRows);
@@ -481,7 +481,7 @@
 
     // |[1] [2] [3] [4] [5]
     public void testLayout5KeyL0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
         assertEquals("5 key L0 columns", 5, params.mNumColumns);
         assertEquals("5 key L0 rows", 1, params.mNumRows);
@@ -498,7 +498,7 @@
 
     // |___ [1] [2] [3] [4] [5]
     public void testLayout5KeyL1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
         assertEquals("5 key L1 columns", 5, params.mNumColumns);
         assertEquals("5 key L1 rows", 1, params.mNumRows);
@@ -515,7 +515,7 @@
 
     // |___ ___ [3] [1] [2] [4] [5]
     public void testLayout5KeyL2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
         assertEquals("5 key L2 columns", 5, params.mNumColumns);
         assertEquals("5 key L2 rows", 1, params.mNumRows);
@@ -532,7 +532,7 @@
 
     // [5] [4] [3] [2] [1]|
     public void testLayout5KeyR0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
         assertEquals("5 key R0 columns", 5, params.mNumColumns);
         assertEquals("5 key R0 rows", 1, params.mNumRows);
@@ -549,7 +549,7 @@
 
     // [5] [4] [3] [2] [1] ___|
     public void testLayout5KeyR1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
         assertEquals("5 key R1 columns", 5, params.mNumColumns);
         assertEquals("5 key R1 rows", 1, params.mNumRows);
@@ -566,7 +566,7 @@
 
     // [5] [4] [3] [1] [2] ___ ___|
     public void testLayout5KeyR2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
         assertEquals("5 key R2 columns", 5, params.mNumColumns);
         assertEquals("5 key R2 rows", 1, params.mNumRows);
@@ -584,7 +584,7 @@
     // [6] [4] [5]
     // [3] [1] [2]
     public void testLayout6KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("6 key columns", 3, params.mNumColumns);
         assertEquals("6 key rows", 2, params.mNumRows);
@@ -603,7 +603,7 @@
     // |[4] [5] [6]
     // |[1] [2] [3]
     public void testLayout6KeyL0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
         assertEquals("6 key L0 columns", 3, params.mNumColumns);
         assertEquals("6 key L0 rows", 2, params.mNumRows);
@@ -622,7 +622,7 @@
     // |___ [4] [5] [6]
     // |___ [1] [2] [3]
     public void testLayout6KeyL1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
         assertEquals("6 key L1 columns", 3, params.mNumColumns);
         assertEquals("6 key L1 rows", 2, params.mNumRows);
@@ -641,7 +641,7 @@
     // |___ ___ [6] [4] [5]
     // |___ ___ [3] [1] [2]
     public void testLayout6KeyL2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
         assertEquals("6 key L2 columns", 3, params.mNumColumns);
         assertEquals("6 key L2 rows", 2, params.mNumRows);
@@ -660,7 +660,7 @@
     // [6] [5] [4]|
     // [3] [2] [1]|
     public void testLayout6KeyR0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
         assertEquals("6 key R0 columns", 3, params.mNumColumns);
         assertEquals("6 key R0 rows", 2, params.mNumRows);
@@ -679,7 +679,7 @@
     // [6] [5] [4] ___|
     // [3] [2] [1] ___|
     public void testLayout6KeyR1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
         assertEquals("6 key R1 columns", 3, params.mNumColumns);
         assertEquals("6 key R1 rows", 2, params.mNumRows);
@@ -698,7 +698,7 @@
     // [6] [4] [5] ___ ___|
     // [3] [1] [2] ___ ___|
     public void testLayout6KeyR2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
         assertEquals("6 key R2 columns", 3, params.mNumColumns);
         assertEquals("6 key R2 rows", 2, params.mNumRows);
@@ -717,7 +717,7 @@
     //   [7] [5] [6]
     // [3] [1] [2] [4]
     public void testLayout7KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("7 key columns", 4, params.mNumColumns);
         assertEquals("7 key rows", 2, params.mNumRows);
@@ -737,7 +737,7 @@
     // |[5] [6] [7]
     // |[1] [2] [3] [4]
     public void testLayout7KeyL0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
         assertEquals("7 key L0 columns", 4, params.mNumColumns);
         assertEquals("7 key L0 rows", 2, params.mNumRows);
@@ -757,7 +757,7 @@
     // |___ [5] [6] [7]
     // |___ [1] [2] [3] [4]
     public void testLayout7KeyL1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
         assertEquals("7 key L1 columns", 4, params.mNumColumns);
         assertEquals("7 key L1 rows", 2, params.mNumRows);
@@ -777,7 +777,7 @@
     // |___ ___   [7] [5] [6]
     // |___ ___ [3] [1] [2] [4]
     public void testLayout7KeyL2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
         assertEquals("7 key L2 columns", 4, params.mNumColumns);
         assertEquals("7 key L2 rows", 2, params.mNumRows);
@@ -797,7 +797,7 @@
     //     [7] [6] [5]|
     // [4] [3] [2] [1]|
     public void testLayout7KeyR0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
         assertEquals("7 key R0 columns", 4, params.mNumColumns);
         assertEquals("7 key R0 rows", 2, params.mNumRows);
@@ -817,7 +817,7 @@
     //     [7] [6] [5] ___|
     // [4] [3] [2] [1] ___|
     public void testLayout7KeyR1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
         assertEquals("7 key R1 columns", 4, params.mNumColumns);
         assertEquals("7 key R1 rows", 2, params.mNumRows);
@@ -837,7 +837,7 @@
     //   [7] [5] [6]   ___ ___|
     // [4] [3] [1] [2] ___ ___|
     public void testLayout7KeyR2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
         assertEquals("7 key R2 columns", 4, params.mNumColumns);
         assertEquals("7 key R2 rows", 2, params.mNumRows);
@@ -856,7 +856,7 @@
 
     // [7] [6] [5] [3] [1] [2] [4] ___|
     public void testLayout7KeyR3Max7() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, 7, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(7, 7, WIDTH,
                 HEIGHT, XPOS_R3, KEYBOARD_WIDTH);
         assertEquals("7 key R2 columns", 7, params.mNumColumns);
         assertEquals("7 key R2 rows", 1, params.mNumRows);
@@ -876,7 +876,7 @@
     // [7] [5] [6] [8]
     // [3] [1] [2] [4]
     public void testLayout8KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("8 key M0 columns", 4, params.mNumColumns);
         assertEquals("8 key M0 rows", 2, params.mNumRows);
@@ -897,7 +897,7 @@
     // |[5] [6] [7] [8]
     // |[1] [2] [3] [4]
     public void testLayout8KeyL0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
         assertEquals("8 key L0 columns", 4, params.mNumColumns);
         assertEquals("8 key L0 rows", 2, params.mNumRows);
@@ -918,7 +918,7 @@
     // |___ [5] [6] [7] [8]
     // |___ [1] [2] [3] [4]
     public void testLayout8KeyL1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
         assertEquals("8 key L1 columns", 4, params.mNumColumns);
         assertEquals("8 key L1 rows", 2, params.mNumRows);
@@ -939,7 +939,7 @@
     // |___ ___ [7] [5] [6] [8]
     // |___ ___ [3] [1] [2] [4]
     public void testLayout8KeyL2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
         assertEquals("8 key L2 columns", 4, params.mNumColumns);
         assertEquals("8 key L2 rows", 2, params.mNumRows);
@@ -960,7 +960,7 @@
     // [8] [7] [6] [5]|
     // [4] [3] [2] [1]|
     public void testLayout8KeyR0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
         assertEquals("8 key R0 columns", 4, params.mNumColumns);
         assertEquals("8 key R0 rows", 2, params.mNumRows);
@@ -981,7 +981,7 @@
     // [8] [7] [6] [5] ___|
     // [4] [3] [2] [1] ___|
     public void testLayout8KeyR1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
         assertEquals("8 key R1 columns", 4, params.mNumColumns);
         assertEquals("8 key R1 rows", 2, params.mNumRows);
@@ -1002,7 +1002,7 @@
     // [8] [7] [5] [6] ___ ___|
     // [4] [3] [1] [2] ___ ___|
     public void testLayout8KeyR2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
         assertEquals("8 key R2 columns", 4, params.mNumColumns);
         assertEquals("8 key R2 rows", 2, params.mNumRows);
@@ -1023,7 +1023,7 @@
     //   [8] [6] [7] [9]
     // [5] [3] [1] [2] [4]
     public void testLayout9KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("9 key M0 columns", 5, params.mNumColumns);
         assertEquals("9 key M0 rows", 2, params.mNumRows);
@@ -1045,7 +1045,7 @@
     // |[6] [7] [8] [9]
     // |[1] [2] [3] [4] [5]
     public void testLayout9KeyL0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
         assertEquals("9 key L0 columns", 5, params.mNumColumns);
         assertEquals("9 key L0 rows", 2, params.mNumRows);
@@ -1067,7 +1067,7 @@
     // |___ [6] [7] [8] [9]
     // |___ [1] [2] [3] [4] [5]
     public void testLayout9KeyL1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
         assertEquals("9 key L1 columns", 5, params.mNumColumns);
         assertEquals("9 key L1 rows", 2, params.mNumRows);
@@ -1089,7 +1089,7 @@
     // |___ ___   [8] [6] [7] [9]
     // |___ ___ [3] [1] [2] [4] [5]
     public void testLayout9KeyL2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
         assertEquals("9 key L2 columns", 5, params.mNumColumns);
         assertEquals("9 key L2 rows", 2, params.mNumRows);
@@ -1111,7 +1111,7 @@
     //     [9] [8] [7] [6]|
     // [5] [4] [3] [2] [1]|
     public void testLayout9KeyR0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
         assertEquals("9 key R0 columns", 5, params.mNumColumns);
         assertEquals("9 key R0 rows", 2, params.mNumRows);
@@ -1133,7 +1133,7 @@
     //     [9] [8] [7] [6] ___|
     // [5] [4] [3] [2] [1] ___|
     public void testLayout9KeyR1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
         assertEquals("9 key R1 columns", 5, params.mNumColumns);
         assertEquals("9 key R1 rows", 2, params.mNumRows);
@@ -1155,7 +1155,7 @@
     //   [9] [8] [6] [7]   ___ ___|
     // [5] [4] [3] [1] [2] ___ ___|
     public void testLayout9KeyR2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
         assertEquals("9 key R2 columns", 5, params.mNumColumns);
         assertEquals("9 key R2 rows", 2, params.mNumRows);
@@ -1177,7 +1177,7 @@
     // [A] [8] [6] [7] [9]
     // [5] [3] [1] [2] [4]
     public void testLayout10KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("10 key M0 columns", 5, params.mNumColumns);
         assertEquals("10 key M0 rows", 2, params.mNumRows);
@@ -1200,7 +1200,7 @@
     // |[6] [7] [8] [9] [A]
     // |[1] [2] [3] [4] [5]
     public void testLayout10KeyL0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L0, KEYBOARD_WIDTH);
         assertEquals("10 key L0 columns", 5, params.mNumColumns);
         assertEquals("10 key L0 rows", 2, params.mNumRows);
@@ -1223,7 +1223,7 @@
     // |___ [6] [7] [8] [9] [A]
     // |___ [1] [2] [3] [4] [5]
     public void testLayout10KeyL1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L1, KEYBOARD_WIDTH);
         assertEquals("10 key L1 columns", 5, params.mNumColumns);
         assertEquals("10 key L1 rows", 2, params.mNumRows);
@@ -1246,7 +1246,7 @@
     // |___ ___ [8] [6] [7] [9] [A]
     // |___ ___ [3] [1] [2] [4] [5]
     public void testLayout10KeyL2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_L2, KEYBOARD_WIDTH);
         assertEquals("10 key L2 columns", 5, params.mNumColumns);
         assertEquals("10 key L2 rows", 2, params.mNumRows);
@@ -1269,7 +1269,7 @@
     // [A] [9] [8] [7] [6]|
     // [5] [4] [3] [2] [1]|
     public void testLayout10KeyR0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R0, KEYBOARD_WIDTH);
         assertEquals("10 key R0 columns", 5, params.mNumColumns);
         assertEquals("10 key R0 rows", 2, params.mNumRows);
@@ -1292,7 +1292,7 @@
     // [A] [9] [8] [7] [6] ___|
     // [5] [4] [3] [2] [1] ___|
     public void testLayout10KeyR1() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R1, KEYBOARD_WIDTH);
         assertEquals("10 key R1 columns", 5, params.mNumColumns);
         assertEquals("10 key R1 rows", 2, params.mNumRows);
@@ -1315,7 +1315,7 @@
     // [A] [9] [8] [6] [7] ___ ___|
     // [5] [4] [3] [1] [2] ___ ___|
     public void testLayout10KeyR2() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_R2, KEYBOARD_WIDTH);
         assertEquals("10 key R2 columns", 5, params.mNumColumns);
         assertEquals("10 key R2 rows", 2, params.mNumRows);
@@ -1339,7 +1339,7 @@
     // [7] [5] [6] [8]
     // [3] [1] [2] [4]
     public void testLayout11KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(11, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(11, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("11 key M0 columns", 4, params.mNumColumns);
         assertEquals("11 key M0 rows", 3, params.mNumRows);
@@ -1364,7 +1364,7 @@
     // [7] [5] [6] [8]
     // [3] [1] [2] [4]
     public void testLayout12KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(12, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(12, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("12 key M0 columns", 4, params.mNumColumns);
         assertEquals("12 key M0 rows", 3, params.mNumRows);
@@ -1391,7 +1391,7 @@
     // [A] [8] [6] [7] [9]
     // [5] [3] [1] [2] [4]
     public void testLayout13KeyM0() {
-        MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(13, MAX_COLUMNS, WIDTH,
+        MiniKeyboardParams params = new MiniKeyboardParams(13, MAX_COLUMNS, WIDTH,
                 HEIGHT, XPOS_M0, KEYBOARD_WIDTH);
         assertEquals("13 key M0 columns", 5, params.mNumColumns);
         assertEquals("13 key M0 rows", 3, params.mNumRows);
diff --git a/tests/src/com/android/inputmethod/latin/SuggestHelper.java b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
index 07d0e5d..48fe817 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestHelper.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestHelper.java
@@ -16,27 +16,26 @@
 
 package com.android.inputmethod.latin;
 
+import android.content.Context;
+import android.text.TextUtils;
+
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.KeyDetector;
 import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.LatinKeyboard;
 
-import android.content.Context;
-import android.text.TextUtils;
-
 import java.io.File;
-import java.util.List;
 
 public class SuggestHelper {
     protected final Suggest mSuggest;
-    private final LatinKeyboard mKeyboard;
+    protected final LatinKeyboard mKeyboard;
     private final KeyDetector mKeyDetector;
 
     public SuggestHelper(Context context, int dictionaryId, KeyboardId keyboardId) {
         // 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);
+        mKeyboard = new LatinKeyboard.Builder(context).load(keyboardId).build();
         mKeyDetector = new KeyDetector(0);
         init();
     }
@@ -44,7 +43,7 @@
     protected SuggestHelper(Context context, File dictionaryPath, long startOffset, long length,
             KeyboardId keyboardId) {
         mSuggest = new Suggest(context, dictionaryPath, startOffset, length, null);
-        mKeyboard = new LatinKeyboard(context, keyboardId, keyboardId.mWidth);
+        mKeyboard = new LatinKeyboard.Builder(context).load(keyboardId).build();
         mKeyDetector = new KeyDetector(0);
         init();
     }
@@ -54,7 +53,7 @@
         mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL);
         mKeyDetector.setKeyboard(mKeyboard, 0, 0);
         mKeyDetector.setProximityCorrectionEnabled(true);
-        mKeyDetector.setProximityThreshold(mKeyboard.getMostCommonKeyWidth());
+        mKeyDetector.setProximityThreshold(mKeyboard.mMostCommonKeyWidth);
     }
 
     public void setCorrectionMode(int correctionMode) {
@@ -66,8 +65,7 @@
     }
 
     private void addKeyInfo(WordComposer word, char c) {
-        final List<Key> keys = mKeyboard.getKeys();
-        for (final Key key : keys) {
+        for (final Key key : mKeyboard.mKeys) {
             if (key.mCode == c) {
                 final int x = key.mX + key.mWidth / 2;
                 final int y = key.mY + key.mHeight / 2;
@@ -96,19 +94,22 @@
 
     // TODO: This may be slow, but is OK for test so far.
     public SuggestedWords getSuggestions(CharSequence typed) {
-        return mSuggest.getSuggestions(null, createWordComposer(typed), null);
+        return mSuggest.getSuggestions(null, createWordComposer(typed), null,
+                mKeyboard.getProximityInfo());
     }
 
     public CharSequence getFirstSuggestion(CharSequence typed) {
         WordComposer word = createWordComposer(typed);
-        SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null);
+        SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null,
+                mKeyboard.getProximityInfo());
         // Note that suggestions.getWord(0) is the word user typed.
         return suggestions.size() > 1 ? suggestions.getWord(1) : null;
     }
 
     public CharSequence getAutoCorrection(CharSequence typed) {
         WordComposer word = createWordComposer(typed);
-        SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null);
+        SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null,
+                mKeyboard.getProximityInfo());
         // Note that suggestions.getWord(0) is the word user typed.
         return (suggestions.size() > 1 && mSuggest.hasAutoCorrection())
                 ? suggestions.getWord(1) : null;
@@ -116,7 +117,8 @@
 
     public int getSuggestIndex(CharSequence typed, CharSequence expected) {
         WordComposer word = createWordComposer(typed);
-        SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null);
+        SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null,
+                mKeyboard.getProximityInfo());
         // Note that suggestions.getWord(0) is the word user typed.
         for (int i = 1; i < suggestions.size(); i++) {
             if (TextUtils.equals(suggestions.getWord(i), expected))
@@ -128,21 +130,23 @@
     private void getBigramSuggestions(CharSequence previous, CharSequence typed) {
         if (!TextUtils.isEmpty(previous) && (typed.length() > 1)) {
             WordComposer firstChar = createWordComposer(Character.toString(typed.charAt(0)));
-            mSuggest.getSuggestions(null, firstChar, previous);
+            mSuggest.getSuggestions(null, firstChar, previous, mKeyboard.getProximityInfo());
         }
     }
 
     public CharSequence getBigramFirstSuggestion(CharSequence previous, CharSequence typed) {
         WordComposer word = createWordComposer(typed);
         getBigramSuggestions(previous, typed);
-        SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous);
+        SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous,
+                mKeyboard.getProximityInfo());
         return suggestions.size() > 1 ? suggestions.getWord(1) : null;
     }
 
     public CharSequence getBigramAutoCorrection(CharSequence previous, CharSequence typed) {
         WordComposer word = createWordComposer(typed);
         getBigramSuggestions(previous, typed);
-        SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous);
+        SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous,
+                mKeyboard.getProximityInfo());
         return (suggestions.size() > 1 && mSuggest.hasAutoCorrection())
                 ? suggestions.getWord(1) : null;
     }
@@ -151,7 +155,8 @@
             CharSequence expected) {
         WordComposer word = createWordComposer(typed);
         getBigramSuggestions(previous, typed);
-        SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous);
+        SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous,
+                mKeyboard.getProximityInfo());
         for (int i = 1; i < suggestions.size(); i++) {
             if (TextUtils.equals(suggestions.getWord(i), expected))
                 return i;
diff --git a/tests/src/com/android/inputmethod/latin/SuggestPerformanceTests.java b/tests/src/com/android/inputmethod/latin/SuggestPerformanceTests.java
index 99bcc61..7af566b 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestPerformanceTests.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestPerformanceTests.java
@@ -18,6 +18,7 @@
 import com.android.inputmethod.latin.tests.R;
 
 import android.content.res.AssetFileDescriptor;
+import android.content.res.Configuration;
 import android.text.TextUtils;
 import android.util.Slog;
 
@@ -38,7 +39,7 @@
         final AssetFileDescriptor dict = openTestRawResourceFd(R.raw.test);
         mHelper = new SuggestHelper(
                 getContext(), mTestPackageFile, dict.getStartOffset(), dict.getLength(),
-                createKeyboardId(Locale.US));
+                createKeyboardId(Locale.US, Configuration.ORIENTATION_PORTRAIT));
         loadString(R.raw.testtext);
     }
 
diff --git a/tests/src/com/android/inputmethod/latin/SuggestTests.java b/tests/src/com/android/inputmethod/latin/SuggestTests.java
index 6e9a127..cf45580 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestTests.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestTests.java
@@ -19,6 +19,7 @@
 import com.android.inputmethod.latin.tests.R;
 
 import android.content.res.AssetFileDescriptor;
+import android.content.res.Configuration;
 
 import java.util.Locale;
 
@@ -31,7 +32,7 @@
         final AssetFileDescriptor dict = openTestRawResourceFd(R.raw.test);
         mHelper = new SuggestHelper(
                 getContext(), mTestPackageFile, dict.getStartOffset(), dict.getLength(),
-                createKeyboardId(Locale.US));
+                createKeyboardId(Locale.US, Configuration.ORIENTATION_PORTRAIT));
         mHelper.setCorrectionMode(Suggest.CORRECTION_FULL_BIGRAM);
     }
 
diff --git a/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java b/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java
index 8aadee4..058a3e7 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java
@@ -20,6 +20,7 @@
 import android.content.res.Configuration;
 import android.test.AndroidTestCase;
 import android.text.TextUtils;
+import android.util.DisplayMetrics;
 import android.view.inputmethod.EditorInfo;
 
 import com.android.inputmethod.keyboard.KeyboardId;
@@ -37,12 +38,22 @@
         mTestPackageFile = new File(getTestContext().getApplicationInfo().sourceDir);
     }
 
-    protected KeyboardId createKeyboardId(Locale locale) {
-        final int displayWidth = getContext().getResources().getDisplayMetrics().widthPixels;
+    protected KeyboardId createKeyboardId(Locale locale, int orientation) {
+        final DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
+        final int width;
+        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            width = Math.max(dm.widthPixels, dm.heightPixels);
+        } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+            width = Math.min(dm.widthPixels, dm.heightPixels);
+        } else {
+            fail("Orientation should be ORIENTATION_LANDSCAPE or ORIENTATION_PORTRAIT: "
+                    + "orientation=" + orientation);
+            return null;
+        }
         return new KeyboardId(locale.toString() + " keyboard",
-                com.android.inputmethod.latin.R.xml.kbd_qwerty, locale,
-                Configuration.ORIENTATION_LANDSCAPE, displayWidth, KeyboardId.MODE_TEXT,
-                new EditorInfo(), false, KeyboardId.F2KEY_MODE_NONE, false, false, false, false);
+                com.android.inputmethod.latin.R.xml.kbd_qwerty, locale, orientation, width,
+                KeyboardId.MODE_TEXT, new EditorInfo(), false, KeyboardId.F2KEY_MODE_NONE,
+                false, false, false);
     }
 
     protected InputStream openTestRawResource(int resIdInTest) {
diff --git a/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java b/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java
index 46e5a24..a7d532e 100644
--- a/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java
+++ b/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java
@@ -58,7 +58,7 @@
         flushUserBigrams();
         if (!TextUtils.isEmpty(previous) && !TextUtils.isEmpty(Character.toString(typed))) {
             WordComposer firstChar = createWordComposer(Character.toString(typed));
-            mSuggest.getSuggestions(null, firstChar, previous);
+            mSuggest.getSuggestions(null, firstChar, previous, mKeyboard.getProximityInfo());
             boolean reloading = mUserBigram.reloadDictionaryIfRequired();
             if (reloading) mUserBigram.waitForDictionaryLoading();
             mUserBigram.getBigrams(firstChar, previous, mSuggest);
diff --git a/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java b/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java
index 9bd8538..0b97e41 100644
--- a/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java
+++ b/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java
@@ -15,9 +15,10 @@
  */
 
 package com.android.inputmethod.latin;
-import com.android.inputmethod.latin.tests.R;
-
 import android.content.res.AssetFileDescriptor;
+import android.content.res.Configuration;
+
+import com.android.inputmethod.latin.tests.R;
 
 import java.util.Locale;
 
@@ -34,7 +35,8 @@
         final AssetFileDescriptor dict = openTestRawResourceFd(R.raw.test);
         mHelper = new UserBigramSuggestHelper(
                 getContext(), mTestPackageFile, dict.getStartOffset(), dict.getLength(),
-                MAX_DATA, DELETE_DATA, createKeyboardId(Locale.US));
+                MAX_DATA, DELETE_DATA,
+                createKeyboardId(Locale.US, Configuration.ORIENTATION_PORTRAIT));
     }
 
     /************************** Tests ************************/