Merge "Write forgetting curve flag."
diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml
index 20597fb..8d8fedd 100644
--- a/java/res/values-fr-rCA/strings.xml
+++ b/java/res/values-fr-rCA/strings.xml
@@ -34,7 +34,7 @@
     <string name="advanced_settings" msgid="362895144495591463">"Paramètres avancés"</string>
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Options destinées aux experts"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Autres modes de saisie"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La touche de sélection de langue couvre d\'autres modes de saisie."</string>
+    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La touche de sélection de langue couvre d\'autres modes de saisie"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Touche de sélection de langue"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Afficher lorsque plusieurs langues de saisie sont activées"</string>
     <string name="sliding_key_input_preview" msgid="6604262359510068370">"Aff. indicateur saisie gestuelle"</string>
@@ -47,7 +47,7 @@
     <string name="use_contacts_dict" msgid="4435317977804180815">"Proposer noms de contacts"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utiliser des noms de contacts pour les suggestions et corrections"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Point et espace"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"Appuyez deux fois sur la barre d\'espace pour insérer un point et un espace."</string>
+    <string name="use_double_space_period_summary" msgid="6532892187247952799">"Appuyez deux fois sur la barre d\'espace pour insérer un point et une espace"</string>
     <string name="auto_cap" msgid="1719746674854628252">"Majuscules automatiques"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"Majuscule au premier mot de chaque phrase"</string>
     <string name="edit_personal_dictionary" msgid="3996910038952940420">"Dictionnaire personnel"</string>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 13bdc11..dfe9a55 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -50,8 +50,8 @@
     <string name="use_double_space_period_summary" msgid="6532892187247952799">"स्पेसबार पर डबल टैप करने से पीरियड शामिल हो जाता है जिसके बाद एक रिक्ति होती है"</string>
     <string name="auto_cap" msgid="1719746674854628252">"स्‍वत: अक्षर बड़े करना"</string>
     <string name="auto_cap_summary" msgid="7934452761022946874">"प्रत्येक वाक्य के पहले शब्द को बड़ा लिखें"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"व्यक्तिगत डिक्शनरी"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"एड-ऑन डिक्शनरी"</string>
+    <string name="edit_personal_dictionary" msgid="3996910038952940420">"व्यक्तिगत शब्दकोश"</string>
+    <string name="configure_dictionaries_title" msgid="4238652338556902049">"एड-ऑन शब्दकोश"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"मुख्‍य डिक्‍शनरी"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"सुधार सुझाव दिखाएं"</string>
     <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"लिखते समय सुझाए गए शब्‍द दिखाएं"</string>
@@ -167,7 +167,7 @@
     <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"कुंजी को देर तक दबाने का विलंब"</string>
     <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"कुंजी-स्पर्श कंपन अवधि"</string>
     <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"कुंजी-स्पर्श ध्वनि वॉल्यूम"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"बाहरी डिक्शनरी फ़ाइल पढ़ें"</string>
+    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"बाहरी शब्दकोश फ़ाइल पढ़ें"</string>
     <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"डाउनलोड फ़ोल्डर में कोई शब्दकोश फ़ाइल नहीं है"</string>
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"इंस्टॉल करने के लिए कोई शब्दकोश फ़ाइल चुनें"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"<xliff:g id="LOCALE_NAME">%s</xliff:g> के लिए वास्तव में यह फ़ाइल इंस्टॉल करें?"</string>
@@ -179,8 +179,8 @@
     <string name="setup_next_action" msgid="371821437915144603">"अगला चरण"</string>
     <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> सेट करना"</string>
     <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> को सक्षम करें"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"कृपया अपनी भाषा और इनपुट सेटिंग में \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" को चेक करें. इससे वह आपके उपकरण पर चलने के लिए अधिकृत हो जाएगा."</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> आपकी भाषा और इनपुट सेटिंग में पहले से सक्षम है, इसलिए यह चरण पूर्ण हो गया है. अगले चरण पर जाएं!"</string>
+    <string name="setup_step1_instruction" msgid="2578631936624637241">"कृपया अपनी भाषा और अक्षर सेटिंग में \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" को चेक करें. इससे वह आपके उपकरण पर चलने के लिए अधिकृत हो जाएगा."</string>
+    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> आपकी भाषा और अक्षर सेटिंग में पहले से सक्षम है, इसलिए यह चरण पूर्ण हो गया है. अगले चरण पर जाएं!"</string>
     <string name="setup_step1_action" msgid="4366513534999901728">"सेटिंग में सक्षम करें"</string>
     <string name="setup_step2_title" msgid="6860725447906690594">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> पर स्विच करें"</string>
     <string name="setup_step2_instruction" msgid="9141481964870023336">"इसके बाद, \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" को अपनी सक्रिय पाठ-इनपुट पद्धति के रूप में चुनें."</string>
@@ -195,16 +195,16 @@
     <string name="dictionary_provider_name" msgid="3027315045397363079">"डिक्‍शनरी प्रदाता"</string>
     <string name="dictionary_service_name" msgid="6237472350693511448">"डिक्‍शनरी सेवा"</string>
     <string name="download_description" msgid="6014835283119198591">"डिक्‍शनरी अपडेट जानकारी"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"एड-ऑन डिक्शनरी"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"डिक्शनरी उपलब्‍ध"</string>
+    <string name="dictionary_settings_title" msgid="8091417676045693313">"एड-ऑन शब्दकोश"</string>
+    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"शब्दकोश उपलब्‍ध"</string>
     <string name="dictionary_settings_summary" msgid="5305694987799824349">"डिक्‍शनरी की सेटिंग"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"उपयोगकर्ता डिक्शनरी"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"उपयोगकर्ता डिक्शनरी"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"डिक्शनरी उपलब्‍ध"</string>
+    <string name="user_dictionaries" msgid="3582332055892252845">"उपयोगकर्ता शब्दकोश"</string>
+    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"उपयोगकर्ता शब्दकोश"</string>
+    <string name="dictionary_available" msgid="4728975345815214218">"शब्दकोश उपलब्‍ध"</string>
     <string name="dictionary_downloading" msgid="2982650524622620983">"वर्तमान में डाउनलोड हो रही है"</string>
     <string name="dictionary_installed" msgid="8081558343559342962">"इंस्‍टॉल है"</string>
     <string name="dictionary_disabled" msgid="8950383219564621762">"इंस्‍टॉल है, अक्षम है"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"डिक्शनरी सेवा से कनेक्ट करने में समस्या"</string>
+    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"शब्दकोश सेवा से कनेक्ट करने में समस्या"</string>
     <string name="no_dictionaries_available" msgid="8039920716566132611">"डिक्‍शनरी अनुपलब्‍ध"</string>
     <string name="check_for_updates_now" msgid="8087688440916388581">"रीफ़्रेश करें"</string>
     <string name="last_update" msgid="730467549913588780">"अंतिम अपडेट"</string>
@@ -215,7 +215,7 @@
     <string name="install_dict" msgid="180852772562189365">"इंस्टॉल करें"</string>
     <string name="cancel_download_dict" msgid="7843340278507019303">"रद्द करें"</string>
     <string name="delete_dict" msgid="756853268088330054">"हटाएं"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"आपके मोबाइल उपकरण पर चयनित भाषा में डिक्‍शनरी उपलब्‍ध है.&lt;br/&gt; आपके लेखन अनुभव को बेहतर बनाने के लिए हम <xliff:g id="LANGUAGE">%1$s</xliff:g> डिक्‍शनरी को &lt;b&gt;डाउनलोड करने&lt;/b&gt; की अनुशंसा करते हैं.&lt;br/&gt; &lt;br/&gt; 3G पर डाउनलोड होने में एक या दो मिनट लग सकते हैं. यदि आपके पास &lt;b&gt;असीमित डेटा प्लान&lt;/b&gt; नहीं है, तो शुल्‍क लग सकते हैं.&lt;br/&gt; यदि आप अपने डेटा प्लान के बारे में सुनिश्चित नहीं हैं, तो हम अपने आप डाउनलोड प्रारंभ करने के लिए Wi-Fi कनेक्‍शन ढूंढने की अनुशंसा करते हैं.&lt;br/&gt; &lt;br/&gt; युक्ति: आप अपने मोबाइल उपकरण पर &lt;b&gt;सेटिंग&lt;/b&gt; मेनू में &lt;b&gt;भाषा और इनपुट&lt;/b&gt; पर जाकर डिक्‍शनरी डाउनलोड कर सकते हैं और निकाल सकते हैं."</string>
+    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"आपके मोबाइल उपकरण पर चयनित भाषा में डिक्‍शनरी उपलब्‍ध है.&lt;br/&gt; आपके लेखन अनुभव को बेहतर बनाने के लिए हम <xliff:g id="LANGUAGE">%1$s</xliff:g> डिक्‍शनरी को &lt;b&gt;डाउनलोड करने&lt;/b&gt; की अनुशंसा करते हैं.&lt;br/&gt; &lt;br/&gt; 3G पर डाउनलोड होने में एक या दो मिनट लग सकते हैं. यदि आपके पास &lt;b&gt;असीमित डेटा प्लान&lt;/b&gt; नहीं है, तो शुल्‍क लग सकते हैं.&lt;br/&gt; यदि आप अपने डेटा प्लान के बारे में सुनिश्चित नहीं हैं, तो हम अपने आप डाउनलोड प्रारंभ करने के लिए Wi-Fi कनेक्‍शन ढूंढने की अनुशंसा करते हैं.&lt;br/&gt; &lt;br/&gt; युक्ति: आप अपने मोबाइल उपकरण पर &lt;b&gt;सेटिंग&lt;/b&gt; मेनू में &lt;b&gt;भाषा और अक्षर&lt;/b&gt; पर जाकर डिक्‍शनरी डाउनलोड कर सकते हैं और निकाल सकते हैं."</string>
     <string name="download_over_metered" msgid="1643065851159409546">"अभी डाउनलोड करें (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>MB)"</string>
     <string name="do_not_download_over_metered" msgid="2176209579313941583">"Wi-Fi से डाउनलोड करें"</string>
     <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g> के लिए डिक्‍शनरी उपलब्‍ध है"</string>
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 599af12..7de93e6 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -122,4 +122,6 @@
     <dimen name="accessibility_edge_slop">8dp</dimen>
 
     <integer name="user_dictionary_max_word_length" translatable="false">48</integer>
+
+    <dimen name="language_on_spacebar_horizontal_margin">1dp</dimen>
 </resources>
diff --git a/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml b/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml
deleted file mode 100644
index dd545b5..0000000
--- a/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_10_10_7_symbols" />
-</Keyboard>
diff --git a/java/res/xml-sw600dp/kbd_10_10_7_symbols_shift.xml b/java/res/xml-sw600dp/kbd_10_10_7_symbols_shift.xml
deleted file mode 100644
index c36f009..0000000
--- a/java/res/xml-sw600dp/kbd_10_10_7_symbols_shift.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_10_10_7_symbols_shift" />
-</Keyboard>
diff --git a/java/res/xml-sw600dp/key_space.xml b/java/res/xml-sw600dp/key_space_5kw.xml
similarity index 100%
rename from java/res/xml-sw600dp/key_space.xml
rename to java/res/xml-sw600dp/key_space_5kw.xml
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml-sw600dp/key_space_symbols.xml
similarity index 82%
copy from java/res/xml/kbd_10_10_7_symbols.xml
copy to java/res/xml-sw600dp/key_space_symbols.xml
index 4d9861b..07aa7d1 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml-sw600dp/key_space_symbols.xml
@@ -18,10 +18,9 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
 >
     <include
-        latin:keyboardLayout="@xml/rows_symbols" />
-</Keyboard>
+        latin:keyboardLayout="@xml/key_space_5kw" />
+</merge>
diff --git a/java/res/xml-sw600dp/row_dvorak4.xml b/java/res/xml-sw600dp/row_dvorak4.xml
index 1dfb3f4..11b4034 100644
--- a/java/res/xml-sw600dp/row_dvorak4.xml
+++ b/java/res/xml-sw600dp/row_dvorak4.xml
@@ -34,7 +34,7 @@
             latin:keyboardLayout="@xml/key_f1" />
         <include
             latin:keyXPos="28.0%p"
-            latin:keyboardLayout="@xml/key_space"
+            latin:keyboardLayout="@xml/key_space_5kw"
             latin:backgroundType="normal" />
         <include
             latin:keyboardLayout="@xml/key_question_exclamation" />
diff --git a/java/res/xml-sw600dp/row_qwerty4.xml b/java/res/xml-sw600dp/row_qwerty4.xml
index 16d3731..7969dd8 100644
--- a/java/res/xml-sw600dp/row_qwerty4.xml
+++ b/java/res/xml-sw600dp/row_qwerty4.xml
@@ -34,7 +34,7 @@
             latin:keyboardLayout="@xml/key_f1" />
         <include
             latin:keyXPos="28.0%p"
-            latin:keyboardLayout="@xml/key_space"
+            latin:keyboardLayout="@xml/key_space_5kw"
             latin:backgroundType="normal" />
         <include
             latin:keyboardLayout="@xml/keys_comma_period" />
diff --git a/java/res/xml-sw600dp/row_symbols4.xml b/java/res/xml-sw600dp/row_symbols4.xml
deleted file mode 100644
index f138d8e..0000000
--- a/java/res/xml-sw600dp/row_symbols4.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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"
->
-    <Row
-        latin:keyWidth="9.0%p"
-        latin:backgroundType="functional"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyWidth="10.0%p" />
-        <Key
-            latin:keyLabel="/" />
-        <include
-            latin:keyboardLayout="@xml/key_f1" />
-        <include
-            latin:keyXPos="28.0%p"
-            latin:keyboardLayout="@xml/key_space"
-            latin:backgroundType="normal" />
-        <Key
-            latin:keyLabel="&quot;"
-            latin:moreKeys="!text/more_keys_for_tablet_double_quote" />
-        <Key
-            latin:keyLabel="_" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer
-            latin:keyWidth="fillRight" />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw600dp/row_symbols_shift4.xml b/java/res/xml-sw600dp/row_symbols_shift4.xml
deleted file mode 100644
index 29befa9..0000000
--- a/java/res/xml-sw600dp/row_symbols_shift4.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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"
->
-    <Row
-        latin:keyWidth="9.0%p"
-        latin:backgroundType="functional"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyWidth="10.0%p" />
-        <!-- Here is empty space. -->
-        <include
-            latin:keyXPos="28.0%p"
-            latin:keyboardLayout="@xml/key_space"
-            latin:backgroundType="normal" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer
-            latin:keyWidth="fillRight" />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_symbols2.xml b/java/res/xml-sw600dp/rowkeys_symbols2.xml
deleted file mode 100644
index 14abb42..0000000
--- a/java/res/xml-sw600dp/rowkeys_symbols2.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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:languageCode="fa"
-        >
-            <!-- U+066C: "٬" ARABIC THOUSANDS SEPARATOR -->
-            <Key
-                latin:keyLabel="&#x066C;"
-                latin:keyHintLabel="&amp;"
-                latin:keyLabelFlags="hasPopupHint|hasShiftedLetterHint"
-                latin:moreKeys="&amp;" />
-        </case>
-        <default>
-            <Key
-                latin:keyLabel="\#" />
-        </default>
-    </switch>
-    <Key
-        latin:keyStyle="currencyKeyStyle" />
-    <Key
-        latin:keyLabel="!text/keylabel_for_symbols_percent"
-        latin:moreKeys="!text/more_keys_for_symbols_percent" />
-    <switch>
-        <case
-            latin:languageCode="fa"
-        >
-            <!-- U+066B: "٫" ARABIC DECIMAL SEPARATOR -->
-            <Key
-                latin:keyLabel="&#x066B;"
-                latin:keyHintLabel="\#"
-                latin:keyLabelFlags="hasPopupHint|hasShiftedLetterHint"
-                latin:moreKeys="\#" />
-        </case>
-        <default>
-            <Key
-                latin:keyLabel="&amp;" />
-        </default>
-    </switch>
-    <Key
-        latin:keyLabel="*"
-        latin:moreKeys="!text/more_keys_for_star" />
-    <!-- U+2013: "–" EN DASH
-         U+2014: "—" EM DASH
-         U+00B7: "·" MIDDLE DOT -->
-    <Key
-        latin:keyLabel="-"
-        latin:moreKeys="_,&#x2013;,&#x2014;,&#x00B7;" />
-    <Key
-        latin:keyLabel="+"
-        latin:moreKeys="!text/more_keys_for_plus" />
-    <include
-        latin:keyboardLayout="@xml/keys_parentheses" />
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_symbols3.xml b/java/res/xml-sw600dp/rowkeys_symbols3.xml
deleted file mode 100644
index 30fba38..0000000
--- a/java/res/xml-sw600dp/rowkeys_symbols3.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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/keys_less_greater" />
-    <!-- U+2260: "≠" NOT EQUAL TO
-         U+2248: "≈" ALMOST EQUAL TO -->
-    <Key
-        latin:keyLabel="="
-        latin:moreKeys="&#x2260;,&#x2248;" />
-    <switch>
-        <case
-            latin:mode="url"
-        >
-            <Key
-                latin:keyLabel="\'" />
-        </case>
-        <default>
-            <Key
-                latin:keyLabel=":" />
-        </default>
-    </switch>
-    <Key
-        latin:keyLabel="!text/keylabel_for_symbols_semicolon"
-        latin:moreKeys="!text/more_keys_for_symbols_semicolon" />
-    <Key
-        latin:keyLabel="!text/keylabel_for_comma"
-        latin:moreKeys="!text/more_keys_for_comma" />
-    <Key
-        latin:keyLabel="." />
-    <Key
-        latin:keyLabel="!"
-        latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
-    <Key
-        latin:keyLabel="!text/keylabel_for_symbols_question"
-        latin:moreKeys="!text/more_keys_for_symbols_question" />
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_symbols_shift1.xml b/java/res/xml-sw600dp/rowkeys_symbols_shift1.xml
deleted file mode 100644
index 3549fdd..0000000
--- a/java/res/xml-sw600dp/rowkeys_symbols_shift1.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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"
->
-    <Key
-        latin:keyLabel="~" />
-    <Key
-        latin:keyLabel="`" />
-    <Key
-        latin:keyLabel="|" />
-    <!-- U+2022: "•" BULLET -->
-    <Key
-        latin:keyLabel="&#x2022;"
-        latin:moreKeys="!text/more_keys_for_bullet" />
-    <!-- U+221A: "√" SQUARE ROOT -->
-    <Key
-        latin:keyLabel="&#x221A;" />
-    <!-- U+03C0: "π" GREEK SMALL LETTER PI
-         U+03A0: "Π" GREEK CAPITAL LETTER PI -->
-    <Key
-        latin:keyLabel="&#x03C0;"
-        latin:moreKeys="&#x03A0;" />
-    <!-- U+00F7: "÷" DIVISION SIGN -->
-    <Key
-        latin:keyLabel="&#x00F7;" />
-    <!-- U+00D7: "×" MULTIPLICATION SIGN -->
-    <Key
-        latin:keyLabel="&#x00D7;" />
-    <!-- U+00A7: "§" SECTION SIGN
-         U+00B6: "¶" PILCROW SIGN -->
-    <Key
-        latin:keyLabel="&#x00A7;"
-        latin:moreKeys="&#x00B6;" />
-    <!-- U+0394: "Δ" GREEK CAPITAL LETTER DELTA -->
-    <Key
-        latin:keyLabel="&#x0394;" />
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_symbols_shift2.xml b/java/res/xml-sw600dp/rowkeys_symbols_shift2.xml
deleted file mode 100644
index 2048b73..0000000
--- a/java/res/xml-sw600dp/rowkeys_symbols_shift2.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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"
->
-    <Key
-        latin:keyStyle="moreCurrency1KeyStyle" />
-    <Key
-        latin:keyStyle="moreCurrency2KeyStyle" />
-    <Key
-        latin:keyStyle="moreCurrency3KeyStyle" />
-    <Key
-        latin:keyStyle="moreCurrency4KeyStyle" />
-    <!-- U+2191: "↑" UPWARDS ARROW
-         U+2193: "↓" DOWNWARDS ARROW
-         U+2190: "←" LEFTWARDS ARROW
-         U+2192: "→" RIGHTWARDS ARROW -->
-    <Key
-        latin:keyLabel="^"
-        latin:moreKeys="&#x2191;,&#x2193;,&#x2190;,&#x2192;" />
-    <!-- U+00B0: "°" DEGREE SIGN
-         U+2032: "′" PRIME
-         U+2033: "″" DOUBLE PRIME -->
-    <Key
-        latin:keyLabel="&#x00B0;"
-        latin:moreKeys="&#x2032;,&#x2033;" />
-    <!-- U+00B1: "±" PLUS-MINUS SIGN
-         U+221E: "∞" INFINITY -->
-    <Key
-        latin:keyLabel="&#x00B1;"
-        latin:moreKeys="&#x221E;" />
-    <include
-        latin:keyboardLayout="@xml/keys_curly_brackets" />
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_symbols_shift3.xml b/java/res/xml-sw600dp/rowkeys_symbols_shift3.xml
deleted file mode 100644
index 8bd8656..0000000
--- a/java/res/xml-sw600dp/rowkeys_symbols_shift3.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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"
->
-    <Key
-        latin:keyLabel="\\" />
-    <!-- U+00A9: "©" COPYRIGHT SIGN -->
-    <Key
-        latin:keyLabel="&#x00A9;" />
-    <!-- U+00AE: "®" REGISTERED SIGN -->
-    <Key
-        latin:keyLabel="&#x00AE;" />
-    <!-- U+2122: "™" TRADE MARK SIGN -->
-    <Key
-        latin:keyLabel="&#x2122;" />
-    <!-- U+2105: "℅" CARE OF -->
-    <Key
-        latin:keyLabel="&#x2105;" />
-    <include
-        latin:keyboardLayout="@xml/keys_square_brackets" />
-    <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK -->
-    <Key
-        latin:keyLabel="&#x00A1;" />
-    <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
-    <Key
-        latin:keyLabel="&#x00BF;" />
-</merge>
diff --git a/java/res/xml-sw600dp/rows_10_10_7_symbols.xml b/java/res/xml-sw600dp/rows_10_10_7_symbols.xml
deleted file mode 100644
index 0e4710c..0000000
--- a/java/res/xml-sw600dp/rows_10_10_7_symbols.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
-    <Row
-        latin:keyWidth="9.0%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="9.0%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="9.0%p"
-    >
-        <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
-            latin:keyWidth="10.0%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols3" />
-        <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols4" />
-</merge>
diff --git a/java/res/xml-sw600dp/rows_10_10_7_symbols_shift.xml b/java/res/xml-sw600dp/rows_10_10_7_symbols_shift.xml
deleted file mode 100644
index 3d3b59f..0000000
--- a/java/res/xml-sw600dp/rows_10_10_7_symbols_shift.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
-    <Row
-        latin:keyWidth="9.0%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="9.0%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="9.0%p"
-    >
-        <Key
-            latin:keyStyle="backFromMoreSymbolKeyStyle"
-            latin:keyWidth="10.0%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift3" />
-        <Key
-            latin:keyStyle="backFromMoreSymbolKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols_shift4" />
-</merge>
diff --git a/java/res/xml-sw600dp/rows_symbols.xml b/java/res/xml-sw600dp/rows_symbols.xml
index 3d0593d..fbd8492 100644
--- a/java/res/xml-sw600dp/rows_symbols.xml
+++ b/java/res/xml-sw600dp/rows_symbols.xml
@@ -50,12 +50,23 @@
         <Key
             latin:keyStyle="toMoreSymbolKeyStyle"
             latin:keyWidth="10.0%p" />
+        <Key
+            latin:keyLabel="\\" />
+        <Key
+            latin:keyLabel="=" />
         <include
             latin:keyboardLayout="@xml/rowkeys_symbols3" />
         <Key
             latin:keyStyle="toMoreSymbolKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols4" />
+    <Row
+        latin:keyWidth="9.0%p"
+    >
+        <Key
+            latin:keyStyle="toAlphaKeyStyle"
+            latin:keyWidth="10.0%p" />
+        <include
+            latin:keyboardLayout="@xml/row_symbols4" />
+    </Row>
 </merge>
diff --git a/java/res/xml-sw600dp/rows_symbols_shift.xml b/java/res/xml-sw600dp/rows_symbols_shift.xml
index 0050c0c..aad047f 100644
--- a/java/res/xml-sw600dp/rows_symbols_shift.xml
+++ b/java/res/xml-sw600dp/rows_symbols_shift.xml
@@ -52,10 +52,23 @@
             latin:keyWidth="10.0%p" />
         <include
             latin:keyboardLayout="@xml/rowkeys_symbols_shift3" />
+        <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK -->
+        <Key
+            latin:keyLabel="&#x00A1;" />
+        <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
+        <Key
+            latin:keyLabel="&#x00BF;" />
         <Key
             latin:keyStyle="backFromMoreSymbolKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols_shift4" />
+    <Row
+        latin:keyWidth="9.0%p"
+    >
+        <Key
+            latin:keyStyle="toAlphaKeyStyle"
+            latin:keyWidth="10%p" />
+        <include
+            latin:keyboardLayout="@xml/row_symbols_shift4" />
+    </Row>
 </merge>
diff --git a/java/res/xml/kbd_10_10_7_symbols_shift.xml b/java/res/xml/kbd_10_10_7_symbols_shift.xml
deleted file mode 100644
index a2d67ca..0000000
--- a/java/res/xml/kbd_10_10_7_symbols_shift.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_symbols_shift" />
-</Keyboard>
diff --git a/java/res/xml/key_space_3kw.xml b/java/res/xml/key_space_3kw.xml
new file mode 100644
index 0000000..20ec882
--- /dev/null
+++ b/java/res/xml/key_space_3kw.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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:languageSwitchKeyEnabled="true"
+        >
+            <Key
+                latin:keyStyle="languageSwitchKeyStyle" />
+            <Key
+                latin:keyStyle="spaceKeyStyle"
+                latin:keyWidth="20%p" />
+        </case>
+        <!-- languageSwitchKeyEnabled="false" -->
+        <default>
+            <Key
+                latin:keyStyle="spaceKeyStyle"
+                latin:keyWidth="30%p" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/key_space.xml b/java/res/xml/key_space_5kw.xml
similarity index 100%
rename from java/res/xml/key_space.xml
rename to java/res/xml/key_space_5kw.xml
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml/key_space_symbols.xml
similarity index 82%
rename from java/res/xml/kbd_10_10_7_symbols.xml
rename to java/res/xml/key_space_symbols.xml
index 4d9861b..1efc4ff 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/key_space_symbols.xml
@@ -18,10 +18,9 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
 >
     <include
-        latin:keyboardLayout="@xml/rows_symbols" />
-</Keyboard>
+        latin:keyboardLayout="@xml/key_space_3kw" />
+</merge>
diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml
index 07436a4..6b3dc9a 100644
--- a/java/res/xml/key_styles_common.xml
+++ b/java/res/xml/key_styles_common.xml
@@ -21,6 +21,20 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLockShifted"
+        >
+            <key-style
+                latin:styleName="hasShiftedLetterHintStyle"
+                latin:keyLabelFlags="hasShiftedLetterHint|shiftedLetterActivated" />
+        </case>
+        <default>
+            <key-style
+                latin:styleName="hasShiftedLetterHintStyle"
+                latin:keyLabelFlags="hasShiftedLetterHint" />
+        </default>
+    </switch>
     <!-- Base key style for the key which may have settings or tab key as popup key. -->
     <include
         latin:keyboardLayout="@xml/key_styles_f1" />
diff --git a/java/res/xml/keyboard_layout_set_azerty.xml b/java/res/xml/keyboard_layout_set_azerty.xml
index 4d144ed..38797f9 100644
--- a/java/res/xml/keyboard_layout_set_azerty.xml
+++ b/java/res/xml/keyboard_layout_set_azerty.xml
@@ -26,10 +26,10 @@
         latin:enableProximityCharsCorrection="true" />
     <Element
         latin:elementName="symbols"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols" />
+        latin:elementKeyboard="@xml/kbd_symbols" />
     <Element
         latin:elementName="symbolsShifted"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols_shift" />
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
     <Element
         latin:elementName="phone"
         latin:elementKeyboard="@xml/kbd_phone" />
diff --git a/java/res/xml/keyboard_layout_set_colemak.xml b/java/res/xml/keyboard_layout_set_colemak.xml
index c18f132..3061872 100644
--- a/java/res/xml/keyboard_layout_set_colemak.xml
+++ b/java/res/xml/keyboard_layout_set_colemak.xml
@@ -26,10 +26,10 @@
         latin:enableProximityCharsCorrection="true" />
     <Element
         latin:elementName="symbols"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols" />
+        latin:elementKeyboard="@xml/kbd_symbols" />
     <Element
         latin:elementName="symbolsShifted"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols_shift" />
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
     <Element
         latin:elementName="phone"
         latin:elementKeyboard="@xml/kbd_phone" />
diff --git a/java/res/xml/keyboard_layout_set_dvorak.xml b/java/res/xml/keyboard_layout_set_dvorak.xml
index eb8e0c5..31aeec5 100644
--- a/java/res/xml/keyboard_layout_set_dvorak.xml
+++ b/java/res/xml/keyboard_layout_set_dvorak.xml
@@ -26,10 +26,10 @@
         latin:enableProximityCharsCorrection="true" />
     <Element
         latin:elementName="symbols"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols" />
+        latin:elementKeyboard="@xml/kbd_symbols" />
     <Element
         latin:elementName="symbolsShifted"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols_shift" />
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
     <Element
         latin:elementName="phone"
         latin:elementKeyboard="@xml/kbd_phone" />
diff --git a/java/res/xml/keyboard_layout_set_hebrew.xml b/java/res/xml/keyboard_layout_set_hebrew.xml
index 212816d..d5b25b3 100644
--- a/java/res/xml/keyboard_layout_set_hebrew.xml
+++ b/java/res/xml/keyboard_layout_set_hebrew.xml
@@ -26,10 +26,10 @@
         latin:enableProximityCharsCorrection="true" />
     <Element
         latin:elementName="symbols"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols" />
+        latin:elementKeyboard="@xml/kbd_symbols" />
     <Element
         latin:elementName="symbolsShifted"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols_shift" />
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
     <Element
         latin:elementName="phone"
         latin:elementKeyboard="@xml/kbd_phone" />
diff --git a/java/res/xml/keyboard_layout_set_spanish.xml b/java/res/xml/keyboard_layout_set_spanish.xml
index 57cef52..c454de3 100644
--- a/java/res/xml/keyboard_layout_set_spanish.xml
+++ b/java/res/xml/keyboard_layout_set_spanish.xml
@@ -26,10 +26,10 @@
         latin:enableProximityCharsCorrection="true" />
     <Element
         latin:elementName="symbols"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols" />
+        latin:elementKeyboard="@xml/kbd_symbols" />
     <Element
         latin:elementName="symbolsShifted"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols_shift" />
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
     <Element
         latin:elementName="phone"
         latin:elementKeyboard="@xml/kbd_phone" />
diff --git a/java/res/xml-sw600dp/keys_comma_period.xml b/java/res/xml/keys_comma_period.xml
similarity index 88%
rename from java/res/xml-sw600dp/keys_comma_period.xml
rename to java/res/xml/keys_comma_period.xml
index a600839..7e7c728 100644
--- a/java/res/xml-sw600dp/keys_comma_period.xml
+++ b/java/res/xml/keys_comma_period.xml
@@ -29,11 +29,13 @@
                 latin:keyLabel="."
                 latin:keyHintLabel="_"
                 latin:moreKeys="_"
+                latin:backgroundType="functional"
                 latin:keyStyle="hasShiftedLetterHintStyle" />
             <Key
                 latin:keyLabel=","
                 latin:keyHintLabel="-"
                 latin:moreKeys="-"
+                latin:backgroundType="functional"
                 latin:keyStyle="hasShiftedLetterHintStyle" />
         </case>
         <case
@@ -43,12 +45,14 @@
                 latin:keyLabel="!text/keylabel_for_apostrophe"
                 latin:keyHintLabel="!text/keyhintlabel_for_apostrophe"
                 latin:moreKeys="!text/more_keys_for_apostrophe"
+                latin:backgroundType="functional"
                 latin:keyStyle="hasShiftedLetterHintStyle" />
             <Key
                 latin:keyLabel="."
                 latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics"
                 latin:keyLabelFlags="hasPopupHint"
                 latin:moreKeys="!text/more_keys_for_arabic_diacritics"
+                latin:backgroundType="functional"
                 latin:keyStyle="hasShiftedLetterHintStyle" />
         </case>
         <case
@@ -59,22 +63,26 @@
                 latin:keyHintLabel="!text/keyhintlabel_for_apostrophe"
                 latin:keyLabelFlags="hasPopupHint"
                 latin:moreKeys="!text/more_keys_for_apostrophe"
+                latin:backgroundType="functional"
                 latin:keyStyle="hasShiftedLetterHintStyle" />
             <Key
                 latin:keyLabel="."
                 latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics"
                 latin:keyLabelFlags="hasPopupHint"
                 latin:moreKeys="!text/more_keys_for_arabic_diacritics"
+                latin:backgroundType="functional"
                 latin:keyStyle="hasShiftedLetterHintStyle" />
         </case>
         <default>
             <Key
                 latin:keyLabel="."
                 latin:keyHintLabel="!text/keyhintlabel_for_tablet_period"
+                latin:backgroundType="functional"
                 latin:moreKeys="!text/more_keys_for_tablet_period" />
             <Key
                 latin:keyLabel="!text/keylabel_for_tablet_comma"
                 latin:keyHintLabel="!text/keyhintlabel_for_tablet_comma"
+                latin:backgroundType="functional"
                 latin:moreKeys="!text/more_keys_for_tablet_comma" />
         </default>
     </switch>
diff --git a/java/res/xml/keys_less_greater.xml b/java/res/xml/keys_less_greater.xml
index bc9ecdf..56d0727 100644
--- a/java/res/xml/keys_less_greater.xml
+++ b/java/res/xml/keys_less_greater.xml
@@ -30,20 +30,24 @@
             <Key
                 latin:keyLabel="&#x00AB;"
                 latin:code="0x00BB"
+                latin:backgroundType="functional"
                 latin:moreKeys="!text/more_keys_for_less_than" />
             <Key
                 latin:keyLabel="&#x00BB;"
                 latin:code="0x00AB"
+                latin:backgroundType="functional"
                 latin:moreKeys="!text/more_keys_for_greater_than" />
         </case>
         <default>
             <Key
                 latin:keyLabel="&lt;"
                 latin:code="!code/key_less_than"
+                latin:backgroundType="functional"
                 latin:moreKeys="!text/more_keys_for_less_than" />
             <Key
                 latin:keyLabel="&gt;"
                 latin:code="!code/key_greater_than"
+                latin:backgroundType="functional"
                 latin:moreKeys="!text/more_keys_for_greater_than" />
         </default>
     </switch>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 2a726c4..6c36b0e 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -42,13 +42,11 @@
             android:title="@string/popup_on_keypress"
             android:persistent="true"
             android:defaultValue="@bool/config_default_key_preview_popup" />
-        <ListPreference
-            android:key="voice_mode"
+        <CheckBoxPreference
+            android:key="pref_voice_input_key"
             android:title="@string/voice_input"
             android:persistent="true"
-            android:entryValues="@array/voice_input_modes_values"
-            android:entries="@array/voice_input_modes"
-            android:defaultValue="@string/voice_mode_main" />
+            android:defaultValue="true" />
     </PreferenceCategory>
     <PreferenceCategory
         android:title="@string/correction_category"
diff --git a/java/res/xml/row_dvorak4.xml b/java/res/xml/row_dvorak4.xml
index e6d487e..02a95ac 100644
--- a/java/res/xml/row_dvorak4.xml
+++ b/java/res/xml/row_dvorak4.xml
@@ -65,7 +65,7 @@
         </switch>
         <include
             latin:keyXPos="25%p"
-            latin:keyboardLayout="@xml/key_space" />
+            latin:keyboardLayout="@xml/key_space_5kw" />
         <Key
             latin:keyLabel="z"
             latin:keyLabelFlags="hasPopupHint"
diff --git a/java/res/xml/row_qwerty4.xml b/java/res/xml/row_qwerty4.xml
index e6a5074..340beb9 100644
--- a/java/res/xml/row_qwerty4.xml
+++ b/java/res/xml/row_qwerty4.xml
@@ -31,7 +31,7 @@
             latin:keyboardLayout="@xml/key_f1" />
         <include
             latin:keyXPos="25%p"
-            latin:keyboardLayout="@xml/key_space" />
+            latin:keyboardLayout="@xml/key_space_5kw" />
         <switch>
             <case
                 latin:languageCode="ar|fa"
diff --git a/java/res/xml/row_symbols4.xml b/java/res/xml/row_symbols4.xml
index 96e550c..0bf412f 100644
--- a/java/res/xml/row_symbols4.xml
+++ b/java/res/xml/row_symbols4.xml
@@ -18,38 +18,29 @@
 */
 -->
 
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="10%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyWidth="15%p" />
-        <switch>
-            <case
-                latin:hasShortcutKey="true"
-            >
-                <Key
-                    latin:keyStyle="shortcutKeyStyle" />
-            </case>
-            <!-- latin:hasShortcutKey="false" -->
-            <default>
-                <Key
-                    latin:keyLabel="!text/keylabel_for_comma"
-                    latin:keyLabelFlags="hasPopupHint"
-                    latin:additionalMoreKeys="!text/more_keys_for_comma"
-                    latin:keyStyle="f1MoreKeysStyle" />
-            </default>
-        </switch>
-        <include
-            latin:keyXPos="25%p"
-            latin:keyboardLayout="@xml/key_space" />
-        <Key
-            latin:keyStyle="punctuationKeyStyle" />
-        <Key
-            latin:keyStyle="emojiKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
+
+    <Key
+        latin:backgroundType="functional"
+        latin:keyLabel="_" />
+    <Key
+        latin:backgroundType="functional"
+        latin:keyLabel="/" />
+
+    <switch>
+        <case latin:hasShortcutKey="true" >
+            <Key latin:keyStyle="shortcutKeyStyle" />
+        </case>
+        <!-- latin:hasShortcutKey="false" -->
+        <default>
+        </default>
+    </switch>
+
+    <include latin:keyboardLayout="@xml/key_space_symbols" />
+    <include latin:keyboardLayout="@xml/keys_comma_period" />
+
+    <Key
+        latin:keyStyle="emojiKeyStyle"
+        latin:keyWidth="fillRight" />
+
 </merge>
diff --git a/java/res/xml/row_symbols_shift4.xml b/java/res/xml/row_symbols_shift4.xml
index 99a685c..57a2ec0 100644
--- a/java/res/xml/row_symbols_shift4.xml
+++ b/java/res/xml/row_symbols_shift4.xml
@@ -17,31 +17,15 @@
 ** limitations under the License.
 */
 -->
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
 
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="10%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyWidth="15%p" />
-        <!-- U+201A: "‚" SINGLE LOW-9 QUOTATION MARK
-             U+201E: "„" DOUBLE LOW-9 QUOTATION MARK -->
-        <Key
-            latin:keyLabel="&#x201E;"
-            latin:moreKeys="&#x201A;"
-            latin:backgroundType="functional" />
-        <include
-            latin:keyXPos="25%p"
-            latin:keyboardLayout="@xml/key_space" />
-        <!-- U+2026: "…" HORIZONTAL ELLIPSIS -->
-        <Key
-            latin:keyLabel="&#x2026;"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
+    <include latin:keyboardLayout="@xml/keys_less_greater" />
+    <include
+        latin:keyboardLayout="@xml/key_space_symbols" />
+    <include latin:keyboardLayout="@xml/keys_comma_period" />
+
+    <Key
+        latin:keyStyle="enterKeyStyle"
+        latin:keyWidth="fillRight" />
+
 </merge>
diff --git a/java/res/xml/rowkeys_symbols2.xml b/java/res/xml/rowkeys_symbols2.xml
index 3e27f15..76cbf62 100644
--- a/java/res/xml/rowkeys_symbols2.xml
+++ b/java/res/xml/rowkeys_symbols2.xml
@@ -50,9 +50,6 @@
         latin:moreKeys="!text/more_keys_for_symbols_percent" />
     <Key
         latin:keyLabel="&amp;" />
-    <Key
-        latin:keyLabel="*"
-        latin:moreKeys="!text/more_keys_for_star" />
     <!-- U+2013: "–" EN DASH
          U+2014: "—" EM DASH
          U+00B7: "·" MIDDLE DOT -->
diff --git a/java/res/xml/rowkeys_symbols3.xml b/java/res/xml/rowkeys_symbols3.xml
index 7722ca9..9f5e620 100644
--- a/java/res/xml/rowkeys_symbols3.xml
+++ b/java/res/xml/rowkeys_symbols3.xml
@@ -22,8 +22,8 @@
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <Key
-        latin:keyLabel="!"
-        latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
+        latin:keyLabel="*"
+        latin:moreKeys="!text/more_keys_for_star" />
     <switch>
         <case
             latin:languageCode="fa"
@@ -54,8 +54,9 @@
         latin:keyLabel="!text/keylabel_for_symbols_semicolon"
         latin:moreKeys="!text/more_keys_for_symbols_semicolon" />
     <Key
-        latin:keyLabel="/" />
-    <Key
         latin:keyLabel="!text/keylabel_for_symbols_question"
         latin:moreKeys="!text/more_keys_for_symbols_question" />
+    <Key
+        latin:keyLabel="!"
+        latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
 </merge>
diff --git a/java/res/xml/rowkeys_symbols_shift1.xml b/java/res/xml/rowkeys_symbols_shift1.xml
index fea8ae3..6013493 100644
--- a/java/res/xml/rowkeys_symbols_shift1.xml
+++ b/java/res/xml/rowkeys_symbols_shift1.xml
@@ -34,17 +34,23 @@
     <!-- U+221A: "√" SQUARE ROOT -->
     <Key
         latin:keyLabel="&#x221A;" />
-    <!-- U+03C0: "π" GREEK SMALL LETTER PI
-         U+03A0: "Π" GREEK CAPITAL LETTER PI  -->
+    <!-- U+03A0: "Π" GREEK CAPITAL LETTER PI
+         U+03C0: "π" GREEK SMALL LETTER PI  -->
     <Key
-        latin:keyLabel="&#x03C0;"
-        latin:moreKeys="&#x03A0;" />
+        latin:keyLabel="&#x03A0;"
+        latin:moreKeys="&#x03C0;" />
     <!-- U+00F7: "÷" DIVISION SIGN -->
     <Key
         latin:keyLabel="&#x00F7;" />
     <!-- U+00D7: "×" MULTIPLICATION SIGN -->
     <Key
         latin:keyLabel="&#x00D7;" />
-    <include
-        latin:keyboardLayout="@xml/keys_curly_brackets" />
+    <!-- U+00B6: "¶" PILCROW SIGN
+         U+00A7: "§" SECTION SIGN -->
+    <Key
+        latin:keyLabel="&#x00B6;"
+        latin:moreKeys="&#x00A7;" />
+    <!-- U+2206: "∆" INCREMENT -->
+    <Key
+        latin:keyLabel="&#x2206;" />
 </merge>
diff --git a/java/res/xml/rowkeys_symbols_shift2.xml b/java/res/xml/rowkeys_symbols_shift2.xml
index 3fd8aac..36f9214 100644
--- a/java/res/xml/rowkeys_symbols_shift2.xml
+++ b/java/res/xml/rowkeys_symbols_shift2.xml
@@ -22,19 +22,13 @@
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <Key
-        latin:keyStyle="nonSpecialBackgroundTabKeyStyle" />
-    <Key
         latin:keyStyle="moreCurrency1KeyStyle" />
     <Key
         latin:keyStyle="moreCurrency2KeyStyle" />
     <Key
         latin:keyStyle="moreCurrency3KeyStyle" />
-    <!-- U+00B0: "°" DEGREE SIGN
-         U+2032: "′" PRIME
-         U+2033: "″" DOUBLE PRIME -->
     <Key
-        latin:keyLabel="&#x00B0;"
-        latin:moreKeys="&#x2032;,&#x2033;" />
+        latin:keyStyle="moreCurrency4KeyStyle" />
     <!-- U+2191: "↑" UPWARDS ARROW
          U+2193: "↓" DOWNWARDS ARROW
          U+2190: "←" LEFTWARDS ARROW
@@ -42,8 +36,12 @@
     <Key
         latin:keyLabel="^"
         latin:moreKeys="&#x2191;,&#x2193;,&#x2190;,&#x2192;" />
+    <!-- U+00B0: "°" DEGREE SIGN
+         U+2032: "′" PRIME
+         U+2033: "″" DOUBLE PRIME -->
     <Key
-        latin:keyLabel="_" />
+        latin:keyLabel="&#x00B0;"
+        latin:moreKeys="&#x2032;,&#x2033;" />
     <!-- U+2260: "≠" NOT EQUAL TO
          U+2248: "≈" ALMOST EQUAL TO
          U+221E: "∞" INFINITY -->
@@ -51,5 +49,5 @@
         latin:keyLabel="="
         latin:moreKeys="&#x2260;,&#x2248;,&#x221E;" />
     <include
-        latin:keyboardLayout="@xml/keys_square_brackets" />
+        latin:keyboardLayout="@xml/keys_curly_brackets" />
 </merge>
diff --git a/java/res/xml/rowkeys_symbols_shift3.xml b/java/res/xml/rowkeys_symbols_shift3.xml
index a35af21..5fe1c74 100644
--- a/java/res/xml/rowkeys_symbols_shift3.xml
+++ b/java/res/xml/rowkeys_symbols_shift3.xml
@@ -21,22 +21,20 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <!-- U+2122: "™" TRADE MARK SIGN -->
     <Key
-        latin:keyLabel="&#x2122;" />
-    <!-- U+00AE: "®" REGISTERED SIGN -->
-    <Key
-        latin:keyLabel="&#x00AE;" />
+        latin:keyLabel="\\" />
     <!-- U+00A9: "©" COPYRIGHT SIGN -->
     <Key
         latin:keyLabel="&#x00A9;" />
-    <!-- U+00B6: "¶" PILCROW SIGN
-         U+00A7: "§" SECTION SIGN -->
+    <!-- U+00AE: "®" REGISTERED SIGN -->
     <Key
-        latin:keyLabel="&#x00B6;"
-        latin:moreKeys="&#x00A7;" />
+        latin:keyLabel="&#x00AE;" />
+    <!-- U+2122: "™" TRADE MARK SIGN -->
     <Key
-        latin:keyLabel="\\" />
+        latin:keyLabel="&#x2122;" />
+    <!-- U+2105: "℅" CARE OF -->
+    <Key
+        latin:keyLabel="&#x2105;" />
     <include
-        latin:keyboardLayout="@xml/keys_less_greater" />
+        latin:keyboardLayout="@xml/keys_square_brackets" />
 </merge>
diff --git a/java/res/xml/rows_symbols.xml b/java/res/xml/rows_symbols.xml
index bd1a57e..3f102e2 100644
--- a/java/res/xml/rows_symbols.xml
+++ b/java/res/xml/rows_symbols.xml
@@ -35,7 +35,8 @@
         latin:keyWidth="10%p"
     >
         <include
-            latin:keyboardLayout="@xml/rowkeys_symbols2" />
+            latin:keyboardLayout="@xml/rowkeys_symbols2"
+            latin:keyXPos="5%p" />
     </Row>
     <Row
         latin:keyWidth="10%p"
@@ -51,6 +52,13 @@
             latin:keyWidth="fillRight"
             latin:visualInsetsLeft="1%p" />
     </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols4" />
+    <Row
+        latin:keyWidth="10%p"
+    >
+        <Key
+            latin:keyStyle="toAlphaKeyStyle"
+            latin:keyWidth="15%p" />
+        <include
+            latin:keyboardLayout="@xml/row_symbols4" />
+    </Row>
 </merge>
diff --git a/java/res/xml/rows_symbols_shift.xml b/java/res/xml/rows_symbols_shift.xml
index 9c03d90..45ada2a 100644
--- a/java/res/xml/rows_symbols_shift.xml
+++ b/java/res/xml/rows_symbols_shift.xml
@@ -35,6 +35,7 @@
         latin:keyWidth="10%p"
     >
         <include
+            latin:keyXPos="5%p"
             latin:keyboardLayout="@xml/rowkeys_symbols_shift2" />
     </Row>
     <Row
@@ -51,6 +52,13 @@
             latin:keyWidth="fillRight"
             latin:visualInsetsLeft="1%p" />
     </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols_shift4" />
+    <Row
+        latin:keyWidth="10%p"
+    >
+        <Key
+            latin:keyStyle="toAlphaKeyStyle"
+            latin:keyWidth="15%p" />
+        <include
+            latin:keyboardLayout="@xml/row_symbols_shift4" />
+    </Row>
 </merge>
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 711de63..1eccdf3 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -106,6 +106,8 @@
         EditorInfo mEditorInfo;
         boolean mDisableTouchPositionCorrectionDataForTest;
         boolean mVoiceKeyEnabled;
+        // TODO: Remove mVoiceKeyOnMain when it's certainly confirmed that we no longer show
+        // the voice input key on the symbol layout
         boolean mVoiceKeyOnMain;
         boolean mNoSettingsKey;
         boolean mLanguageSwitchKeyEnabled;
@@ -259,6 +261,8 @@
             return this;
         }
 
+        // TODO: Remove mVoiceKeyOnMain when it's certainly confirmed that we no longer show
+        // the voice input key on the symbol layout
         public Builder setOptions(final boolean voiceKeyEnabled, final boolean voiceKeyOnMain,
                 final boolean languageSwitchKeyEnabled) {
             @SuppressWarnings("deprecation")
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 4569d31..d128d35 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -142,7 +142,7 @@
         builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
         builder.setOptions(
                 settingsValues.isVoiceKeyEnabled(editorInfo),
-                settingsValues.isVoiceKeyOnMain(),
+                true /* always show a voice key on the main keyboard */,
                 settingsValues.isLanguageSwitchKeyEnabled());
         mKeyboardLayoutSet = builder.build();
         try {
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index e4a8f4c..f8ad43e 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -183,6 +183,7 @@
     private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper;
 
     private final KeyTimerHandler mKeyTimerHandler;
+    private final int mLanguageOnSpacebarHorizontalMargin;
 
     private static final class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
             implements TimerProxy {
@@ -512,6 +513,9 @@
                 altCodeKeyWhileTypingFadeinAnimatorResId, this);
 
         mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
+
+        mLanguageOnSpacebarHorizontalMargin =
+                (int) getResources().getDimension(R.dimen.language_on_spacebar_horizontal_margin);
     }
 
     @Override
@@ -1188,26 +1192,27 @@
         }
     }
 
-    private static boolean fitsTextIntoWidth(final int width, final String text,
-            final Paint paint) {
+    private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
+        final int maxTextWidth = width - mLanguageOnSpacebarHorizontalMargin * 2;
         paint.setTextScaleX(1.0f);
         final float textWidth = TypefaceUtils.getLabelWidth(text, paint);
         if (textWidth < width) {
             return true;
         }
 
-        final float scaleX = width / textWidth;
+        final float scaleX = maxTextWidth / textWidth;
         if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) {
             return false;
         }
 
         paint.setTextScaleX(scaleX);
-        return TypefaceUtils.getLabelWidth(text, paint) < width;
+        return TypefaceUtils.getLabelWidth(text, paint) < maxTextWidth;
     }
 
     // Layout language name on spacebar.
-    private static String layoutLanguageOnSpacebar(final Paint paint,
+    private String layoutLanguageOnSpacebar(final Paint paint,
             final InputMethodSubtype subtype, final int width) {
+
         // Choose appropriate language name to fit into the width.
         final String fullText = SubtypeLocaleUtils.getFullDisplayName(subtype);
         if (fitsTextIntoWidth(width, fullText, paint)) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index 63da954..7008b06 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -361,8 +361,10 @@
         /* 103 */ "\u2030",
         /* 104 */ ",",
         /* 105~ */
-        EMPTY, EMPTY, EMPTY, EMPTY,
-        /* ~108 */
+        EMPTY, EMPTY, EMPTY,
+        /* ~107 */
+        // U+2026: "…" HORIZONTAL ELLIPSIS
+        /* 108 */ "\u2026",
         /* 109 */ "\'",
         /* 110 */ "\"",
         /* 111 */ "\"",
@@ -381,7 +383,7 @@
         // Label for "switch to more symbol" modifier key.  Must be short to fit on key!
         /* 124 */ "= \\ <",
         // Label for "switch to more symbol" modifier key on tablets.  Must be short to fit on key!
-        /* 125 */ "~ \\ {",
+        /* 125 */ "~ [ {",
         // Label for "Tab" key.  Must be short to fit on key!
         /* 126 */ "Tab",
         // Label for "switch to phone numeric" key.  Must be short to fit on key!
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 6be9ded..a8a29a1 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1668,15 +1668,8 @@
         return didAutoCorrect;
     }
 
-    // Called from PointerTracker through the KeyboardActionListener interface
-    @Override
-    public void onTextInput(final String rawText) {
-        mConnection.beginBatchEdit();
-        if (mWordComposer.isComposingWord()) {
-            commitCurrentAutoCorrection(rawText);
-        } else {
-            resetComposingState(true /* alsoResetLastComposedWord */);
-        }
+    // Called from the end of onTextInput
+    private void completeOnTextInput(final String rawText) {
         mHandler.postUpdateSuggestionStrip();
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS
                 && ResearchLogger.RESEARCH_KEY_OUTPUT_TEXT.equals(rawText)) {
@@ -1699,12 +1692,44 @@
         mEnteredText = text;
     }
 
+    // Called from PointerTracker through the KeyboardActionListener interface
+    @Override
+    public void onTextInput(final String rawText) {
+        mConnection.beginBatchEdit();
+        boolean isReturningAsynchronously = false;
+        if (mWordComposer.isComposingWord()) {
+            commitCurrentAutoCorrection(rawText, new Runnable() {
+                @Override
+                public void run() {
+                    completeOnTextInput(rawText);
+                }
+            });
+            isReturningAsynchronously = true;
+        } else {
+            resetComposingState(true /* alsoResetLastComposedWord */);
+        }
+        if (!isReturningAsynchronously) {
+            completeOnTextInput(rawText);
+        }
+    }
+
+    private void completeOnStartBatchInput(final SettingsValues settingsValues) {
+        final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
+        if (Character.isLetterOrDigit(codePointBeforeCursor)
+                || settingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) {
+            mSpaceState = SPACE_STATE_PHANTOM;
+        }
+        mConnection.endBatchEdit();
+        mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
+    }
+
     @Override
     public void onStartBatchInput() {
         mInputUpdater.onStartBatchInput();
         mHandler.cancelUpdateSuggestionStrip();
         mConnection.beginBatchEdit();
         final SettingsValues settingsValues = mSettings.getCurrent();
+        boolean isReturningAsynchronously = false;
         if (mWordComposer.isComposingWord()) {
             if (settingsValues.mIsInternal) {
                 if (mWordComposer.isBatchMode()) {
@@ -1726,19 +1751,21 @@
                 // tapping probably is that the word you intend to type is not in the dictionary,
                 // so we do not attempt to correct, on the assumption that if that was a dictionary
                 // word, the user would probably have gestured instead.
-                commitCurrentAutoCorrection(LastComposedWord.NOT_A_SEPARATOR);
+                commitCurrentAutoCorrection(LastComposedWord.NOT_A_SEPARATOR, new Runnable() {
+                    @Override
+                    public void run() {
+                        completeOnStartBatchInput(settingsValues);
+                    }
+                });
+                isReturningAsynchronously = true;
             } else {
                 commitTyped(LastComposedWord.NOT_A_SEPARATOR);
             }
             mExpectingUpdateSelection = true;
         }
-        final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
-        if (Character.isLetterOrDigit(codePointBeforeCursor)
-                || settingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) {
-            mSpaceState = SPACE_STATE_PHANTOM;
+        if (!isReturningAsynchronously) {
+            completeOnStartBatchInput(settingsValues);
         }
-        mConnection.endBatchEdit();
-        mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
     }
 
     private static final class InputUpdater implements Handler.Callback {
@@ -2218,30 +2245,9 @@
         mKeyboardSwitcher.updateShiftState();
     }
 
-    // Returns true if we did an autocorrection, false otherwise.
-    private boolean handleSeparator(final int primaryCode, final int x, final int y,
-            final int spaceState) {
-        boolean didAutoCorrect = false;
-        final SettingsValues currentSettings = mSettings.getCurrent();
-        // We avoid sending spaces in languages without spaces if we were composing.
-        final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == primaryCode
-                && !currentSettings.mCurrentLanguageHasSpaces && mWordComposer.isComposingWord();
-        if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
-            // If we are in the middle of a recorrection, we need to commit the recorrection
-            // first so that we can insert the separator at the current cursor position.
-            resetEntireInputState(mLastSelectionStart);
-        }
-        if (mWordComposer.isComposingWord()) { // May have changed since we stored wasComposing
-            if (currentSettings.mCorrectionEnabled) {
-                final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR
-                        : new String(new int[] { primaryCode }, 0, 1);
-                commitCurrentAutoCorrection(separator);
-                didAutoCorrect = true;
-            } else {
-                commitTyped(new String(new int[]{primaryCode}, 0, 1));
-            }
-        }
-
+    private void completeHandleSeparator(final int primaryCode, final int x, final int y,
+            final int spaceState, final SettingsValues currentSettings,
+            final boolean shouldAvoidSendingCode) {
         final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState,
                 Constants.SUGGESTION_STRIP_COORDINATE == x);
 
@@ -2296,7 +2302,44 @@
         }
 
         mKeyboardSwitcher.updateShiftState();
-        return didAutoCorrect;
+    }
+
+    // Returns true if we do an autocorrection, false otherwise.
+    private boolean handleSeparator(final int primaryCode, final int x, final int y,
+            final int spaceState) {
+        boolean doesAutoCorrect = false;
+        final SettingsValues currentSettings = mSettings.getCurrent();
+        // We avoid sending spaces in languages without spaces if we were composing.
+        final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == primaryCode
+                && !currentSettings.mCurrentLanguageHasSpaces && mWordComposer.isComposingWord();
+        if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
+            // If we are in the middle of a recorrection, we need to commit the recorrection
+            // first so that we can insert the separator at the current cursor position.
+            resetEntireInputState(mLastSelectionStart);
+        }
+        boolean isReturningAsynchronously = false;
+        if (mWordComposer.isComposingWord()) { // May have changed since we stored wasComposing
+            if (currentSettings.mCorrectionEnabled) {
+                final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR
+                        : new String(new int[] { primaryCode }, 0, 1);
+                commitCurrentAutoCorrection(separator, new Runnable() {
+                    @Override
+                    public void run() {
+                        completeHandleSeparator(primaryCode, x, y, spaceState, currentSettings,
+                               shouldAvoidSendingCode);
+                    }
+                });
+                doesAutoCorrect = true;
+                isReturningAsynchronously = true;
+            } else {
+                commitTyped(new String(new int[]{primaryCode}, 0, 1));
+            }
+        }
+        if (!isReturningAsynchronously) {
+            completeHandleSeparator(primaryCode, x, y, spaceState, currentSettings,
+                    shouldAvoidSendingCode);
+        }
+        return doesAutoCorrect;
     }
 
     private CharSequence getTextWithUnderline(final String text) {
@@ -2507,7 +2550,7 @@
         setSuggestionStripShown(isSuggestionsStripVisible());
     }
 
-    private void commitCurrentAutoCorrection(final String separatorString) {
+    private void commitCurrentAutoCorrection(final String separator, final Runnable callback) {
         // Complete any pending suggestions query first
         if (mHandler.hasPendingUpdateSuggestions()) {
             updateSuggestionStrip();
@@ -2523,16 +2566,16 @@
             }
             if (mSettings.isInternal()) {
                 LatinImeLoggerUtils.onAutoCorrection(
-                        typedWord, autoCorrection, separatorString, mWordComposer);
+                        typedWord, autoCorrection, separator, mWordComposer);
             }
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 final SuggestedWords suggestedWords = mSuggestedWords;
                 ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection,
-                        separatorString, mWordComposer.isBatchMode(), suggestedWords);
+                        separator, mWordComposer.isBatchMode(), suggestedWords);
             }
             mExpectingUpdateSelection = true;
             commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
-                    separatorString);
+                    separator);
             if (!typedWord.equals(autoCorrection)) {
                 // This will make the correction flash for a short while as a visual clue
                 // to the user that auto-correction happened. It has no other effect; in particular
@@ -2545,6 +2588,9 @@
                         typedWord, autoCorrection));
             }
         }
+        if (callback != null) {
+            callback.run();
+        }
     }
 
     // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
index 79f5ad8..1727382 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
@@ -501,52 +501,52 @@
     }
 
     /**
-     * Helper method to write a variable-size address to a file.
+     * Helper method to write a children position to a file.
      *
      * @param buffer the buffer to write to.
      * @param index the index in the buffer to write the address to.
-     * @param address the address to write.
+     * @param position the position to write.
      * @return the size in bytes the address actually took.
      */
-    private static int writeVariableAddress(final byte[] buffer, int index, final int address) {
-        switch (getByteSize(address)) {
+    private static int writeChildrenPosition(final byte[] buffer, int index, final int position) {
+        switch (getByteSize(position)) {
         case 1:
-            buffer[index++] = (byte)address;
+            buffer[index++] = (byte)position;
             return 1;
         case 2:
-            buffer[index++] = (byte)(0xFF & (address >> 8));
-            buffer[index++] = (byte)(0xFF & address);
+            buffer[index++] = (byte)(0xFF & (position >> 8));
+            buffer[index++] = (byte)(0xFF & position);
             return 2;
         case 3:
-            buffer[index++] = (byte)(0xFF & (address >> 16));
-            buffer[index++] = (byte)(0xFF & (address >> 8));
-            buffer[index++] = (byte)(0xFF & address);
+            buffer[index++] = (byte)(0xFF & (position >> 16));
+            buffer[index++] = (byte)(0xFF & (position >> 8));
+            buffer[index++] = (byte)(0xFF & position);
             return 3;
         case 0:
             return 0;
         default:
-            throw new RuntimeException("Address " + address + " has a strange size");
+            throw new RuntimeException("Position " + position + " has a strange size");
         }
     }
 
     /**
-     * Helper method to write a variable-size signed address to a file.
+     * Helper method to write a signed children position to a file.
      *
      * @param buffer the buffer to write to.
      * @param index the index in the buffer to write the address to.
-     * @param address the address to write.
+     * @param position the position to write.
      * @return the size in bytes the address actually took.
      */
-    private static int writeVariableSignedAddress(final byte[] buffer, int index,
-            final int address) {
-        if (!BinaryDictIOUtils.hasChildrenAddress(address)) {
+    private static int writeSignedChildrenPosition(final byte[] buffer, int index,
+            final int position) {
+        if (!BinaryDictIOUtils.hasChildrenAddress(position)) {
             buffer[index] = buffer[index + 1] = buffer[index + 2] = 0;
         } else {
-            final int absAddress = Math.abs(address);
+            final int absPosition = Math.abs(position);
             buffer[index++] =
-                    (byte)((address < 0 ? FormatSpec.MSB8 : 0) | (0xFF & (absAddress >> 16)));
-            buffer[index++] = (byte)(0xFF & (absAddress >> 8));
-            buffer[index++] = (byte)(0xFF & absAddress);
+                    (byte)((position < 0 ? FormatSpec.MSB8 : 0) | (0xFF & (absPosition >> 16)));
+            buffer[index++] = (byte)(0xFF & (absPosition >> 8));
+            buffer[index++] = (byte)(0xFF & absPosition);
         }
         return 3;
     }
@@ -721,6 +721,17 @@
         }
     }
 
+    private static final int getChildrenPosition(final PtNode ptNode,
+            final FormatOptions formatOptions) {
+        int positionOfChildrenPosField = ptNode.mCachedAddressAfterUpdate
+                + getNodeHeaderSize(ptNode, formatOptions);
+        if (ptNode.mFrequency >= 0) {
+            positionOfChildrenPosField += FormatSpec.PTNODE_FREQUENCY_SIZE;
+        }
+        return null == ptNode.mChildren ? FormatSpec.NO_CHILDREN_ADDRESS
+                : ptNode.mChildren.mCachedAddressAfterUpdate - positionOfChildrenPosField;
+    }
+
     /**
      * Write a PtNodeArray to memory. The PtNodeArray is expected to have its final position cached.
      *
@@ -749,26 +760,22 @@
         } else {
             throw new RuntimeException("Strange size from getGroupCountSize : " + countSize);
         }
-        int ptNodeAddress = index;
         for (int i = 0; i < ptNodeCount; ++i) {
             final PtNode ptNode = ptNodeArray.mData.get(i);
             if (index != ptNode.mCachedAddressAfterUpdate) {
                 throw new RuntimeException("Bug: write index is not the same as the cached address "
                         + "of the node : " + index + " <> " + ptNode.mCachedAddressAfterUpdate);
             }
-            ptNodeAddress += getNodeHeaderSize(ptNode, formatOptions);
             // Sanity checks.
             if (DBG && ptNode.mFrequency > FormatSpec.MAX_TERMINAL_FREQUENCY) {
                 throw new RuntimeException("A node has a frequency > "
                         + FormatSpec.MAX_TERMINAL_FREQUENCY
                         + " : " + ptNode.mFrequency);
             }
-            if (ptNode.mFrequency >= 0) ptNodeAddress += FormatSpec.PTNODE_FREQUENCY_SIZE;
-            final int childrenOffset = null == ptNode.mChildren
-                    ? FormatSpec.NO_CHILDREN_ADDRESS
-                            : ptNode.mChildren.mCachedAddressAfterUpdate - ptNodeAddress;
-            buffer[index++] =
-                    makePtNodeFlags(ptNode, ptNodeAddress, childrenOffset, formatOptions);
+
+            final int childrenPosition = getChildrenPosition(ptNode, formatOptions);
+            buffer[index++] = makePtNodeFlags(ptNode, index, childrenPosition,
+                    formatOptions);
 
             if (parentAddress == FormatSpec.NO_PARENT_ADDRESS) {
                 index = writeParentAddress(buffer, index, parentAddress, formatOptions);
@@ -787,31 +794,25 @@
                 buffer[index++] = (byte) ptNode.mFrequency;
             }
 
-            final int shift;
             if (formatOptions.mSupportsDynamicUpdate) {
-                shift = writeVariableSignedAddress(buffer, index, childrenOffset);
+                index += writeSignedChildrenPosition(buffer, index, childrenPosition);
             } else {
-                shift = writeVariableAddress(buffer, index, childrenOffset);
+                index += writeChildrenPosition(buffer, index, childrenPosition);
             }
-            index += shift;
-            ptNodeAddress += shift;
 
             // Write shortcuts
             if (null != ptNode.mShortcutTargets && !ptNode.mShortcutTargets.isEmpty()) {
                 final int indexOfShortcutByteSize = index;
                 index += FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE;
-                ptNodeAddress += FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE;
                 final Iterator<WeightedString> shortcutIterator =
                         ptNode.mShortcutTargets.iterator();
                 while (shortcutIterator.hasNext()) {
                     final WeightedString target = shortcutIterator.next();
-                    ++ptNodeAddress;
                     int shortcutFlags = makeShortcutFlags(shortcutIterator.hasNext(),
                             target.mFrequency);
                     buffer[index++] = (byte)shortcutFlags;
                     final int shortcutShift = CharEncoding.writeString(buffer, index, target.mWord);
                     index += shortcutShift;
-                    ptNodeAddress += shortcutShift;
                 }
                 final int shortcutByteSize = index - indexOfShortcutByteSize;
                 if (shortcutByteSize > 0xFFFF) {
@@ -829,14 +830,13 @@
                             FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord);
                     final int addressOfBigram = target.mCachedAddressAfterUpdate;
                     final int unigramFrequencyForThisWord = target.mFrequency;
-                    ++ptNodeAddress;
-                    final int offset = addressOfBigram - ptNodeAddress;
+                    final int offset = addressOfBigram
+                            - (index + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
                     int bigramFlags = makeBigramFlags(bigramIterator.hasNext(), offset,
                             bigram.mFrequency, unigramFrequencyForThisWord, bigram.mWord);
                     buffer[index++] = (byte)bigramFlags;
-                    final int bigramShift = writeVariableAddress(buffer, index, Math.abs(offset));
+                    final int bigramShift = writeChildrenPosition(buffer, index, Math.abs(offset));
                     index += bigramShift;
-                    ptNodeAddress += bigramShift;
                 }
             }
 
@@ -912,23 +912,15 @@
     }
 
     /**
-     * Dumps a FusionDictionary to a file.
+     * Writes a file header to an output stream.
      *
-     * @param destination the stream to write the binary data to.
+     * @param destination the stream to write the file header to.
      * @param dict the dictionary to write.
      * @param formatOptions file format options.
      */
-    /* package */ static void writeDictionaryBinary(final OutputStream destination,
+    /* package */ static void writeDictionaryHeader(final OutputStream destination,
             final FusionDictionary dict, final FormatOptions formatOptions)
-            throws IOException, UnsupportedFormatException {
-
-        // Addresses are limited to 3 bytes, but since addresses can be relative to each node
-        // array, the structure itself is not limited to 16MB. However, if it is over 16MB deciding
-        // the order of the PtNode arrays becomes a quite complicated problem, because though the
-        // dictionary itself does not have a size limit, each node array must still be within 16MB
-        // of all its children and parents. As long as this is ensured, the dictionary file may
-        // grow to any size.
-
+                    throws IOException, UnsupportedFormatException {
         final int version = formatOptions.mVersion;
         if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
                 || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
@@ -975,6 +967,24 @@
         destination.write(bytes);
 
         headerBuffer.close();
+    }
+
+    /**
+     * Dumps a FusionDictionary to a file.
+     *
+     * @param destination the stream to write the dictionary body to.
+     * @param dict the dictionary to write.
+     * @param formatOptions file format options.
+     */
+    /* package */ static void writeDictionaryBody(final OutputStream destination,
+            final FusionDictionary dict, final FormatOptions formatOptions) throws IOException {
+
+        // Addresses are limited to 3 bytes, but since addresses can be relative to each node
+        // array, the structure itself is not limited to 16MB. However, if it is over 16MB deciding
+        // the order of the PtNode arrays becomes a quite complicated problem, because though the
+        // dictionary itself does not have a size limit, each node array must still be within 16MB
+        // of all its children and parents. As long as this is ensured, the dictionary file may
+        // grow to any size.
 
         // Leave the choice of the optimal node order to the flattenTree function.
         MakedictLog.i("Flattening the tree...");
@@ -989,7 +999,6 @@
         final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1);
         final int bufferSize = lastNodeArray.mCachedAddressAfterUpdate + lastNodeArray.mCachedSize;
         final byte[] buffer = new byte[bufferSize];
-        int index = 0;
 
         MakedictLog.i("Writing file...");
         int dataEndOffset = 0;
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index b8ef576..bf35f6a 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -112,8 +112,10 @@
      * e | 1 byte = bbbbbbbb match
      * n |   case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte)
      * t |   otherwise => (bbbbbbbb << 16) + (next byte << 8) + next byte
-     * a |
-     * ddress
+     * a | This address is relative to the head of the PtNode.
+     * d | If the node doesn't have a parent, this field is set to 0.
+     * d |
+     * ress
      *
      * c | IF FLAG_HAS_MULTIPLE_CHARS
      * h |   char, char, char, char    n * (1 or 3 bytes) : use PtNodeInfo for i/o helpers
@@ -132,17 +134,18 @@
      * i |   1 byte = bbbbbbbb match
      * l |     case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte)
      * d |     otherwise => (bbbbbbbb<<16) + (next byte << 8) + next byte
-     * r | ELSIF 00 = FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS == addressType
-     * e |   // nothing
-     * n | ELSIF 01 = FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE == addressType
-     * A |   children address, 1 byte
-     * d | ELSIF 10 = FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES == addressType
-     * d |   children address, 2 bytes
-     * r | ELSE // 11 = FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES = addressType
-     * e |   children address, 3 bytes
-     * s | END
-     * s
-     * ress
+     * r |   if this node doesn't have children, this field is set to 0.
+     * e |     (see BinaryDictEncoderUtils#writeVariableSignedAddress)
+     * n | ELSIF 00 = FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS == addressType
+     * a |   // nothing
+     * d | ELSIF 01 = FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE == addressType
+     * d |   children address, 1 byte
+     * r | ELSIF 10 = FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES == addressType
+     * e |   children address, 2 bytes
+     * s | ELSE // 11 = FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES = addressType
+     * s |   children address, 3 bytes
+     *   | END
+     *   | This address is relative to the position of this field.
      *
      *   | IF FLAG_IS_TERMINAL && FLAG_HAS_SHORTCUT_TARGETS
      *   | shortcut string list
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
index e81fd45..4e28fea 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
@@ -62,7 +62,8 @@
         if (mOutStream == null) {
             openStream();
         }
-        BinaryDictEncoderUtils.writeDictionaryBinary(mOutStream, dict, formatOptions);
+        BinaryDictEncoderUtils.writeDictionaryHeader(mOutStream, dict, formatOptions);
+        BinaryDictEncoderUtils.writeDictionaryBody(mOutStream, dict, formatOptions);
         close();
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index fd83865..8732a59 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -44,7 +44,9 @@
     public static final String PREF_VIBRATE_ON = "vibrate_on";
     public static final String PREF_SOUND_ON = "sound_on";
     public static final String PREF_POPUP_ON = "popup_on";
-    public static final String PREF_VOICE_MODE = "voice_mode";
+    // PREF_VOICE_MODE_OBSOLETE is obsolete. Use PREF_VOICE_INPUT_KEY instead.
+    public static final String PREF_VOICE_MODE_OBSOLETE = "voice_mode";
+    public static final String PREF_VOICE_INPUT_KEY = "pref_voice_input_key";
     public static final String PREF_CORRECTION_SETTINGS = "correction_settings";
     public static final String PREF_EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary";
     public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key";
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
index 1677e18..cb7dda6 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
@@ -61,7 +61,7 @@
             DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS
                     || Build.VERSION.SDK_INT <= 18 /* Build.VERSION.JELLY_BEAN_MR2 */;
 
-    private ListPreference mVoicePreference;
+    private CheckBoxPreference mVoiceInputKeyPreference;
     private ListPreference mShowCorrectionSuggestionsPreference;
     private ListPreference mAutoCorrectionThresholdPreference;
     private ListPreference mKeyPreviewPopupDismissDelay;
@@ -107,7 +107,8 @@
         SubtypeLocaleUtils.init(context);
         AudioAndHapticFeedbackManager.init(context);
 
-        mVoicePreference = (ListPreference) findPreference(Settings.PREF_VOICE_MODE);
+        mVoiceInputKeyPreference =
+                (CheckBoxPreference) findPreference(Settings.PREF_VOICE_INPUT_KEY);
         mShowCorrectionSuggestionsPreference =
                 (ListPreference) findPreference(Settings.PREF_SHOW_SUGGESTIONS_SETTING);
         final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
@@ -166,7 +167,7 @@
         final boolean showVoiceKeyOption = res.getBoolean(
                 R.bool.config_enable_show_voice_key_option);
         if (!showVoiceKeyOption) {
-            generalSettings.removePreference(mVoicePreference);
+            generalSettings.removePreference(mVoiceInputKeyPreference);
         }
 
         final PreferenceGroup advancedSettings =
@@ -243,10 +244,8 @@
     public void onResume() {
         super.onResume();
         final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
-        if (isShortcutImeEnabled) {
-            updateVoiceModeSummary();
-        } else {
-            getPreferenceScreen().removePreference(mVoicePreference);
+        if (!isShortcutImeEnabled) {
+            getPreferenceScreen().removePreference(mVoiceInputKeyPreference);
         }
         final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
         final CheckBoxPreference showSetupWizardIcon =
@@ -287,7 +286,6 @@
             LauncherIconVisibilityManager.updateSetupWizardIconVisibility(getActivity());
         }
         ensureConsistencyOfAutoCorrectionSettings();
-        updateVoiceModeSummary();
         updateShowCorrectionSuggestionsSummary();
         updateKeyPreviewPopupDelaySummary();
         refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources());
@@ -330,12 +328,6 @@
         lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]);
     }
 
-    private void updateVoiceModeSummary() {
-        mVoicePreference.setSummary(
-                getResources().getStringArray(R.array.voice_input_modes_summary)
-                        [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
-    }
-
     private void refreshEnablingsOfKeypressSoundAndVibrationSettings(
             final SharedPreferences sp, final Resources res) {
         setPreferenceEnabled(Settings.PREF_VIBRATION_DURATION_SETTINGS,
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 32730d2..072bb87 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -64,7 +64,7 @@
     public final boolean mVibrateOn;
     public final boolean mSoundOn;
     public final boolean mKeyPreviewPopupOn;
-    private final String mVoiceMode;
+    private final boolean mShowsVoiceInputKey;
     public final boolean mIncludesOtherImesInLanguageSwitchList;
     public final boolean mShowsLanguageSwitchKey;
     public final boolean mUseContactsDict;
@@ -90,8 +90,6 @@
     public final float mAutoCorrectionThreshold;
     public final boolean mCorrectionEnabled;
     public final int mSuggestionVisibility;
-    private final boolean mVoiceKeyEnabled;
-    private final boolean mVoiceKeyOnMain;
     public final boolean mBoostPersonalizationDictionaryForDebug;
     public final boolean mUseOnlyPersonalizationDictionaryForDebug;
 
@@ -137,9 +135,7 @@
         mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res);
         mSlidingKeyInputPreviewEnabled = prefs.getBoolean(
                 Settings.PREF_SLIDING_KEY_INPUT_PREVIEW, true);
-        final String voiceModeMain = res.getString(R.string.voice_mode_main);
-        final String voiceModeOff = res.getString(R.string.voice_mode_off);
-        mVoiceMode = prefs.getString(Settings.PREF_VOICE_MODE, voiceModeMain);
+        mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res);
         final String autoCorrectionThresholdRawValue = prefs.getString(
                 Settings.PREF_AUTO_CORRECTION_THRESHOLD,
                 res.getString(R.string.auto_correction_threshold_mode_index_modest));
@@ -159,8 +155,6 @@
         mKeyPreviewPopupDismissDelay = Settings.readKeyPreviewPopupDismissDelay(prefs, res);
         mAutoCorrectionThreshold = readAutoCorrectionThreshold(res,
                 autoCorrectionThresholdRawValue);
-        mVoiceKeyEnabled = mVoiceMode != null && !mVoiceMode.equals(voiceModeOff);
-        mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain);
         mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res);
         mGestureTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true);
         mGestureFloatingPreviewTextEnabled = prefs.getBoolean(
@@ -201,7 +195,7 @@
         mSoundOn = true;
         mKeyPreviewPopupOn = true;
         mSlidingKeyInputPreviewEnabled = true;
-        mVoiceMode = "0";
+        mShowsVoiceInputKey = true;
         mIncludesOtherImesInLanguageSwitchList = false;
         mShowsLanguageSwitchKey = true;
         mUseContactsDict = true;
@@ -214,8 +208,6 @@
         mKeypressSoundVolume = 1;
         mKeyPreviewPopupDismissDelay = 70;
         mAutoCorrectionThreshold = 1;
-        mVoiceKeyEnabled = true;
-        mVoiceKeyOnMain = true;
         mGestureInputEnabled = true;
         mGestureTrailEnabled = true;
         mGestureFloatingPreviewTextEnabled = true;
@@ -274,14 +266,10 @@
     public boolean isVoiceKeyEnabled(final EditorInfo editorInfo) {
         final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
         final int inputType = (editorInfo != null) ? editorInfo.inputType : 0;
-        return shortcutImeEnabled && mVoiceKeyEnabled
+        return shortcutImeEnabled && mShowsVoiceInputKey
                 && !InputTypeUtils.isPasswordInputType(inputType);
     }
 
-    public boolean isVoiceKeyOnMain() {
-        return mVoiceKeyOnMain;
-    }
-
     public boolean isLanguageSwitchKeyEnabled() {
         if (!mShowsLanguageSwitchKey) {
             return false;
@@ -372,4 +360,18 @@
         }
         return autoCorrectionThreshold;
     }
+
+    private static boolean needsToShowVoiceInputKey(SharedPreferences prefs, Resources res) {
+        final String voiceModeMain = res.getString(R.string.voice_mode_main);
+        final String voiceMode = prefs.getString(Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain);
+        final boolean showsVoiceInputKey = voiceMode == null || voiceMode.equals(voiceModeMain);
+        if (!showsVoiceInputKey) {
+            // Migrate settings from PREF_VOICE_MODE_OBSOLETE to PREF_VOICE_INPUT_KEY
+            // Set voiceModeMain as a value of obsolete voice mode settings.
+            prefs.edit().putString(Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain).apply();
+            // Disable voice input key.
+            prefs.edit().putBoolean(Settings.PREF_VOICE_INPUT_KEY, false).apply();
+        }
+        return prefs.getBoolean(Settings.PREF_VOICE_INPUT_KEY, true);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java b/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java
index c4ead0a..ac654fa 100644
--- a/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java
@@ -65,12 +65,12 @@
 
     /**
      * Get the stack trace contained in an exception as a human-readable string.
-     * @param e the exception
+     * @param t the throwable
      * @return the human-readable stack trace
      */
-    public static String getStackTrace(final Exception e) {
+    public static String getStackTrace(final Throwable t) {
         final StringBuilder sb = new StringBuilder();
-        final StackTraceElement[] frames = e.getStackTrace();
+        final StackTraceElement[] frames = t.getStackTrace();
         for (int j = 0; j < frames.length; ++j) {
             sb.append(frames[j].toString() + "\n");
         }
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index 0e36f3d..d83bdad 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -78,6 +78,7 @@
         dynamic_patricia_trie_reading_helper.cpp \
         dynamic_patricia_trie_reading_utils.cpp \
         dynamic_patricia_trie_writing_helper.cpp \
+        dynamic_patricia_trie_writing_utils.cpp \
         patricia_trie_policy.cpp \
         patricia_trie_reading_utils.cpp) \
     $(addprefix suggest/policyimpl/dictionary/utils/, \
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
index bb54e60..e815919 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
+++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
@@ -21,7 +21,6 @@
 #include "suggest/core/dicnode/dic_node.h"
 #include "suggest/core/dicnode/dic_node_vector.h"
 #include "suggest/core/dictionary/multi_bigram_map.h"
-#include "suggest/core/dictionary/probability_utils.h"
 #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 #include "utils/char_utils.h"
 
@@ -93,13 +92,15 @@
     if (NOT_A_VALID_WORD_POS == wordPos || NOT_A_VALID_WORD_POS == prevWordPos) {
         // Note: Normally wordPos comes from the dictionary and should never equal
         // NOT_A_VALID_WORD_POS.
-        return ProbabilityUtils::backoff(unigramProbability);
+        return dictionaryStructurePolicy->getProbability(unigramProbability,
+                NOT_A_PROBABILITY);
     }
     if (multiBigramMap) {
         return multiBigramMap->getBigramProbability(dictionaryStructurePolicy, prevWordPos,
                 wordPos, unigramProbability);
     }
-    return ProbabilityUtils::backoff(unigramProbability);
+    return dictionaryStructurePolicy->getProbability(unigramProbability,
+            NOT_A_PROBABILITY);
 }
 
 ////////////////
diff --git a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h
index f437c95..9bc9687 100644
--- a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h
+++ b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h
@@ -116,10 +116,6 @@
         return mPrevWordStart;
     }
 
-    int16_t getPrevWordProbability() const {
-        return mPrevWordProbability;
-    }
-
     int getPrevWordNodePos() const {
         return mPrevWordNodePos;
     }
diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
index e74a1db..cf1cd88 100644
--- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
@@ -23,7 +23,6 @@
 #include "defines.h"
 #include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h"
 #include "suggest/core/dictionary/dictionary.h"
-#include "suggest/core/dictionary/probability_utils.h"
 #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 #include "utils/char_utils.h"
 
@@ -131,7 +130,7 @@
         // resulting probability is 8 - although in the practice it's never bigger than 3 or 4
         // in very bad cases. This means that sometimes, we'll see some bigrams interverted
         // here, but it can't get too bad.
-        const int probability = ProbabilityUtils::computeProbabilityForBigram(
+        const int probability = mDictionaryStructurePolicy->getProbability(
                 unigramProbability, bigramsIt.getProbability());
         addWordBigram(bigramBuffer, codePointCount, probability, outBigramProbability,
                 outBigramCodePoints, outputTypes);
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index 8418a60..02ece63 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -90,7 +90,7 @@
     if (NOT_A_VALID_WORD_POS == pos) {
         return NOT_A_PROBABILITY;
     }
-    return getDictionaryStructurePolicy()->getUnigramProbability(pos);
+    return getDictionaryStructurePolicy()->getUnigramProbabilityOfPtNode(pos);
 }
 
 bool Dictionary::isValidBigram(const int *word0, int length0, const int *word1, int length1) const {
diff --git a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h
index fb4a800..9efe5f6 100644
--- a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h
+++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h
@@ -22,7 +22,6 @@
 #include "defines.h"
 #include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h"
 #include "suggest/core/dictionary/bloom_filter.h"
-#include "suggest/core/dictionary/probability_utils.h"
 #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 #include "utils/hash_map_compat.h"
 
@@ -43,11 +42,12 @@
         hash_map_compat<int, BigramMap>::const_iterator mapPosition =
                 mBigramMaps.find(wordPosition);
         if (mapPosition != mBigramMaps.end()) {
-            return mapPosition->second.getBigramProbability(nextWordPosition, unigramProbability);
+            return mapPosition->second.getBigramProbability(structurePolicy, nextWordPosition,
+                    unigramProbability);
         }
         if (mBigramMaps.size() < MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP) {
             addBigramsForWordPosition(structurePolicy, wordPosition);
-            return mBigramMaps[wordPosition].getBigramProbability(
+            return mBigramMaps[wordPosition].getBigramProbability(structurePolicy,
                     nextWordPosition, unigramProbability);
         }
         return readBigramProbabilityFromBinaryDictionary(structurePolicy, wordPosition,
@@ -82,17 +82,17 @@
         }
 
         AK_FORCE_INLINE int getBigramProbability(
+                const DictionaryStructureWithBufferPolicy *const structurePolicy,
                 const int nextWordPosition, const int unigramProbability) const {
+            int bigramProbability = NOT_A_PROBABILITY;
             if (mBloomFilter.isInFilter(nextWordPosition)) {
                 const hash_map_compat<int, int>::const_iterator bigramProbabilityIt =
                         mBigramMap.find(nextWordPosition);
                 if (bigramProbabilityIt != mBigramMap.end()) {
-                    const int bigramProbability = bigramProbabilityIt->second;
-                    return ProbabilityUtils::computeProbabilityForBigram(
-                            unigramProbability, bigramProbability);
+                    bigramProbability = bigramProbabilityIt->second;
                 }
             }
-            return ProbabilityUtils::backoff(unigramProbability);
+            return structurePolicy->getProbability(unigramProbability, bigramProbability);
         }
 
      private:
@@ -111,17 +111,18 @@
     AK_FORCE_INLINE int readBigramProbabilityFromBinaryDictionary(
             const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos,
             const int nextWordPosition, const int unigramProbability) {
+        int bigramProbability = NOT_A_PROBABILITY;
         const int bigramsListPos = structurePolicy->getBigramsPositionOfNode(nodePos);
         BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(),
                 bigramsListPos);
         while (bigramsIt.hasNext()) {
             bigramsIt.next();
             if (bigramsIt.getBigramPos() == nextWordPosition) {
-                return ProbabilityUtils::computeProbabilityForBigram(
-                        unigramProbability, bigramsIt.getProbability());
+                bigramProbability = bigramsIt.getProbability();
+                break;
             }
         }
-        return ProbabilityUtils::backoff(unigramProbability);
+        return structurePolicy->getProbability(unigramProbability, bigramProbability);
     }
 
     static const size_t MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP;
diff --git a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
index 5324115..c8cbbcf 100644
--- a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
+++ b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
@@ -47,7 +47,10 @@
     virtual int getTerminalNodePositionOfWord(const int *const inWord,
             const int length, const bool forceLowerCaseSearch) const = 0;
 
-    virtual int getUnigramProbability(const int nodePos) const = 0;
+    virtual int getProbability(const int unigramProbability,
+            const int bigramProbability) const = 0;
+
+    virtual int getUnigramProbabilityOfPtNode(const int nodePos) const = 0;
 
     virtual int getShortcutPositionOfNode(const int nodePos) const = 0;
 
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index 7d8dd21..e788e91 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -171,7 +171,9 @@
                 terminalIndex, doubleLetterTerminalIndex, doubleLetterLevel);
         const float compoundDistance = terminalDicNode->getCompoundDistance(languageWeight)
                 + doubleLetterCost;
-        const bool isPossiblyOffensiveWord = terminalDicNode->getProbability() <= 0;
+        const bool isPossiblyOffensiveWord =
+                traverseSession->getDictionaryStructurePolicy()->getProbability(
+                        terminalDicNode->getProbability(), NOT_A_PROBABILITY) <= 0;
         const bool isExactMatch = terminalDicNode->isExactMatch();
         const bool isFirstCharUppercase = terminalDicNode->isFirstCharUppercase();
         // Heuristic: We exclude freq=0 first-char-uppercase words from exact match.
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
index 3b9878b..945677b 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
@@ -24,6 +24,7 @@
 #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
 #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h"
 #include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/utils/probability_utils.h"
 
 namespace latinime {
 
@@ -134,7 +135,20 @@
     return NOT_A_VALID_WORD_POS;
 }
 
-int DynamicPatriciaTriePolicy::getUnigramProbability(const int nodePos) const {
+int DynamicPatriciaTriePolicy::getProbability(const int unigramProbability,
+        const int bigramProbability) const {
+    // TODO: check mHeaderPolicy.usesForgettingCurve();
+    if (unigramProbability == NOT_A_PROBABILITY) {
+        return NOT_A_PROBABILITY;
+    } else if (bigramProbability == NOT_A_PROBABILITY) {
+        return ProbabilityUtils::backoff(unigramProbability);
+    } else {
+        return ProbabilityUtils::computeProbabilityForBigram(unigramProbability,
+                bigramProbability);
+    }
+}
+
+int DynamicPatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int nodePos) const {
     if (nodePos == NOT_A_VALID_WORD_POS) {
         return NOT_A_PROBABILITY;
     }
@@ -144,7 +158,7 @@
     if (nodeReader.isDeleted() || nodeReader.isBlacklisted() || nodeReader.isNotAWord()) {
         return NOT_A_PROBABILITY;
     }
-    return nodeReader.getProbability();
+    return getProbability(nodeReader.getProbability(), NOT_A_PROBABILITY);
 }
 
 int DynamicPatriciaTriePolicy::getShortcutPositionOfNode(const int nodePos) const {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
index 5873d3d..cdab0e1 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
@@ -57,7 +57,9 @@
     int getTerminalNodePositionOfWord(const int *const inWord,
             const int length, const bool forceLowerCaseSearch) const;
 
-    int getUnigramProbability(const int nodePos) const;
+    int getProbability(const int unigramProbability, const int bigramProbability) const;
+
+    int getUnigramProbabilityOfPtNode(const int nodePos) const;
 
     int getShortcutPositionOfNode(const int nodePos) const;
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp
index 2042fcb..a0b5be6 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp
@@ -70,9 +70,10 @@
     if (usesAdditionalBuffer) {
         mPos += mBuffer->getOriginalBufferSize();
     }
+    mPosOfLastForwardLinkField = mPos;
     if (DynamicPatriciaTrieReadingUtils::isValidForwardLinkPosition(forwardLinkPosition)) {
         // Follow the forward link.
-        mPos = forwardLinkPosition;
+        mPos += forwardLinkPosition;
         nextNodeArray();
     } else {
         // All node arrays have been read.
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h
index b108ed5..db1c392 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h
@@ -38,8 +38,8 @@
             const DictionaryBigramsStructurePolicy *const bigramsPolicy,
             const DictionaryShortcutsStructurePolicy *const shortcutsPolicy)
             : mIsError(false), mPos(NOT_A_DICT_POS), mNodeCount(0), mPrevTotalCodePointCount(0),
-              mTotalNodeCount(0), mNodeArrayCount(0), mBuffer(buffer),
-              mNodeReader(mBuffer, bigramsPolicy, shortcutsPolicy) {}
+              mTotalNodeCount(0), mNodeArrayCount(0), mPosOfLastForwardLinkField(NOT_A_DICT_POS),
+              mBuffer(buffer), mNodeReader(mBuffer, bigramsPolicy, shortcutsPolicy) {}
 
     ~DynamicPatriciaTrieReadingHelper() {}
 
@@ -62,6 +62,7 @@
             mPrevTotalCodePointCount = 0;
             mTotalNodeCount = 0;
             mNodeArrayCount = 0;
+            mPosOfLastForwardLinkField = NOT_A_DICT_POS;
             nextNodeArray();
             if (!isEnd()) {
                 fetchNodeInfo();
@@ -81,6 +82,7 @@
             mPrevTotalCodePointCount = 0;
             mTotalNodeCount = 1;
             mNodeArrayCount = 1;
+            mPosOfLastForwardLinkField = NOT_A_DICT_POS;
             fetchNodeInfo();
         }
     }
@@ -140,6 +142,7 @@
             mTotalNodeCount = 0;
             mNodeArrayCount = 0;
             mPos = mNodeReader.getChildrenPos();
+            mPosOfLastForwardLinkField = NOT_A_DICT_POS;
             // Read children node array.
             nextNodeArray();
             if (!isEnd()) {
@@ -158,12 +161,17 @@
             mNodeArrayCount = 1;
             mNodeCount = 1;
             mPos = mNodeReader.getParentPos();
+            mPosOfLastForwardLinkField = NOT_A_DICT_POS;
             fetchNodeInfo();
         } else {
             mPos = NOT_A_DICT_POS;
         }
     }
 
+    AK_FORCE_INLINE int getPosOfLastForwardLinkField() const {
+        return mPosOfLastForwardLinkField;
+    }
+
  private:
     DISALLOW_COPY_AND_ASSIGN(DynamicPatriciaTrieReadingHelper);
 
@@ -177,6 +185,7 @@
     int mPrevTotalCodePointCount;
     int mTotalNodeCount;
     int mNodeArrayCount;
+    int mPosOfLastForwardLinkField;
     const BufferWithExtendableBuffer *const mBuffer;
     DynamicPatriciaTrieNodeReader mNodeReader;
     int mMergedNodeCodePoints[MAX_WORD_LENGTH];
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h
index a6cb46d..62d73bb 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h
@@ -56,6 +56,15 @@
         return FLAG_IS_DELETED == (MASK_MOVED & flags);
     }
 
+    static AK_FORCE_INLINE NodeFlags updateAndGetFlags(const NodeFlags originalFlags,
+            const bool isMoved, const bool isDeleted) {
+        NodeFlags flags = originalFlags;
+        flags = isMoved ? ((flags & (!MASK_MOVED)) | FLAG_IS_MOVED) : flags;
+        flags = isDeleted ? ((flags & (!MASK_MOVED)) | FLAG_IS_DELETED) : flags;
+        flags = (!isMoved && !isDeleted) ? ((flags & (!MASK_MOVED)) | FLAG_IS_NOT_MOVED) : flags;
+        return flags;
+    }
+
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieReadingUtils);
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
index 128d69d..03dc576 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
@@ -19,6 +19,9 @@
 #include "suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h"
 #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h"
 #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h"
+#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h"
 #include "suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h"
 
 namespace latinime {
@@ -26,6 +29,7 @@
 bool DynamicPatriciaTrieWritingHelper::addUnigramWord(
         DynamicPatriciaTrieReadingHelper *const readingHelper,
         const int *const wordCodePoints, const int codePointCount, const int probability) {
+    int parentPos = NOT_A_VALID_WORD_POS;
     while (!readingHelper->isEnd()) {
         const int matchedCodePointCount = readingHelper->getPrevTotalCodePointCount();
         if (!readingHelper->isMatchedCodePoint(0 /* index */,
@@ -64,14 +68,20 @@
             return false;
         }
         // Advance to the children nodes.
+        parentPos = nodeReader->getNodePos();
         readingHelper->readChildNode();
     }
     if (readingHelper->isError()) {
         // The dictionary is invalid.
         return false;
     }
-    // TODO: add at the last position of the node array.
+    int pos = readingHelper->getPosOfLastForwardLinkField();
+    // TODO: Remove.
     return false;
+    return createAndInsertNodeIntoPtNodeArray(parentPos,
+            wordCodePoints + readingHelper->getPrevTotalCodePointCount(),
+            codePointCount - readingHelper->getPrevTotalCodePointCount(),
+            probability, &pos);
 }
 
 bool DynamicPatriciaTrieWritingHelper::addBigramWords(const int word0Pos, const int word1Pos,
@@ -96,4 +106,112 @@
     return false;
 }
 
+bool DynamicPatriciaTrieWritingHelper::markNodeAsMovedAndSetPosition(
+        const DynamicPatriciaTrieNodeReader *const originalNode, const int movedPos) {
+    int pos = originalNode->getNodePos();
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(pos);
+    const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer);
+    if (usesAdditionalBuffer) {
+        pos -= mBuffer->getOriginalBufferSize();
+    }
+    // Read original flags
+    const PatriciaTrieReadingUtils::NodeFlags originalFlags =
+            PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos);
+    const PatriciaTrieReadingUtils::NodeFlags updatedFlags =
+            DynamicPatriciaTrieReadingUtils::updateAndGetFlags(originalFlags, true /* isMoved */,
+                    false /* isDeleted */);
+    int writingPos = originalNode->getNodePos();
+    // Update flags.
+    if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(mBuffer, updatedFlags,
+            &writingPos)) {
+        return false;
+    }
+    // Update moved position, which is stored in the parent position field.
+    if (!DynamicPatriciaTrieWritingUtils::writeParentPositionAndAdvancePosition(
+            mBuffer, movedPos, &writingPos)) {
+        return false;
+    }
+    return true;
+}
+
+// Write new node at writingPos.
+bool DynamicPatriciaTrieWritingHelper::writeNodeToBuffer(const bool isBlacklisted,
+        const bool isNotAWord, const int parentPos, const int *const codePoints,
+        const int codePointCount, const int probability, const int childrenPos,
+        const int originalBigramListPos, const int originalShortcutListPos,
+        int *const writingPos) {
+    // Create node flags and write them.
+    const PatriciaTrieReadingUtils::NodeFlags nodeFlags =
+            PatriciaTrieReadingUtils::createAndGetFlags(isBlacklisted, isNotAWord,
+                    probability != NOT_A_PROBABILITY, originalShortcutListPos != NOT_A_DICT_POS,
+                    originalBigramListPos != NOT_A_DICT_POS, codePointCount > 1,
+                    3 /* childrenPositionFieldSize */);
+    if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(mBuffer, nodeFlags,
+            writingPos)) {
+        return false;
+    }
+    // Write parent position
+    if (!DynamicPatriciaTrieWritingUtils::writeParentPositionAndAdvancePosition(mBuffer, parentPos,
+            writingPos)) {
+        return false;
+    }
+    // Write code points
+    if (!DynamicPatriciaTrieWritingUtils::writeCodePointsAndAdvancePosition(mBuffer, codePoints,
+            codePointCount, writingPos)) {
+        return false;;
+    }
+    // Write probability when the probability is a valid probability, which means this node is
+    // terminal.
+    if (probability != NOT_A_PROBABILITY) {
+        if (!DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition(mBuffer,
+                probability, writingPos)) {
+            return false;
+        }
+    }
+    // Write children position
+    if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(mBuffer,
+            childrenPos, writingPos)) {
+        return false;
+    }
+    // Copy shortcut list when the originalShortcutListPos is valid dictionary position.
+    if (originalShortcutListPos != NOT_A_DICT_POS) {
+        int fromPos = originalShortcutListPos;
+        mShortcutPolicy->copyAllShortcuts(&fromPos, writingPos);
+    }
+    // Copy bigram list when the originalBigramListPos is valid dictionary position.
+    if (originalBigramListPos != NOT_A_DICT_POS) {
+        int fromPos = originalBigramListPos;
+        if (!mBigramPolicy->copyAllBigrams(&fromPos, writingPos)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool DynamicPatriciaTrieWritingHelper::createAndInsertNodeIntoPtNodeArray(const int parentPos,
+        const int *const nodeCodePoints, const int nodeCodePointCount, const int probability,
+        int *const forwardLinkFieldPos) {
+    const int newPtNodeArrayPos = mBuffer->getTailPosition();
+    if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer,
+            newPtNodeArrayPos, forwardLinkFieldPos)) {
+        return false;
+    }
+    int writingPos = newPtNodeArrayPos;
+    if (!DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition(mBuffer,
+            1 /* arraySize */, &writingPos)) {
+        return false;
+    }
+    if (!writeNodeToBuffer(false /* isBlacklisted */, false /* isNotAWord */, parentPos,
+            nodeCodePoints, nodeCodePointCount, probability, NOT_A_DICT_POS /* childrenPos */,
+            NOT_A_DICT_POS /* originalBigramsPos */, NOT_A_DICT_POS /* originalShortcutPos */,
+            &writingPos)) {
+        return false;
+    }
+    if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer,
+            NOT_A_DICT_POS /* forwardLinkPos */, &writingPos)) {
+        return false;
+    }
+    return true;
+}
+
 } // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h
index f8165fc..524f361 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h
@@ -23,6 +23,7 @@
 
 class BufferWithExtendableBuffer;
 class DynamicBigramListPolicy;
+class DynamicPatriciaTrieNodeReader;
 class DynamicPatriciaTrieReadingHelper;
 class DynamicShortcutListPolicy;
 
@@ -51,6 +52,17 @@
     BufferWithExtendableBuffer *const mBuffer;
     DynamicBigramListPolicy *const mBigramPolicy;
     DynamicShortcutListPolicy *const mShortcutPolicy;
+
+    bool markNodeAsMovedAndSetPosition(const DynamicPatriciaTrieNodeReader *const nodeToUpdate,
+            const int movedPos);
+
+    bool writeNodeToBuffer(const bool isBlacklisted, const bool isNotAWord, const int parentPos,
+            const int *const codePoints, const int codePointCount, const int probability,
+            const int childrenPos, const int originalBigramListPos,
+            const int originalShortcutListPos, int *const writingPos);
+
+    bool createAndInsertNodeIntoPtNodeArray(const int parentPos, const int *const nodeCodePoints,
+            const int nodeCodePointCount, const int probability, int *const forwardLinkFieldPos);
 };
 } // namespace latinime
 #endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp
new file mode 100644
index 0000000..4187504
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2013, 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 "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h"
+
+#include <cstddef>
+#include <cstdlib>
+#include <stdint.h>
+
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+
+namespace latinime {
+
+const size_t DynamicPatriciaTrieWritingUtils::MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD = 0x7F;
+const size_t DynamicPatriciaTrieWritingUtils::MAX_PTNODE_ARRAY_SIZE = 0x7FFF;
+const int DynamicPatriciaTrieWritingUtils::SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE = 1;
+const int DynamicPatriciaTrieWritingUtils::LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE = 2;
+const int DynamicPatriciaTrieWritingUtils::LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG = 0x8000;
+const int DynamicPatriciaTrieWritingUtils::DICT_OFFSET_FIELD_SIZE = 3;
+const int DynamicPatriciaTrieWritingUtils::MAX_DICT_OFFSET_VALUE = 0x7FFFFF;
+const int DynamicPatriciaTrieWritingUtils::MIN_DICT_OFFSET_VALUE = -0x7FFFFF;
+const int DynamicPatriciaTrieWritingUtils::DICT_OFFSET_NEGATIVE_FLAG = 0x800000;
+const int DynamicPatriciaTrieWritingUtils::PROBABILITY_FIELD_SIZE = 1;
+const int DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE = 1;
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer, const int forwardLinkPos,
+        int *const forwardLinkFieldPos) {
+    const int offset = (forwardLinkPos != NOT_A_DICT_POS) ?
+            forwardLinkPos - (*forwardLinkFieldPos) : 0;
+    return writeDictOffset(buffer, offset, forwardLinkFieldPos);
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer, const size_t arraySize,
+        int *const arraySizeFieldPos) {
+    if (arraySize <= MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD) {
+        return buffer->writeUintAndAdvancePosition(arraySize, SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE,
+                arraySizeFieldPos);
+    } else if (arraySize <= MAX_PTNODE_ARRAY_SIZE) {
+        uint32_t data = arraySize | LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG;
+        return buffer->writeUintAndAdvancePosition(data, LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE,
+                arraySizeFieldPos);
+    } else {
+        AKLOGI("PtNode array size cannot be written because arraySize is too large: %zd",
+                arraySize);
+        ASSERT(false);
+        return false;
+    }
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer,
+        const DynamicPatriciaTrieReadingUtils::NodeFlags nodeFlags, int *const nodeFlagsFieldPos) {
+    return buffer->writeUintAndAdvancePosition(nodeFlags, NODE_FLAG_FIELD_SIZE, nodeFlagsFieldPos);
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeParentPositionAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer, const int parentPosition,
+        int *const parentPosFieldPos) {
+    // Note that parentPosition is offset from node's head position.
+    int offset = (parentPosition != NOT_A_DICT_POS) ? parentPosition : 0;
+    return writeDictOffset(buffer, offset, parentPosFieldPos);
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeCodePointsAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer, const int *const codePoints,
+        const int codePointCount, int *const codePointFieldPos) {
+    if (codePointCount <= 0) {
+        AKLOGI("code points cannot be written because codePointCount is invalid: %d",
+                codePointCount);
+        ASSERT(false);
+        return false;
+    }
+    const bool hasMultipleCodePoints = codePointCount > 1;
+    return buffer->writeCodePointsAndAdvancePosition(codePoints, codePointCount,
+            hasMultipleCodePoints, codePointFieldPos);
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer, const int probability,
+        int *const probabilityFieldPos) {
+    if (probability < 0 || probability > MAX_PROBABILITY) {
+        AKLOGI("probability cannot be written because the probability is invalid: %d",
+                probability);
+        ASSERT(false);
+        return false;
+    }
+    return buffer->writeUintAndAdvancePosition(probability, PROBABILITY_FIELD_SIZE,
+            probabilityFieldPos);
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer, const int childrenPosition,
+        int *const childrenPositionFieldPos) {
+    int offset = (childrenPosition != NOT_A_DICT_POS) ?
+            childrenPosition - (*childrenPositionFieldPos) : 0;
+    return writeDictOffset(buffer, offset, childrenPositionFieldPos);
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeDictOffset(
+        BufferWithExtendableBuffer *const buffer, const int offset, int *const offsetFieldPos) {
+    if (offset > MAX_DICT_OFFSET_VALUE || offset < MIN_DICT_OFFSET_VALUE) {
+        AKLOGI("offset cannot be written because the offset is too large or too small: %d",
+                offset);
+        ASSERT(false);
+        return false;
+    }
+    uint32_t data = 0;
+    if (offset >= 0) {
+        data = offset;
+    } else {
+        data = abs(offset) | DICT_OFFSET_NEGATIVE_FLAG;
+    }
+    return buffer->writeUintAndAdvancePosition(data, DICT_OFFSET_FIELD_SIZE, offsetFieldPos);
+}
+}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h
new file mode 100644
index 0000000..801042d
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013, 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_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H
+#define LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H
+
+#include <cstddef>
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
+
+namespace latinime {
+
+class BufferWithExtendableBuffer;
+
+class DynamicPatriciaTrieWritingUtils {
+ public:
+    static bool writeForwardLinkPositionAndAdvancePosition(
+            BufferWithExtendableBuffer *const buffer, const int forwardLinkPos,
+            int *const forwardLinkFieldPos);
+
+    static bool writePtNodeArraySizeAndAdvancePosition(BufferWithExtendableBuffer *const buffer,
+            const size_t arraySize, int *const arraySizeFieldPos);
+
+    static bool writeFlagsAndAdvancePosition(BufferWithExtendableBuffer *const buffer,
+            const DynamicPatriciaTrieReadingUtils::NodeFlags nodeFlags,
+            int *const nodeFlagsFieldPos);
+
+    static bool writeParentPositionAndAdvancePosition(BufferWithExtendableBuffer *const buffer,
+            const int parentPosition, int *const parentPosFieldPos);
+
+    static bool writeCodePointsAndAdvancePosition(BufferWithExtendableBuffer *const buffer,
+            const int *const codePoints, const int codePointCount, int *const codePointFieldPos);
+
+    static bool writeProbabilityAndAdvancePosition(BufferWithExtendableBuffer *const buffer,
+            const int probability, int *const probabilityFieldPos);
+
+    static bool writeChildrenPositionAndAdvancePosition(BufferWithExtendableBuffer *const buffer,
+            const int childrenPosition, int *const childrenPositionFieldPos);
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieWritingUtils);
+
+    static const size_t MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD;
+    static const size_t MAX_PTNODE_ARRAY_SIZE;
+    static const int SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE;
+    static const int LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE;
+    static const int LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG;
+    static const int DICT_OFFSET_FIELD_SIZE;
+    static const int MAX_DICT_OFFSET_VALUE;
+    static const int MIN_DICT_OFFSET_VALUE;
+    static const int DICT_OFFSET_NEGATIVE_FLAG;
+    static const int NODE_FLAG_FIELD_SIZE;
+    static const int PROBABILITY_FIELD_SIZE;
+
+    static bool writeDictOffset(BufferWithExtendableBuffer *const buffer, const int offset,
+            int *const offsetFieldPos);
+};
+} // namespace latinime
+#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp
index adcf2db..d5a83a9 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp
@@ -21,6 +21,7 @@
 #include "suggest/core/dicnode/dic_node.h"
 #include "suggest/core/dicnode/dic_node_vector.h"
 #include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/utils/probability_utils.h"
 
 namespace latinime {
 
@@ -306,7 +307,19 @@
     }
 }
 
-int PatriciaTriePolicy::getUnigramProbability(const int nodePos) const {
+int PatriciaTriePolicy::getProbability(const int unigramProbability,
+        const int bigramProbability) const {
+    if (unigramProbability == NOT_A_PROBABILITY) {
+        return NOT_A_PROBABILITY;
+    } else if (bigramProbability == NOT_A_PROBABILITY) {
+        return ProbabilityUtils::backoff(unigramProbability);
+    } else {
+        return ProbabilityUtils::computeProbabilityForBigram(unigramProbability,
+                bigramProbability);
+    }
+}
+
+int PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int nodePos) const {
     if (nodePos == NOT_A_VALID_WORD_POS) {
         return NOT_A_PROBABILITY;
     }
@@ -324,7 +337,8 @@
         return NOT_A_PROBABILITY;
     }
     PatriciaTrieReadingUtils::skipCharacters(mDictRoot, flags, MAX_WORD_LENGTH, &pos);
-    return PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, &pos);
+    return getProbability(PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(
+            mDictRoot, &pos), NOT_A_PROBABILITY);
 }
 
 int PatriciaTriePolicy::getShortcutPositionOfNode(const int nodePos) const {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h
index d0567fd..75d9762 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h
@@ -56,7 +56,9 @@
     int getTerminalNodePositionOfWord(const int *const inWord,
             const int length, const bool forceLowerCaseSearch) const;
 
-    int getUnigramProbability(const int nodePos) const;
+    int getProbability(const int unigramProbability, const int bigramProbability) const;
+
+    int getUnigramProbabilityOfPtNode(const int nodePos) const;
 
     int getShortcutPositionOfNode(const int nodePos) const;
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h
index f76c387..2b0646d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h
@@ -119,6 +119,29 @@
         return FLAG_CHILDREN_POSITION_TYPE_NOPOSITION != (MASK_CHILDREN_POSITION_TYPE & flags);
     }
 
+    static AK_FORCE_INLINE NodeFlags createAndGetFlags(const bool isBlacklisted,
+            const bool isNotAWord, const bool isTerminal, const bool hasShortcutTargets,
+            const bool hasBigrams, const bool hasMultipleChars,
+            const int childrenPositionFieldSize) {
+        NodeFlags nodeFlags = 0;
+        nodeFlags = isBlacklisted ? (nodeFlags | FLAG_IS_BLACKLISTED) : nodeFlags;
+        nodeFlags = isNotAWord ? (nodeFlags | FLAG_IS_NOT_A_WORD) : nodeFlags;
+        nodeFlags = isTerminal ? (nodeFlags | FLAG_IS_TERMINAL) : nodeFlags;
+        nodeFlags = hasShortcutTargets ? (nodeFlags | FLAG_HAS_SHORTCUT_TARGETS) : nodeFlags;
+        nodeFlags = hasBigrams ? (nodeFlags | FLAG_HAS_BIGRAMS) : nodeFlags;
+        nodeFlags = hasMultipleChars ? (nodeFlags | FLAG_HAS_MULTIPLE_CHARS) : nodeFlags;
+        if (childrenPositionFieldSize == 1) {
+            nodeFlags |= FLAG_CHILDREN_POSITION_TYPE_ONEBYTE;
+        } else if (childrenPositionFieldSize == 2) {
+            nodeFlags |= FLAG_CHILDREN_POSITION_TYPE_TWOBYTES;
+        } else if (childrenPositionFieldSize == 3) {
+            nodeFlags |= FLAG_CHILDREN_POSITION_TYPE_THREEBYTES;
+        } else {
+            nodeFlags |= FLAG_CHILDREN_POSITION_TYPE_NOPOSITION;
+        }
+        return nodeFlags;
+    }
+
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(PatriciaTrieReadingUtils);
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp
index 8582c4b..6326754 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp
@@ -22,4 +22,70 @@
 const size_t BufferWithExtendableBuffer::MAX_ADDITIONAL_BUFFER_SIZE = 1024 * 1024;
 const size_t BufferWithExtendableBuffer::EXTEND_ADDITIONAL_BUFFER_SIZE_STEP = 16 * 1024;
 
+bool BufferWithExtendableBuffer::writeUintAndAdvancePosition(const uint32_t data, const int size,
+        int *const pos) {
+    if (!(size >= 1 && size <= 4)) {
+        AKLOGI("writeUintAndAdvancePosition() is called with invalid size: %d", size);
+        ASSERT(false);
+        return false;
+    }
+    if (!checkAndPrepareWriting(*pos, size)) {
+        return false;
+    }
+    const bool usesAdditionalBuffer = isInAdditionalBuffer(*pos);
+    uint8_t *const buffer = usesAdditionalBuffer ? &mAdditionalBuffer[0] : mOriginalBuffer;
+    if (usesAdditionalBuffer) {
+        *pos -= mOriginalBufferSize;
+    }
+    ByteArrayUtils::writeUintAndAdvancePosition(buffer, data, size, pos);
+    if (usesAdditionalBuffer) {
+        *pos += mOriginalBufferSize;
+    }
+    return true;
+}
+
+bool BufferWithExtendableBuffer::writeCodePointsAndAdvancePosition(const int *const codePoints,
+        const int codePointCount, const bool writesTerminator ,int *const pos) {
+    const size_t size = ByteArrayUtils::calculateRequiredByteCountToStoreCodePoints(
+            codePoints, codePointCount, writesTerminator);
+    if (!checkAndPrepareWriting(*pos, size)) {
+        return false;
+    }
+    const bool usesAdditionalBuffer = isInAdditionalBuffer(*pos);
+    uint8_t *const buffer = usesAdditionalBuffer ? &mAdditionalBuffer[0] : mOriginalBuffer;
+    if (usesAdditionalBuffer) {
+        *pos -= mOriginalBufferSize;
+    }
+    ByteArrayUtils::writeCodePointsAndAdvancePosition(buffer, codePoints, codePointCount,
+            writesTerminator, pos);
+    if (usesAdditionalBuffer) {
+        *pos += mOriginalBufferSize;
+    }
+    return true;
+}
+
+bool BufferWithExtendableBuffer::checkAndPrepareWriting(const int pos, const int size) {
+    if (isInAdditionalBuffer(pos)) {
+        if (pos == mUsedAdditionalBufferSize) {
+            // Append data to the tail.
+            if (pos + size > static_cast<int>(mAdditionalBuffer.size())) {
+                // Need to extend buffer.
+                if (!extendBuffer()) {
+                    return false;
+                }
+            }
+            mUsedAdditionalBufferSize += size;
+        } else if (pos + size >= mUsedAdditionalBufferSize) {
+            // The access will beyond the tail of used region.
+            return false;
+        }
+    } else {
+        if (pos < 0 || mOriginalBufferSize < pos + size) {
+            // Invalid position or violate the boundary.
+            return false;
+        }
+    }
+    return true;
+}
+
 }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h
index ec871ec..b35b47d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h
@@ -66,27 +66,10 @@
      * Writing is allowed for original buffer, already written region of additional buffer and the
      * tail of additional buffer.
      */
-    AK_FORCE_INLINE bool writeUintAndAdvancePosition(const uint32_t data, const int size,
-            int *const pos) {
-        if (!(size >= 1 && size <= 4)) {
-            AKLOGI("writeUintAndAdvancePosition() is called with invalid size: %d", size);
-            ASSERT(false);
-            return false;
-        }
-        if (!checkAndPrepareWriting(*pos, size)) {
-            return false;
-        }
-        const bool usesAdditionalBuffer = isInAdditionalBuffer(*pos);
-        uint8_t *const buffer = usesAdditionalBuffer ? &mAdditionalBuffer[0] : mOriginalBuffer;
-        if (usesAdditionalBuffer) {
-            *pos -= mOriginalBufferSize;
-        }
-        ByteArrayUtils::writeUintAndAdvancePosition(buffer, data, size, pos);
-        if (usesAdditionalBuffer) {
-            *pos += mOriginalBufferSize;
-        }
-        return true;
-    }
+    bool writeUintAndAdvancePosition(const uint32_t data, const int size, int *const pos);
+
+    bool writeCodePointsAndAdvancePosition(const int *const codePoints, const int codePointCount,
+            const bool writesTerminator, int *const pos);
 
  private:
     DISALLOW_COPY_AND_ASSIGN(BufferWithExtendableBuffer);
@@ -112,29 +95,7 @@
 
     // Returns if it is possible to write size-bytes from pos. When pos is at the tail position of
     // the additional buffer, try extending the buffer.
-    AK_FORCE_INLINE bool checkAndPrepareWriting(const int pos, const int size) {
-        if (isInAdditionalBuffer(pos)) {
-            if (pos == mUsedAdditionalBufferSize) {
-                // Append data to the tail.
-                if (pos + size > static_cast<int>(mAdditionalBuffer.size())) {
-                    // Need to extend buffer.
-                    if (!extendBuffer()) {
-                        return false;
-                    }
-                }
-                mUsedAdditionalBufferSize += size;
-            } else if (pos + size >= mUsedAdditionalBufferSize) {
-                // The access will beyond the tail of used region.
-                return false;
-            }
-        } else {
-            if (pos < 0 || mOriginalBufferSize < pos + size) {
-                // Invalid position or violate the boundary.
-                return false;
-            }
-        }
-        return true;
-    }
+    AK_FORCE_INLINE bool checkAndPrepareWriting(const int pos, const int size);
 };
 }
 #endif /* LATINIME_BUFFER_WITH_EXTENDABLE_BUFFER_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h
index e2cb9a0..f727ecf 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h
@@ -115,7 +115,7 @@
     }
 
     /**
-     * Code Point
+     * Code Point Reading
      *
      * 1 byte = bbbbbbbb match
      * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte
@@ -149,7 +149,7 @@
     }
 
     /**
-     * String (array of code points)
+     * String (array of code points) Reading
      *
      * Reads code points until the terminator is found.
      */
@@ -176,6 +176,51 @@
         return length;
     }
 
+    /**
+     * String (array of code points) Writing
+     */
+    static void writeCodePointsAndAdvancePosition(uint8_t *const buffer,
+            const int *const codePoints, const int codePointCount, const bool writesTerminator,
+            int *const pos) {
+        for (int i = 0; i < codePointCount; ++i) {
+            const int codePoint = codePoints[i];
+            if (codePoint == NOT_A_CODE_POINT || codePoint == CHARACTER_ARRAY_TERMINATOR) {
+                break;
+            } else if (codePoint < MINIMAL_ONE_BYTE_CHARACTER_VALUE) {
+                // three bytes character.
+                writeUint24AndAdvancePosition(buffer, codePoint, pos);
+            } else {
+                // one byte character.
+                writeUint8AndAdvancePosition(buffer, codePoint, pos);
+            }
+        }
+        if (writesTerminator) {
+            writeUint8AndAdvancePosition(buffer, CHARACTER_ARRAY_TERMINATOR, pos);
+        }
+    }
+
+    static int calculateRequiredByteCountToStoreCodePoints(const int *const codePoints,
+            const int codePointCount, const bool writesTerminator) {
+        int byteCount = 0;
+        for (int i = 0; i < codePointCount; ++i) {
+            const int codePoint = codePoints[i];
+            if (codePoint == NOT_A_CODE_POINT || codePoint == CHARACTER_ARRAY_TERMINATOR) {
+                break;
+            } else if (codePoint < MINIMAL_ONE_BYTE_CHARACTER_VALUE) {
+                // three bytes character.
+                byteCount += 3;
+            } else {
+                // one byte character.
+                byteCount += 1;
+            }
+        }
+        if (writesTerminator) {
+            // The terminator is one byte.
+            byteCount += 1;
+        }
+        return byteCount;
+    }
+
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(ByteArrayUtils);
 
diff --git a/native/jni/src/suggest/core/dictionary/probability_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h
similarity index 100%
rename from native/jni/src/suggest/core/dictionary/probability_utils.h
rename to native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h
diff --git a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
index 3732505..a4c2f12 100644
--- a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
@@ -170,7 +170,8 @@
     <string name="keyhintlabel_for_tablet_comma"></string>
     <string name="more_keys_for_tablet_comma"></string>
     <string name="keyhintlabel_for_tablet_period"></string>
-    <string name="more_keys_for_tablet_period"></string>
+    <!-- U+2026: "…" HORIZONTAL ELLIPSIS -->
+    <string name="more_keys_for_tablet_period">&#x2026;</string>
     <string name="keylabel_for_apostrophe">\'</string>
     <string name="keyhintlabel_for_apostrophe">\"</string>
     <string name="more_keys_for_apostrophe">\"</string>
@@ -189,7 +190,7 @@
     <!-- 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>
+    <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 phone numeric" key.  Must be short to fit on key! -->