Merge changes I20ce61c4,I2edab4e3

* changes:
  Remove "Use only personalization dictionary".
  Remove redundant creation of dictionary facilitator.
diff --git a/dictionaries/cs_wordlist.combined.gz b/dictionaries/cs_wordlist.combined.gz
index 7829d65..94ba863 100644
--- a/dictionaries/cs_wordlist.combined.gz
+++ b/dictionaries/cs_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/da_wordlist.combined.gz b/dictionaries/da_wordlist.combined.gz
index e714019..b4baf62 100644
--- a/dictionaries/da_wordlist.combined.gz
+++ b/dictionaries/da_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/de_wordlist.combined.gz b/dictionaries/de_wordlist.combined.gz
index 6a4bd44..400718d 100644
--- a/dictionaries/de_wordlist.combined.gz
+++ b/dictionaries/de_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/el_wordlist.combined.gz b/dictionaries/el_wordlist.combined.gz
index 74effa3..599734c 100644
--- a/dictionaries/el_wordlist.combined.gz
+++ b/dictionaries/el_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz
index ff69f64..22685d1 100644
--- a/dictionaries/en_GB_wordlist.combined.gz
+++ b/dictionaries/en_GB_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_US_wordlist.combined.gz b/dictionaries/en_US_wordlist.combined.gz
index 2c80bc4..92a3761 100644
--- a/dictionaries/en_US_wordlist.combined.gz
+++ b/dictionaries/en_US_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_emoji.combined.gz b/dictionaries/en_emoji.combined.gz
index 0fc009d..4d9cf1b 100644
--- a/dictionaries/en_emoji.combined.gz
+++ b/dictionaries/en_emoji.combined.gz
Binary files differ
diff --git a/dictionaries/en_wordlist.combined.gz b/dictionaries/en_wordlist.combined.gz
index 06fb64f..f8b71c8 100644
--- a/dictionaries/en_wordlist.combined.gz
+++ b/dictionaries/en_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/es_wordlist.combined.gz b/dictionaries/es_wordlist.combined.gz
index 0a48b6d..56617db 100644
--- a/dictionaries/es_wordlist.combined.gz
+++ b/dictionaries/es_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/fi_wordlist.combined.gz b/dictionaries/fi_wordlist.combined.gz
index eefbfe5..b7332ad 100644
--- a/dictionaries/fi_wordlist.combined.gz
+++ b/dictionaries/fi_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/fr_emoji.combined.gz b/dictionaries/fr_emoji.combined.gz
index f292977..5c9c7a0 100644
--- a/dictionaries/fr_emoji.combined.gz
+++ b/dictionaries/fr_emoji.combined.gz
Binary files differ
diff --git a/dictionaries/fr_wordlist.combined.gz b/dictionaries/fr_wordlist.combined.gz
index b568838..697f367 100644
--- a/dictionaries/fr_wordlist.combined.gz
+++ b/dictionaries/fr_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/hr_wordlist.combined.gz b/dictionaries/hr_wordlist.combined.gz
index 864f676..9a2086f 100644
--- a/dictionaries/hr_wordlist.combined.gz
+++ b/dictionaries/hr_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/it_wordlist.combined.gz b/dictionaries/it_wordlist.combined.gz
index ac977e3..5a5cbdc 100644
--- a/dictionaries/it_wordlist.combined.gz
+++ b/dictionaries/it_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/iw_wordlist.combined.gz b/dictionaries/iw_wordlist.combined.gz
index 36b0478..13eab9f 100644
--- a/dictionaries/iw_wordlist.combined.gz
+++ b/dictionaries/iw_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/lt_wordlist.combined.gz b/dictionaries/lt_wordlist.combined.gz
index 029722d..961266b 100644
--- a/dictionaries/lt_wordlist.combined.gz
+++ b/dictionaries/lt_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/lv_wordlist.combined.gz b/dictionaries/lv_wordlist.combined.gz
index 41e1c28..ae906a9 100644
--- a/dictionaries/lv_wordlist.combined.gz
+++ b/dictionaries/lv_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/nb_wordlist.combined.gz b/dictionaries/nb_wordlist.combined.gz
index b699912..1c0f2cf 100644
--- a/dictionaries/nb_wordlist.combined.gz
+++ b/dictionaries/nb_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/nl_wordlist.combined.gz b/dictionaries/nl_wordlist.combined.gz
index 89c2388..37ba8ab 100644
--- a/dictionaries/nl_wordlist.combined.gz
+++ b/dictionaries/nl_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pl_wordlist.combined.gz b/dictionaries/pl_wordlist.combined.gz
index 2b53f69..ba71a55 100644
--- a/dictionaries/pl_wordlist.combined.gz
+++ b/dictionaries/pl_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pt_BR_wordlist.combined.gz b/dictionaries/pt_BR_wordlist.combined.gz
index 2d22447..02df1c1 100644
--- a/dictionaries/pt_BR_wordlist.combined.gz
+++ b/dictionaries/pt_BR_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pt_PT_wordlist.combined.gz b/dictionaries/pt_PT_wordlist.combined.gz
index 1504165..bcd50ab 100644
--- a/dictionaries/pt_PT_wordlist.combined.gz
+++ b/dictionaries/pt_PT_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/ru_wordlist.combined.gz b/dictionaries/ru_wordlist.combined.gz
index 8e654e9..acde9bc 100644
--- a/dictionaries/ru_wordlist.combined.gz
+++ b/dictionaries/ru_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/sl_wordlist.combined.gz b/dictionaries/sl_wordlist.combined.gz
index 55e1bb1..a7240fe 100644
--- a/dictionaries/sl_wordlist.combined.gz
+++ b/dictionaries/sl_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/sr_wordlist.combined.gz b/dictionaries/sr_wordlist.combined.gz
index 8488a08..30ce996 100644
--- a/dictionaries/sr_wordlist.combined.gz
+++ b/dictionaries/sr_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/sv_wordlist.combined.gz b/dictionaries/sv_wordlist.combined.gz
index 6342520..b6ebab3 100644
--- a/dictionaries/sv_wordlist.combined.gz
+++ b/dictionaries/sv_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/tr_wordlist.combined.gz b/dictionaries/tr_wordlist.combined.gz
index 0251778..306cea1 100644
--- a/dictionaries/tr_wordlist.combined.gz
+++ b/dictionaries/tr_wordlist.combined.gz
Binary files differ
diff --git a/java/res/color/key_text_color_holo.xml b/java/res/color/key_text_color_holo.xml
deleted file mode 100644
index d034a94..0000000
--- a/java/res/color/key_text_color_holo.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- Functional keys. -->
-    <item android:state_single="true" android:state_pressed="true"
-          android:color="@color/key_text_color_functional_holo" />
-    <item android:state_single="true"
-          android:color="@color/key_text_color_functional_holo" />
-
-    <!-- Action keys. -->
-    <item android:state_active="true" android:state_pressed="true"
-          android:color="@color/key_text_color_normal_holo" />
-    <item android:state_active="true"
-          android:color="@color/key_text_color_normal_holo" />
-
-    <!-- Toggle keys. Use checkable/checked state. -->
-    <item android:state_checkable="true" android:state_checked="true" android:state_pressed="true"
-          android:color="@color/key_text_color_normal_holo" />
-    <item android:state_checkable="true" android:state_pressed="true"
-          android:color="@color/key_text_color_normal_holo" />
-    <item android:state_checkable="true" android:state_checked="true"
-          android:color="@color/key_text_color_normal_holo" />
-    <item android:state_checkable="true"
-          android:color="@color/key_text_color_normal_holo" />
-
-    <!-- Empty background keys. -->
-    <item android:state_empty="true"
-          android:color="@color/key_text_color_normal_holo" />
-
-    <!-- Normal keys. -->
-    <item android:state_pressed="true"
-          android:color="@color/key_text_color_normal_holo" />
-    <item android:color="@color/key_text_color_normal_holo" />
-</selector>
diff --git a/java/res/raw/main_de.dict b/java/res/raw/main_de.dict
index c65698d..3cbf710 100644
--- a/java/res/raw/main_de.dict
+++ b/java/res/raw/main_de.dict
Binary files differ
diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict
index 57f3fe7..49adc9a 100644
--- a/java/res/raw/main_en.dict
+++ b/java/res/raw/main_en.dict
Binary files differ
diff --git a/java/res/raw/main_es.dict b/java/res/raw/main_es.dict
index 261ab8c..fe24cd6 100644
--- a/java/res/raw/main_es.dict
+++ b/java/res/raw/main_es.dict
Binary files differ
diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict
index c240d96..94d1b96 100644
--- a/java/res/raw/main_fr.dict
+++ b/java/res/raw/main_fr.dict
Binary files differ
diff --git a/java/res/raw/main_it.dict b/java/res/raw/main_it.dict
index 98135ce..ff11b97 100644
--- a/java/res/raw/main_it.dict
+++ b/java/res/raw/main_it.dict
Binary files differ
diff --git a/java/res/raw/main_pt_br.dict b/java/res/raw/main_pt_br.dict
index 21bbe7c..9fa5044 100644
--- a/java/res/raw/main_pt_br.dict
+++ b/java/res/raw/main_pt_br.dict
Binary files differ
diff --git a/java/res/raw/main_ru.dict b/java/res/raw/main_ru.dict
index c2b1e67..0f08f17 100644
--- a/java/res/raw/main_ru.dict
+++ b/java/res/raw/main_ru.dict
Binary files differ
diff --git a/java/res/values-af/strings-config-important-notice.xml b/java/res/values-af/strings-config-important-notice.xml
index d0f328f..7c4dea5 100644
--- a/java/res/values-af/strings-config-important-notice.xml
+++ b/java/res/values-af/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Leer uit jou kommunikasie en getikte data om voorstelle te verbeter"</string>
 </resources>
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index df43b13..59494aa 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -167,10 +167,10 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Kies \'n woordeboeklêer om te installeer"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Moet hierdie lêer regtig vir <xliff:g id="LANGUAGE_NAME">%s</xliff:g> geïnstalleer word?"</string>
     <string name="error" msgid="8940763624668513648">"Daar was \'n fout"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Gooi kontaktewoordeboek weg"</string>
-    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Gooi persoonlike woordeboek weg"</string>
-    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Gooi gebruikergeskiedeniswoordeboek weg"</string>
-    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Gooi personaliseringwoordeboek weg"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Lys kontaktewoordeboek"</string>
+    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Lys persoonlike woordeboek"</string>
+    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Lys gebruikergeskiedeniswoordeboek"</string>
+    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Lys personaliseringwoordeboek"</string>
     <string name="button_default" msgid="3988017840431881491">"Verstek"</string>
     <string name="setup_welcome_title" msgid="6112821709832031715">"Welkom by <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="setup_welcome_additional_description" msgid="8150252008545768953">"met Gebaar-tik"</string>
diff --git a/java/res/values-am/strings-config-important-notice.xml b/java/res/values-am/strings-config-important-notice.xml
index 5bef043..ad64b6a 100644
--- a/java/res/values-am/strings-config-important-notice.xml
+++ b/java/res/values-am/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"የአስተያየት ጥቆማዎችን ለማሻሻል ከእርስዎ ግንኙነቶች እና የተተየበ ውሂብ ይማሩ"</string>
 </resources>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index 3921fd1..6d4c5bf 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -167,10 +167,10 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"የሚጭኑት የመዝገበ-ቃላት ፋይል ይምረጡ"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"እውን ይሄ ፋይል ለ<xliff:g id="LANGUAGE_NAME">%s</xliff:g> ይጫን?"</string>
     <string name="error" msgid="8940763624668513648">"ስህተት ተከስቶ ነበር"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"የእውቂያዎች መዝገበ-ቃላትን ያራግፉ"</string>
-    <string name="prefs_dump_user_dict" msgid="294870685041741951">"የግል መዝገበ-ቃላትን ያራግፉ"</string>
-    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"የተጠቃሚ ታሪክ መዝገበ-ቃላትን ያራግፉ"</string>
-    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"የግላዊነት ማላበሻ መዝገበ-ቃላትን ያራግፉ"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"የእውቂያዎች መዝገበ-ቃላትን ዝርዝር ይፍጠሩ"</string>
+    <string name="prefs_dump_user_dict" msgid="294870685041741951">"የግል መዝገበ-ቃላትን ዝርዝር ይፍጠሩ"</string>
+    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"የተጠቃሚ ታሪክ መዝገበ-ቃላትን ዝርዝር ይፍጠሩ"</string>
+    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"የግላዊነት ማላበሻ መዝገበ-ቃላትን ዝርዝር ይፍጠሩ"</string>
     <string name="button_default" msgid="3988017840431881491">"ነባሪ"</string>
     <string name="setup_welcome_title" msgid="6112821709832031715">"እንኳን ወደ <xliff:g id="APPLICATION_NAME">%s</xliff:g> በደህና መጡ"</string>
     <string name="setup_welcome_additional_description" msgid="8150252008545768953">"በጣት ምልክት መተየብ"</string>
diff --git a/java/res/values-ar/strings-config-important-notice.xml b/java/res/values-ar/strings-config-important-notice.xml
index 36600af..5e716c4 100644
--- a/java/res/values-ar/strings-config-important-notice.xml
+++ b/java/res/values-ar/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"التعلم من اتصالاتك والبيانات التي تكتبها لتحسين الاقتراحات"</string>
 </resources>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index a0c26cf..f6ac117 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"تحديد ملف قاموس للتثبيت"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"هل تريد حقًا تثبيت هذا الملف للغة <xliff:g id="LANGUAGE_NAME">%s</xliff:g>؟"</string>
     <string name="error" msgid="8940763624668513648">"حدث خطأ"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"تفريغ معجم جهات الاتصال"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"عرض جهات الاتصال"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"تفريغ المعجم الشخصي"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"تفريغ معجم سجل المستخدم"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"تفريغ معجم التخصيص"</string>
diff --git a/java/res/values-az-rAZ/strings-config-important-notice.xml b/java/res/values-az-rAZ/strings-config-important-notice.xml
index 1815443..fa6f952 100644
--- a/java/res/values-az-rAZ/strings-config-important-notice.xml
+++ b/java/res/values-az-rAZ/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Əlaqələrdən və təkliflərin inkişafı üçün yazdığınız datadan öyrənin "</string>
 </resources>
diff --git a/java/res/values-bg/strings-config-important-notice.xml b/java/res/values-bg/strings-config-important-notice.xml
index b6b7666..06f1408 100644
--- a/java/res/values-bg/strings-config-important-notice.xml
+++ b/java/res/values-bg/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Ползване на съобщ. ви и въведени от вас данни за подобр. на предлож."</string>
 </resources>
diff --git a/java/res/values-ca/strings-config-important-notice.xml b/java/res/values-ca/strings-config-important-notice.xml
index eadb569..57a7ade 100644
--- a/java/res/values-ca/strings-config-important-notice.xml
+++ b/java/res/values-ca/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
-    <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Considera comunicacions i dades introduïdes per millorar sugger."</string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
+    <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Considera comunicacions i dades introd. per millorar suggeriments"</string>
 </resources>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index 7137aa5..5afaca0 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Selecció d\'un fitxer de diccionari per instal·lar"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Realment vols instal·lar aquest fitxer per a <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"S\'ha produït un error"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Esborrar el diccionari de contactes"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Genera el diccionari de contactes"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Esborrar el diccionari personal"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Esborrar dicc. d\'histor. d\'usuaris"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Esborrar diccionari de personalitz."</string>
diff --git a/java/res/values-cs/strings-config-important-notice.xml b/java/res/values-cs/strings-config-important-notice.xml
index bb2f03a..f69e946 100644
--- a/java/res/values-cs/strings-config-important-notice.xml
+++ b/java/res/values-cs/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Zlepšovat návrhy na základě vaší komunikace a zadaných dat"</string>
 </resources>
diff --git a/java/res/values-da/strings-config-important-notice.xml b/java/res/values-da/strings-config-important-notice.xml
index 57d32cd..a167b31 100644
--- a/java/res/values-da/strings-config-important-notice.xml
+++ b/java/res/values-da/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Giv bedre forslag ud fra tidligere kommunikation og data"</string>
 </resources>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index babea96..7c0af04 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -167,10 +167,10 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Vælg den ordbog, som du vil installere"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Vil du virkelig installere denne fil for <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Der opstod en fejl"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Eksportér ordbog for kontakter"</string>
-    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Eksportér personlig ordbog"</string>
-    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Eksportér ordbog for brugerhistorik"</string>
-    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Eksportér ordbog for tilpasning"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Gem ordbog for kontakter"</string>
+    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Gem personlig ordbog"</string>
+    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Gem ordbog for brugerhistorik"</string>
+    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Gem ordbog for tilpasning"</string>
     <string name="button_default" msgid="3988017840431881491">"Standard"</string>
     <string name="setup_welcome_title" msgid="6112821709832031715">"Velkommen til <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="setup_welcome_additional_description" msgid="8150252008545768953">"med glidende indtastning"</string>
diff --git a/java/res/values-de/strings-config-important-notice.xml b/java/res/values-de/strings-config-important-notice.xml
index 5a84202..6734a1c 100644
--- a/java/res/values-de/strings-config-important-notice.xml
+++ b/java/res/values-de/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Vorschläge anhand bisheriger Nachrichten und Eingaben verbessern"</string>
 </resources>
diff --git a/java/res/values-el/strings-config-important-notice.xml b/java/res/values-el/strings-config-important-notice.xml
index 4ee3532..3e0c45b 100644
--- a/java/res/values-el/strings-config-important-notice.xml
+++ b/java/res/values-el/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Χρήση επικοινωνιών/δεδομένων πληκτρολόγησης για βελτίωση προτάσεων"</string>
 </resources>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 5e888e0..4ea576c 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Επιλογή αρχείου λεξικού για εγκατάσταση"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Εγκατάσταση αυτού του αρχείου για τα <xliff:g id="LANGUAGE_NAME">%s</xliff:g>;"</string>
     <string name="error" msgid="8940763624668513648">"Παρουσιάστηκε σφάλμα."</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Αποτύπωση λεξικού επαφών"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Εξαγωγή λεξικού επαφών"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Αποτύπωση προσωπικού λεξικού"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Αποτύπωση λεξικού ιστορικού χρήστη"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Αποτύπωση λεξικού εξατομίκευσης"</string>
diff --git a/java/res/values-en-rGB/strings-config-important-notice.xml b/java/res/values-en-rGB/strings-config-important-notice.xml
index a7ae300..5530dbe 100644
--- a/java/res/values-en-rGB/strings-config-important-notice.xml
+++ b/java/res/values-en-rGB/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Learn from your communications and typed data to improve suggestions"</string>
 </resources>
diff --git a/java/res/values-en-rIN/strings-config-important-notice.xml b/java/res/values-en-rIN/strings-config-important-notice.xml
index a7ae300..5530dbe 100644
--- a/java/res/values-en-rIN/strings-config-important-notice.xml
+++ b/java/res/values-en-rIN/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Learn from your communications and typed data to improve suggestions"</string>
 </resources>
diff --git a/java/res/values-es-rUS/strings-config-important-notice.xml b/java/res/values-es-rUS/strings-config-important-notice.xml
index b39cfba..4561842 100644
--- a/java/res/values-es-rUS/strings-config-important-notice.xml
+++ b/java/res/values-es-rUS/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Aprende de mensajes y datos ingresados para mejorar las sugerencias."</string>
 </resources>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index c900c6a..50583e8 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Seleccionar archivo de diccionario para instalar"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"¿Realmente quieres instalar este archivo para <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Se produjo un error."</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Volcar diccionario de contactos"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Generar diccionario de contactos"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Volcar diccionario personal"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Volcar diccionario hist. usuario"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Volcar diccionario personalización"</string>
diff --git a/java/res/values-es/strings-config-important-notice.xml b/java/res/values-es/strings-config-important-notice.xml
index 4d82e8b..1068ea8 100644
--- a/java/res/values-es/strings-config-important-notice.xml
+++ b/java/res/values-es/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Aprende de mensajes y datos escritos para mejorar sugerencias"</string>
 </resources>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index ad7547f..3427751 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Selecciona un archivo de diccionario para instalar"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"¿Seguro que quieres instalar este archivo para <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Se ha producido un error"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Volcar diccionario de contactos"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Generar diccionario de contactos"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Volcar diccionario personal"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Volcar diccionario historial usuario"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Volcar diccionario personalización"</string>
diff --git a/java/res/values-et-rEE/strings-config-important-notice.xml b/java/res/values-et-rEE/strings-config-important-notice.xml
index c08d045..d77b7f9 100644
--- a/java/res/values-et-rEE/strings-config-important-notice.xml
+++ b/java/res/values-et-rEE/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Kommunikats. ja sisestatud andmetest õppimine soovit. täiustamiseks"</string>
 </resources>
diff --git a/java/res/values-fa/strings-config-important-notice.xml b/java/res/values-fa/strings-config-important-notice.xml
index b4e0335..915f3d4 100644
--- a/java/res/values-fa/strings-config-important-notice.xml
+++ b/java/res/values-fa/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"یادگیری از ارتباطات و اطلاعات تایپ شده شما برای بهبود پیشنهادات"</string>
 </resources>
diff --git a/java/res/values-fi/strings-config-important-notice.xml b/java/res/values-fi/strings-config-important-notice.xml
index 2646501..316781b 100644
--- a/java/res/values-fi/strings-config-important-notice.xml
+++ b/java/res/values-fi/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Ehdotusten parannus viestinnän ja kirjoitettujen tietojen avulla"</string>
 </resources>
diff --git a/java/res/values-fr-rCA/strings-config-important-notice.xml b/java/res/values-fr-rCA/strings-config-important-notice.xml
index 05b452c..506101d 100644
--- a/java/res/values-fr-rCA/strings-config-important-notice.xml
+++ b/java/res/values-fr-rCA/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Apprendre de vos communic. et données entrées pour amél. suggestions"</string>
 </resources>
diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml
index dcb1119..cb6daee 100644
--- a/java/res/values-fr-rCA/strings.xml
+++ b/java/res/values-fr-rCA/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Sélectionner un fichier de dictionnaire à installer"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Installer ce fichier pour la langue « <xliff:g id="LANGUAGE_NAME">%s</xliff:g> »?"</string>
     <string name="error" msgid="8940763624668513648">"Une erreur s\'est produite"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Vider le dictionnaire des contacts"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Extraire dictionnaire des contacts"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Vider le dictionnaire personnel"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Vider dictionnaire hist. utilisateur"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Vider dictionnaire personnalisation"</string>
diff --git a/java/res/values-fr/strings-config-important-notice.xml b/java/res/values-fr/strings-config-important-notice.xml
index 21000de..1672ee0 100644
--- a/java/res/values-fr/strings-config-important-notice.xml
+++ b/java/res/values-fr/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Améliorer suggestions en fonction des messages et données saisies"</string>
 </resources>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 3cd6e26..e3243c3 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -167,10 +167,10 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Sélectionner un fichier de dictionnaire à installer"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Installer ce fichier pour la langue \"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>\" ?"</string>
     <string name="error" msgid="8940763624668513648">"Une erreur s\'est produite"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Supprimer dictionnaire des contacts"</string>
-    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Supprimer le dictionnaire personnel"</string>
-    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Supprimer l\'ancien dictionnaire"</string>
-    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Suppr. dictionnaire personnalisation"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Extraire dictionnaire des contacts"</string>
+    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Extraire le dictionnaire personnel"</string>
+    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Extraire dict. de l\'histor. util."</string>
+    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Extraire dict. de personnalisation"</string>
     <string name="button_default" msgid="3988017840431881491">"Par défaut"</string>
     <string name="setup_welcome_title" msgid="6112821709832031715">"Bienvenue dans <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="setup_welcome_additional_description" msgid="8150252008545768953">"avec la saisie gestuelle"</string>
diff --git a/java/res/values-hi/strings-config-important-notice.xml b/java/res/values-hi/strings-config-important-notice.xml
index 9a1c12b..ae6b4c7 100644
--- a/java/res/values-hi/strings-config-important-notice.xml
+++ b/java/res/values-hi/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"सुझावों में सुधार हेतु अपने संचार और लिखे गए डेटा से जानकारी पाएं"</string>
 </resources>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 0487986..b752d61 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"इंस्टॉल करने के लिए कोई शब्दकोश फ़ाइल चुनें"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"क्या वाकई <xliff:g id="LANGUAGE_NAME">%s</xliff:g> के लिए यह फ़ाइल इंस्‍टॉल करें?"</string>
     <string name="error" msgid="8940763624668513648">"कोई त्रुटि हुई थी"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"संपर्क शब्दकोश डंप करें"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"संपर्क शब्दकोश सूचीबद्ध करें"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"व्यक्तिगत शब्दकोश डंप करें"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"उपयोगकर्ता इतिहास शब्दकोश डंप करें"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"वैयक्तिकरण शब्दकोश डंप करें"</string>
diff --git a/java/res/values-hr/strings-config-important-notice.xml b/java/res/values-hr/strings-config-important-notice.xml
index c93544e..fff191b 100644
--- a/java/res/values-hr/strings-config-important-notice.xml
+++ b/java/res/values-hr/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Upotrijebi poruke i upisane podatke za poboljšanje prijedloga"</string>
 </resources>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index eb1391f..6d7e191 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Odabir datoteke rječnika za instaliranje"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Želite li zaista instalirati tu datoteku za <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Došlo je do pogreške"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Kopiranje rječnika kontakata"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Ispis rječnika kontakata"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Kopiranje osobnog rječnika"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Kopiranje rječ. povijesti korisnika"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Kopiranje rječnika za prilagodbu"</string>
diff --git a/java/res/values-hu/strings-config-important-notice.xml b/java/res/values-hu/strings-config-important-notice.xml
index b35c9f0..4cb3c17 100644
--- a/java/res/values-hu/strings-config-important-notice.xml
+++ b/java/res/values-hu/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Javaslatok javítása a kommunikáció és begépelt adatok alapján"</string>
 </resources>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index d4fffec..88fd3d6 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Válasszon ki egy szótárfájlt a telepítéshez."</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Valóban telepíti ezt a fájlt <xliff:g id="LANGUAGE_NAME">%s</xliff:g> nyelvhez?"</string>
     <string name="error" msgid="8940763624668513648">"Hiba történt."</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Névjegyszótár törlése"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Névjegytár kiírása"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Személyes szótár törlése"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Felhasználóielőzmény-szótár törlése"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Testreszabási szótár törlése"</string>
diff --git a/java/res/values-hy-rAM/strings-config-important-notice.xml b/java/res/values-hy-rAM/strings-config-important-notice.xml
index 0d1588e..2463c11 100644
--- a/java/res/values-hy-rAM/strings-config-important-notice.xml
+++ b/java/res/values-hy-rAM/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Բարելավեք առաջարկները` ձեր նամակագրությունից և մուտքագրած տվյալներից"</string>
 </resources>
diff --git a/java/res/values-in/strings-config-important-notice.xml b/java/res/values-in/strings-config-important-notice.xml
index c1950c4..03ca54e 100644
--- a/java/res/values-in/strings-config-important-notice.xml
+++ b/java/res/values-in/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Belajar dari komunikasi &amp; data terketik untuk meningkatkan saran"</string>
 </resources>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index b790792..9964daa 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -167,10 +167,10 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Pilih file kamus untuk dipasang"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Yakin ingin memasang file ini untuk <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Terjadi kesalahan"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Kosongkan kamus kontak"</string>
-    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Kosongkan kamus pribadi"</string>
-    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Kosongkan kamus riwayat pengguna"</string>
-    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Kosongkan kamus hasil personalisasi"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Buat daftar kamus kontak"</string>
+    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Buat daftar kamus pribadi"</string>
+    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Buat daftar kamus riwayat pengguna"</string>
+    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Buat daftar kamus personalisasi"</string>
     <string name="button_default" msgid="3988017840431881491">"Default"</string>
     <string name="setup_welcome_title" msgid="6112821709832031715">"Selamat datang di <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="setup_welcome_additional_description" msgid="8150252008545768953">"dengan Ketikan Isyarat"</string>
diff --git a/java/res/values-it/strings-config-important-notice.xml b/java/res/values-it/strings-config-important-notice.xml
index deff79f..95b2a18 100644
--- a/java/res/values-it/strings-config-important-notice.xml
+++ b/java/res/values-it/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Usa comunicazioni e dati digitati per migliorare i suggerimenti"</string>
 </resources>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index a9728f1..78f3987 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -167,10 +167,10 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Seleziona un file di dizionario da installare"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Vuoi davvero installare questo file per <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Si è verificato un errore"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Scarica dizionario contatti"</string>
-    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Scarica dizionario personale"</string>
-    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Scarica dizion. cronologia utente"</string>
-    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Scarica dizionario di personalizz."</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Estrai dizionario contatti"</string>
+    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Estrai dizionario personale"</string>
+    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Estrai dizion. cronologia utente"</string>
+    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Estrai dizionario di personalizz."</string>
     <string name="button_default" msgid="3988017840431881491">"Predefinito"</string>
     <string name="setup_welcome_title" msgid="6112821709832031715">"Benvenuto in <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="setup_welcome_additional_description" msgid="8150252008545768953">"con la Digitazione gestuale"</string>
diff --git a/java/res/values-iw/strings-config-important-notice.xml b/java/res/values-iw/strings-config-important-notice.xml
index c7f3529..0ad8031 100644
--- a/java/res/values-iw/strings-config-important-notice.xml
+++ b/java/res/values-iw/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"למד מהתכתבויות ומנתונים שהקלדת כדי לשפר את ההצעות"</string>
 </resources>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index 870623f..accf809 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"בחירת קובץ מילון להתקנה"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"האם אתה באמת רוצה להתקין את הקובץ הזה עבור <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"אירעה שגיאה"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"מחק את מילון אנשי הקשר"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"צור מילון אנשי קשר"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"מחק מילון אישי"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"מחק את המילון של היסטוריית המשתמשים"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"מחק את מילון ההתאמה האישית"</string>
diff --git a/java/res/values-ja/strings-config-important-notice.xml b/java/res/values-ja/strings-config-important-notice.xml
index 937ddae..31b5a71 100644
--- a/java/res/values-ja/strings-config-important-notice.xml
+++ b/java/res/values-ja/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"メッセージなどのやり取りや入力したデータから入力候補を予測します"</string>
 </resources>
diff --git a/java/res/values-ka-rGE/strings-config-important-notice.xml b/java/res/values-ka-rGE/strings-config-important-notice.xml
index e43ca01..02b280c 100644
--- a/java/res/values-ka-rGE/strings-config-important-notice.xml
+++ b/java/res/values-ka-rGE/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"უკეთესი შეთავაზებისთვის თქვენი კომუნიკაციიდან და ტექსტიდან სწავლა"</string>
 </resources>
diff --git a/java/res/values-km-rKH/strings-config-important-notice.xml b/java/res/values-km-rKH/strings-config-important-notice.xml
index ad0325a..7f58d61 100644
--- a/java/res/values-km-rKH/strings-config-important-notice.xml
+++ b/java/res/values-km-rKH/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"សិក្សាការភ្ជាប់របស់អ្នកនិងទិន្នន័យ​ដែលបានបញ្ចូលដើម្បីធ្វើសំណើ​ឲ្យល្អ"</string>
 </resources>
diff --git a/java/res/values-ko/strings-config-important-notice.xml b/java/res/values-ko/strings-config-important-notice.xml
index 2ea6e49..b8cfb37 100644
--- a/java/res/values-ko/strings-config-important-notice.xml
+++ b/java/res/values-ko/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"사용자의 대화 내용과 입력한 데이터를 통해 단어 추천의 정확도를 개선합니다."</string>
 </resources>
diff --git a/java/res/values-lo-rLA/strings-config-important-notice.xml b/java/res/values-lo-rLA/strings-config-important-notice.xml
index d4e5052..079dd6b 100644
--- a/java/res/values-lo-rLA/strings-config-important-notice.xml
+++ b/java/res/values-lo-rLA/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"ຮຽນຮູ້ຈາກການສື່ສານ ແລະຂໍ້ມູນທີ່ທ່ານເຄີຍພິມເພື່ອປັບປຸງຄຳແນະນຳ"</string>
 </resources>
diff --git a/java/res/values-lt/strings-config-important-notice.xml b/java/res/values-lt/strings-config-important-notice.xml
index 633980e..cb91b5b 100644
--- a/java/res/values-lt/strings-config-important-notice.xml
+++ b/java/res/values-lt/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Mokytis iš ryšių ir įvestų duomenų, siekiant pagerinti pasiūlymus"</string>
 </resources>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index 9efdc67..331656f 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Pasirinkite diegiamą žodyno failą"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Ar tikrai įdiegti šį failą <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Įvyko klaida"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Iškelti kontaktų žodyną"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Sudaryti kontaktų žodyną"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Iškelti asmeninį žodyną"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Iškelti naudotojo istorijos žodyną"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Iškelti suasmeninimo žodyną"</string>
diff --git a/java/res/values-lv/strings-config-important-notice.xml b/java/res/values-lv/strings-config-important-notice.xml
index ce2062d..916128c 100644
--- a/java/res/values-lv/strings-config-important-notice.xml
+++ b/java/res/values-lv/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Izmantojiet saziņu un ievadītos datus, lai uzlabotu ieteikumus."</string>
 </resources>
diff --git a/java/res/values-mn-rMN/strings-config-important-notice.xml b/java/res/values-mn-rMN/strings-config-important-notice.xml
index 047cfc7..fb07ace 100644
--- a/java/res/values-mn-rMN/strings-config-important-notice.xml
+++ b/java/res/values-mn-rMN/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Зөвлөмжүүдийг сайжруулахын тулд таны харилцсан, бичсэн зүйлсээс суралцана"</string>
 </resources>
diff --git a/java/res/values-ms-rMY/strings-config-important-notice.xml b/java/res/values-ms-rMY/strings-config-important-notice.xml
index e53ada2..f021fe6 100644
--- a/java/res/values-ms-rMY/strings-config-important-notice.xml
+++ b/java/res/values-ms-rMY/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Perbaik cadangan berdasarkan komunikasi anda dan data yang ditaip"</string>
 </resources>
diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml
index 6fbd38c..33ec065 100644
--- a/java/res/values-ms-rMY/strings.xml
+++ b/java/res/values-ms-rMY/strings.xml
@@ -167,10 +167,10 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Pilih fail kamus untuk dipasang"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Betul-betul pasang fail ini untuk <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Berlaku ralat"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Buang kamus kenalan"</string>
-    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Buang kamus peribadi"</string>
-    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Buang kamus sejarah pengguna"</string>
-    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Buang kamus pemperibadian"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Longgokan kamus kenalan"</string>
+    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Longgokan kamus peribadi"</string>
+    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Longgokan kamus sejarah pengguna"</string>
+    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Longgokan kamus pemperibadian"</string>
     <string name="button_default" msgid="3988017840431881491">"Lalai"</string>
     <string name="setup_welcome_title" msgid="6112821709832031715">"Selamat datang ke <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="setup_welcome_additional_description" msgid="8150252008545768953">"dengan Taipan Gerak Isyarat"</string>
diff --git a/java/res/values-nb/strings-config-important-notice.xml b/java/res/values-nb/strings-config-important-notice.xml
index 8c79eef..f64f415 100644
--- a/java/res/values-nb/strings-config-important-notice.xml
+++ b/java/res/values-nb/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Bruk kommunikasjonen og inndataene dine for å få bedre forslag"</string>
 </resources>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 02f13bb..6a4eaf4 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Velg ordboksfilen du vil installere"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Vil du virkelig installere denne filen for <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Det oppsto en feil"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Tøm kontakter-ordlisten"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Generer ordliste for kontakter"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Tøm den personlige ordlisten"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Tøm brukerlogg-ordlisten"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Tøm tilpasningsordlisten"</string>
diff --git a/java/res/values-ne-rNP/strings-config-important-notice.xml b/java/res/values-ne-rNP/strings-config-important-notice.xml
index 6945a61..71c016a 100644
--- a/java/res/values-ne-rNP/strings-config-important-notice.xml
+++ b/java/res/values-ne-rNP/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"सुझावहरू सुधार गर्न सञ्‍चारहरू र टाइप गरिएको डेटाबाट जान्नुहोस्"</string>
 </resources>
diff --git a/java/res/values-nl/strings-config-important-notice.xml b/java/res/values-nl/strings-config-important-notice.xml
index f77a1f4..096e029 100644
--- a/java/res/values-nl/strings-config-important-notice.xml
+++ b/java/res/values-nl/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Suggesties verbeteren met uw communicatie en getypte gegevens"</string>
 </resources>
diff --git a/java/res/values-pl/strings-config-important-notice.xml b/java/res/values-pl/strings-config-important-notice.xml
index 03da01d..8a78ab0 100644
--- a/java/res/values-pl/strings-config-important-notice.xml
+++ b/java/res/values-pl/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Analizuj wiadomości i wpisywane dane, by ulepszać podpowiedzi"</string>
 </resources>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index 9d46f61..a4a6931 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -167,10 +167,10 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Wybierz plik słownika do zainstalowania"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Czy na pewno zainstalować ten plik dla języka: <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Wystąpił błąd"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Zrzut słownika kontaktów"</string>
-    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Zrzut słownika osobistego"</string>
-    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Zrzut słownika historii użytkownika"</string>
-    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Zrzut słownika personalizacji"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Wyciąg ze słownika kontaktów"</string>
+    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Wyciąg ze słownika osobistego"</string>
+    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Wyciąg ze słownika historii użytk."</string>
+    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Wyciąg ze słownika personalizacji"</string>
     <string name="button_default" msgid="3988017840431881491">"Domyślne"</string>
     <string name="setup_welcome_title" msgid="6112821709832031715">"Witamy w aplikacji <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="setup_welcome_additional_description" msgid="8150252008545768953">"z pisaniem gestami"</string>
diff --git a/java/res/values-pt-rPT/strings-config-important-notice.xml b/java/res/values-pt-rPT/strings-config-important-notice.xml
index 0724173..7cc3959 100644
--- a/java/res/values-pt-rPT/strings-config-important-notice.xml
+++ b/java/res/values-pt-rPT/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Aprender com comunicações e dados introd. para melhorar sugestões"</string>
 </resources>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index 8adaa4d..4311dcd 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Selecione um ficheiro de dicionário para instalar"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Instalar mesmo este ficheiro para <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Ocorreu um erro"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Descarregar dicionário de contactos"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Transferir dicionário de contactos"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Descarregar dicionário pessoal"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Desc. dicion. do hist. do utiliz."</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Descarregar dicionário de personal."</string>
diff --git a/java/res/values-pt/strings-config-important-notice.xml b/java/res/values-pt/strings-config-important-notice.xml
index 041a04c..05fe4d1 100644
--- a/java/res/values-pt/strings-config-important-notice.xml
+++ b/java/res/values-pt/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Aprender com mensagens e dados digitados para melhorar sugestões"</string>
 </resources>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 966c402..5bbab46 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Selecione um arquivo de dicionário para instalar"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Deseja instalar este arquivo para <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Ocorreu um erro"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Despejar dicionário de contatos"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Listar dicionário de contatos"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Despejar dicionário pessoal"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Despejar dicio. de hist. do usuário"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Despejar dicion. de personalização"</string>
diff --git a/java/res/values-ro/strings-config-important-notice.xml b/java/res/values-ro/strings-config-important-notice.xml
index ff06457..2de8dde 100644
--- a/java/res/values-ro/strings-config-important-notice.xml
+++ b/java/res/values-ro/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
-    <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Utilizați mesajele și datele introduse pt. a îmbunătăți sugestiile"</string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
+    <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Utilizează mesajele și datele introduse pt. a îmbunătăți sugestiile"</string>
 </resources>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index ac713d9..b9c8977 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -167,10 +167,10 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Selectați un fișier dicționar de instalat"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Doriți să instalați acest fișier pentru <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"A apărut o eroare"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Eliminați dicționar pers. cont."</string>
-    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Eliminați dicționar personal"</string>
-    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Eliminați dicționar istoric utiliz."</string>
-    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Eliminați dicționar personalizare"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Listați dicționar persoane contact"</string>
+    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Listați dicționar personal"</string>
+    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Listați dicționar istoric utilizator"</string>
+    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Listați dicționar personalizare"</string>
     <string name="button_default" msgid="3988017840431881491">"Prestabilit"</string>
     <string name="setup_welcome_title" msgid="6112821709832031715">"Bun venit la <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="setup_welcome_additional_description" msgid="8150252008545768953">"cu Tastarea gestuală"</string>
diff --git a/java/res/values-ru/strings-config-important-notice.xml b/java/res/values-ru/strings-config-important-notice.xml
index b2f215c..3382804 100644
--- a/java/res/values-ru/strings-config-important-notice.xml
+++ b/java/res/values-ru/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Устройство будет запоминать то, что вы вводите чаще всего"</string>
 </resources>
diff --git a/java/res/values-sk/strings-config-important-notice.xml b/java/res/values-sk/strings-config-important-notice.xml
index 00365ab..4adb406 100644
--- a/java/res/values-sk/strings-config-important-notice.xml
+++ b/java/res/values-sk/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Zlepšovať návrhy na základe komunikácie a zadaných údajov"</string>
 </resources>
diff --git a/java/res/values-sl/strings-config-important-notice.xml b/java/res/values-sl/strings-config-important-notice.xml
index fd14a6a..29c5351 100644
--- a/java/res/values-sl/strings-config-important-notice.xml
+++ b/java/res/values-sl/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Vaša sporočila in vnesene podatke uporabi za boljše predloge"</string>
 </resources>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 650b69f..3dabb7e 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Izberite datoteko slovarja, ki jo želite namestiti"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Zares želite namestiti to datoteko za ta jezik: <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Prišlo je do napake"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Izvoz slovarja stikov"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Izpis slovarja stikov"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Izvoz osebnega slovarja"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Izvoz slovarja zgodovine uporabnika"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Izvoz slovarja za prilagajanje"</string>
diff --git a/java/res/values-sr/strings-config-important-notice.xml b/java/res/values-sr/strings-config-important-notice.xml
index a6eb25e..e226a06 100644
--- a/java/res/values-sr/strings-config-important-notice.xml
+++ b/java/res/values-sr/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Користи комуникације и унете податке ради побољшања предлога"</string>
 </resources>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 782830d..af8760f 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -167,10 +167,10 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Избор датотеке речника за инсталирање"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Желите ли стварно да инсталирате ову датотеку за <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Дошло је до грешке"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Избриши речник контаката"</string>
-    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Избриши лични речник"</string>
-    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Избриши речник историје корисника"</string>
-    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Избриши персонализовани речник"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Направи сирову копију речника контаката"</string>
+    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Направи сирову копију личног речника"</string>
+    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Направи сирову копију речника историје корисника"</string>
+    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Направи сирову копију персонализованог речника"</string>
     <string name="button_default" msgid="3988017840431881491">"Подразумевано"</string>
     <string name="setup_welcome_title" msgid="6112821709832031715">"Добро дошли у <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="setup_welcome_additional_description" msgid="8150252008545768953">"помоћу Куцања покретима"</string>
diff --git a/java/res/values-sv/strings-config-important-notice.xml b/java/res/values-sv/strings-config-important-notice.xml
index 8d8a089..d484d67 100644
--- a/java/res/values-sv/strings-config-important-notice.xml
+++ b/java/res/values-sv/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Få bättre förslag genom att använda tidigare angiven data och annan kommunikation"</string>
 </resources>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 22a5ce3..cda89b8 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Välj en ordboksfil att installera"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Vill du verkligen installera filen för <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Ett fel uppstod"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Dumpa ordlista för kontakter"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Ta fram ordlista för kontakter"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Dumpa personlig ordlista"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Dumpa ordlista för användarhistorik"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Dumpa anpassad ordlista"</string>
diff --git a/java/res/values-sw/strings-config-important-notice.xml b/java/res/values-sw/strings-config-important-notice.xml
index 10eab64..8a74bdd 100644
--- a/java/res/values-sw/strings-config-important-notice.xml
+++ b/java/res/values-sw/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Jifunze kutoka kwenye mawasiliano yako na data iliyocharazwa ili kuboresha mapendekezo"</string>
 </resources>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index 50c6aaa..587e95a 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Chagua faili ya kamusi ya kusakinisha"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Ungependa kusakinisha faili hii ya <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Kulikuwa na hitilafu"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Tupa kamusi ya anwani"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Onyesha orodha ya anwani"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Tupa kamusi ya kibinafsi"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Tupa kamusi ya historia ya mtumiaji"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Tupa kamusi ya kuwekewa mapendeleo"</string>
diff --git a/java/res/values-th/strings-config-important-notice.xml b/java/res/values-th/strings-config-important-notice.xml
index 78342ae..e156d5f 100644
--- a/java/res/values-th/strings-config-important-notice.xml
+++ b/java/res/values-th/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"เรียนรู้จากการสื่อสารและข้อมูลที่พิมพ์ของคุณเพื่อปรับปรุงคำแนะนำ"</string>
 </resources>
diff --git a/java/res/values-tl/strings-config-important-notice.xml b/java/res/values-tl/strings-config-important-notice.xml
index 687f861..5e0c0b3 100644
--- a/java/res/values-tl/strings-config-important-notice.xml
+++ b/java/res/values-tl/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Pahusayin ang suhestiyon batay sa iyong pag-uusap at na-type na data"</string>
 </resources>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index ac3cb1c..c0503a3 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -167,14 +167,10 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Pumili ng file ng diksyunaryo na ii-install"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Talagang ii-install ang file na ito para sa <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Nagkaroon ng error"</string>
-    <!-- no translation found for prefs_dump_contacts_dict (7227327764402323097) -->
-    <skip />
-    <!-- no translation found for prefs_dump_user_dict (294870685041741951) -->
-    <skip />
-    <!-- no translation found for prefs_dump_user_history_dict (6821075152449554628) -->
-    <skip />
-    <!-- no translation found for prefs_dump_personalization_dict (7558387996151745284) -->
-    <skip />
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Diksyunaryo ng contacts ng Dump"</string>
+    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Personal na diksyunaryo ng Dump"</string>
+    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Diksyunaryo ng user history ng Dump"</string>
+    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Personalization dictionary ng Dump"</string>
     <string name="button_default" msgid="3988017840431881491">"Default"</string>
     <string name="setup_welcome_title" msgid="6112821709832031715">"Maligayang pagdating sa <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="setup_welcome_additional_description" msgid="8150252008545768953">"gamit ang Gesture na Pag-type"</string>
diff --git a/java/res/values-tr/strings-config-important-notice.xml b/java/res/values-tr/strings-config-important-notice.xml
index 06c8378..28e333d 100644
--- a/java/res/values-tr/strings-config-important-notice.xml
+++ b/java/res/values-tr/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
-    <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Önerileri iyileştirmek için iletişimlerimden ve yazılan verilerden öğren"</string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
+    <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Önerileri iyileştirmek için iletşmlrmdn. ve yazılan verilerden öğren"</string>
 </resources>
diff --git a/java/res/values-uk/strings-config-important-notice.xml b/java/res/values-uk/strings-config-important-notice.xml
index 3d6a4e7..6172b34 100644
--- a/java/res/values-uk/strings-config-important-notice.xml
+++ b/java/res/values-uk/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Пристрій буде запам’ятовувати, що ви пишете, надсилаєте й отримуєте"</string>
 </resources>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index 1ed94ad..ee9208c 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -167,10 +167,10 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Вибрати файл словника, який потрібно встановити"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Справді встановити цей файл для такої мови: <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Сталася помилка"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Дамп словника контактів"</string>
-    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Дамп особистого словника"</string>
-    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Дамп словника історії користувача"</string>
-    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Дамп словника персоналізації"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Вивантаження словника контактів"</string>
+    <string name="prefs_dump_user_dict" msgid="294870685041741951">"Вивантаження особистого словника"</string>
+    <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Вивантаж. словника користув. історії"</string>
+    <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Вивантаження словника персоналізації"</string>
     <string name="button_default" msgid="3988017840431881491">"За умовчанням"</string>
     <string name="setup_welcome_title" msgid="6112821709832031715">"Вітаємо в програмі <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="setup_welcome_additional_description" msgid="8150252008545768953">"з функцією Ввід жестами"</string>
diff --git a/java/res/values-vi/strings-config-important-notice.xml b/java/res/values-vi/strings-config-important-notice.xml
index 6528f06..b753e18 100644
--- a/java/res/values-vi/strings-config-important-notice.xml
+++ b/java/res/values-vi/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Tìm hiểu từ thư từ trao đổi và dữ liệu đã nhập của bạn để cải tiến đề xuất"</string>
 </resources>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index c0ccc7c..112bc47 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Chọn tệp từ điển để cài đặt"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Thực sự cài đặt tệp này cho <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Đã xảy ra lỗi"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Lưu vào từ điển danh bạ"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Tạo từ điển danh bạ"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Lưu vào từ điển cá nhân"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Lưu vào từ điển lịch sử người dùng"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Lưu vào từ điển cá nhân hóa"</string>
diff --git a/java/res/values-zh-rCN/strings-config-important-notice.xml b/java/res/values-zh-rCN/strings-config-important-notice.xml
index 2ffe7d9..2394e31 100644
--- a/java/res/values-zh-rCN/strings-config-important-notice.xml
+++ b/java/res/values-zh-rCN/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"根据您的通信记录和以往输入的数据来完善建议"</string>
 </resources>
diff --git a/java/res/values-zh-rHK/strings-config-important-notice.xml b/java/res/values-zh-rHK/strings-config-important-notice.xml
index 9e80655..3784c76 100644
--- a/java/res/values-zh-rHK/strings-config-important-notice.xml
+++ b/java/res/values-zh-rHK/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"根據您的通訊記錄和已輸入的資料改善建議"</string>
 </resources>
diff --git a/java/res/values-zh-rTW/strings-config-important-notice.xml b/java/res/values-zh-rTW/strings-config-important-notice.xml
index f1bdc77..692d61c 100644
--- a/java/res/values-zh-rTW/strings-config-important-notice.xml
+++ b/java/res/values-zh-rTW/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"根據您的通訊紀錄和以往輸入的資料改善建議項目"</string>
 </resources>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 8a56f28..1fdf7e8 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"選取要安裝的字典檔案"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"確定要安裝這個<xliff:g id="LANGUAGE_NAME">%s</xliff:g>檔案嗎?"</string>
     <string name="error" msgid="8940763624668513648">"發生錯誤"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"捨棄聯絡人字典"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"傾印聯絡人字典"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"捨棄個人字典"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"捨棄使用者紀錄字典"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"捨棄個人化字典"</string>
diff --git a/java/res/values-zu/strings-config-important-notice.xml b/java/res/values-zu/strings-config-important-notice.xml
index 95d5a29..245b056 100644
--- a/java/res/values-zu/strings-config-important-notice.xml
+++ b/java/res/values-zu/strings-config-important-notice.xml
@@ -20,7 +20,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="important_notice_title" msgid="1836002733109536160"></string>
-    <string name="important_notice_contents" msgid="897137043719116217"></string>
+  <string-array name="important_notice_title_array">
+  </string-array>
+  <string-array name="important_notice_contents_array">
+  </string-array>
     <string name="use_personalized_dicts_summary" msgid="590432261305469627">"Funda kusukela kwezokuxhumana zakho nedatha ethayiphiwe ukuze uthuthukise iziphakamiso"</string>
 </resources>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index 42430ba..f4c0d92 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -167,7 +167,7 @@
     <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Khetha ifayela lesichazamazwi ukuze ulifake"</string>
     <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"Fakela ngempela leli fayela i-<xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string>
     <string name="error" msgid="8940763624668513648">"Kube nephutha"</string>
-    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Lahla isichazamazwi soxhumana nabo"</string>
+    <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Yenza uhlu isichazamazwi soxhumana nabo"</string>
     <string name="prefs_dump_user_dict" msgid="294870685041741951">"Lahla isichazamazwi somuntu siqu"</string>
     <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Lahla isichazamazwi somlando womsebenzisi"</string>
     <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Lahla isichazamazwi sokwenza kube ngokwakho"</string>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 0550606..475e92f 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -175,7 +175,7 @@
     </declare-styleable>
 
     <declare-styleable name="SuggestionStripView">
-        <attr name="suggestionStripOption" format="integer">
+        <attr name="suggestionStripOptions" format="integer">
             <!-- This should be aligned with SuggestionStripLayoutHelper.AUTO_CORRECT_* and etc. -->
             <flag name="autoCorrectBold" value="0x01" />
             <flag name="autoCorrectUnderline" value="0x02" />
diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml
index 93f25a7..5171744 100644
--- a/java/res/values/colors.xml
+++ b/java/res/values/colors.xml
@@ -39,6 +39,7 @@
     <color name="typed_word_color_ics">#D833B5E5</color>
     <color name="suggested_word_color_ics">#B233B5E5</color>
     <color name="highlight_translucent_color_ics">#9933B5E5</color>
+    <color name="key_text_color_holo">@android:color/white</color>
     <color name="key_text_shadow_color_holo">@android:color/transparent</color>
     <color name="key_text_inactivated_color_holo">#66E0E4E5</color>
     <color name="key_hint_letter_color_holo">#80000000</color>
@@ -65,7 +66,4 @@
     <!-- TODO: Color which should be included in the theme -->
     <color name="emoji_key_background_color">#00000000</color>
     <color name="emoji_key_pressed_background_color">#30FFFFFF</color>
-
-    <color name="key_text_color_normal_holo">@android:color/white</color>
-    <color name="key_text_color_functional_holo">@android:color/white</color>
 </resources>
diff --git a/java/res/values/themes-gb.xml b/java/res/values/themes-gb.xml
index a460d4f..c189da3 100644
--- a/java/res/values/themes-gb.xml
+++ b/java/res/values/themes-gb.xml
@@ -133,7 +133,7 @@
         parent="SuggestionStripView"
     >
         <item name="android:background">@drawable/keyboard_suggest_strip_gb</item>
-        <item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
+        <item name="suggestionStripOptions">autoCorrectBold|validTypedWordBold</item>
         <item name="colorValidTypedWord">@color/highlight_color_gb</item>
         <item name="colorTypedWord">@color/typed_word_color_gb</item>
         <item name="colorAutoCorrect">@color/highlight_color_gb</item>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index caea921..720eda9 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -112,7 +112,7 @@
         parent="SuggestionStripView"
     >
         <item name="android:background">@drawable/keyboard_suggest_strip_holo</item>
-        <item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
+        <item name="suggestionStripOptions">autoCorrectBold|validTypedWordBold</item>
         <item name="colorValidTypedWord">@color/typed_word_color_ics</item>
         <item name="colorTypedWord">@color/typed_word_color_ics</item>
         <item name="colorAutoCorrect">@color/highlight_color_ics</item>
diff --git a/java/res/values/themes-klp.xml b/java/res/values/themes-klp.xml
index 0599fb6..8305271 100644
--- a/java/res/values/themes-klp.xml
+++ b/java/res/values/themes-klp.xml
@@ -112,7 +112,7 @@
         parent="SuggestionStripView"
     >
         <item name="android:background">@drawable/keyboard_suggest_strip_holo</item>
-        <item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
+        <item name="suggestionStripOptions">autoCorrectBold|validTypedWordBold</item>
         <item name="colorValidTypedWord">@color/typed_word_color_klp</item>
         <item name="colorTypedWord">@color/typed_word_color_klp</item>
         <item name="colorAutoCorrect">@color/highlight_color_klp</item>
diff --git a/java/res/xml/row_pcqwerty5.xml b/java/res/xml/row_pcqwerty5.xml
index a72f388..f6438ab 100644
--- a/java/res/xml/row_pcqwerty5.xml
+++ b/java/res/xml/row_pcqwerty5.xml
@@ -64,18 +64,8 @@
             latin:keyStyle="defaultEnterKeyStyle"
             latin:keySpec="!icon/enter_key|!code/key_enter"
             latin:keyWidth="15.384%p" />
-        <switch>
-            <case
-                latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
-            >
-                <Spacer />
-            </case>
-            <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
-            <default>
-                <Key
-                    latin:keyStyle="emojiKeyStyle"
-                    latin:keyWidth="fillRight" />
-            </default>
-        </switch>
+        <Key
+            latin:keyStyle="emojiKeyStyle"
+            latin:keyWidth="fillRight" />
     </Row>
 </merge>
diff --git a/java/res/xml/rowkeys_symbols_shift1.xml b/java/res/xml/rowkeys_symbols_shift1.xml
index 7cb3213..4c2722c 100644
--- a/java/res/xml/rowkeys_symbols_shift1.xml
+++ b/java/res/xml/rowkeys_symbols_shift1.xml
@@ -34,11 +34,11 @@
     <!-- U+221A: "√" SQUARE ROOT -->
     <Key
         latin:keySpec="&#x221A;" />
-    <!-- U+03A0: "Π" GREEK CAPITAL LETTER PI
-         U+03C0: "π" GREEK SMALL LETTER PI  -->
+    <!-- U+03C0: "π" GREEK SMALL LETTER PI
+         U+03A0: "Π" GREEK CAPITAL LETTER PI -->
     <Key
-        latin:keySpec="&#x03A0;"
-        latin:moreKeys="&#x03C0;" />
+        latin:keySpec="&#x03C0;"
+        latin:moreKeys="&#x03A0;" />
     <!-- U+00F7: "÷" DIVISION SIGN -->
     <Key
         latin:keySpec="&#x00F7;" />
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index 4c53b52..7fff46a 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -25,7 +25,6 @@
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.Rect;
-import android.graphics.Typeface;
 import android.os.Build;
 import android.os.CountDownTimer;
 import android.preference.PreferenceManager;
@@ -49,6 +48,8 @@
 import com.android.inputmethod.keyboard.internal.DynamicGridKeyboard;
 import com.android.inputmethod.keyboard.internal.EmojiLayoutParams;
 import com.android.inputmethod.keyboard.internal.EmojiPageKeyboardView;
+import com.android.inputmethod.keyboard.internal.KeyDrawParams;
+import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SubtypeSwitcher;
@@ -634,20 +635,23 @@
         // TODO:
     }
 
-    // Hack: These parameters are hacky.
-    public void startEmojiPalettes(final String switchToAlphaLabel, final int switchToAlphaColor,
-            final float switchToAlphaSize, final Typeface switchToAlphaTypeface) {
+    private static void setupAlphabetKey(final TextView alphabetKey, final String label,
+            final KeyDrawParams params) {
+        alphabetKey.setText(label);
+        alphabetKey.setTextColor(params.mTextColor);
+        alphabetKey.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mLabelSize);
+        alphabetKey.setTypeface(params.mTypeface);
+    }
+
+    public void startEmojiPalettes(final String switchToAlphaLabel,
+            final KeyVisualAttributes keyVisualAttr) {
         if (DEBUG_PAGER) {
             Log.d(TAG, "allocate emoji palettes memory " + mCurrentPagerPosition);
         }
-        mAlphabetKeyLeft.setText(switchToAlphaLabel);
-        mAlphabetKeyLeft.setTextColor(switchToAlphaColor);
-        mAlphabetKeyLeft.setTextSize(TypedValue.COMPLEX_UNIT_PX, switchToAlphaSize);
-        mAlphabetKeyLeft.setTypeface(switchToAlphaTypeface);
-        mAlphabetKeyRight.setText(switchToAlphaLabel);
-        mAlphabetKeyRight.setTextColor(switchToAlphaColor);
-        mAlphabetKeyRight.setTextSize(TypedValue.COMPLEX_UNIT_PX, switchToAlphaSize);
-        mAlphabetKeyRight.setTypeface(switchToAlphaTypeface);
+        final KeyDrawParams params = new KeyDrawParams();
+        params.updateParams(mEmojiLayoutParams.getActionBarHeight(), keyVisualAttr);
+        setupAlphabetKey(mAlphabetKeyLeft, switchToAlphaLabel, params);
+        setupAlphabetKey(mAlphabetKeyRight, switchToAlphaLabel, params);
         mEmojiPager.setAdapter(mEmojiPalettesAdapter);
         mEmojiPager.setCurrentItem(mCurrentPagerPosition);
     }
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index ceda9ee..816a943 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -137,8 +137,6 @@
 
     private final OptionalAttributes mOptionalAttributes;
 
-    private static final int DEFAULT_TEXT_COLOR = 0xFFFFFFFF;
-
     private static final class OptionalAttributes {
         /** Text to output when pressed. This can be multiple characters, like ".com" */
         public final String mOutputText;
@@ -585,22 +583,7 @@
     }
 
     public final int selectTextColor(final KeyDrawParams params) {
-        if (isShiftedLetterActivated()) {
-            return params.mTextInactivatedColor;
-        }
-        if (params.mTextColorStateList == null) {
-            return DEFAULT_TEXT_COLOR;
-        }
-        final int[] state;
-        // TODO: Hack!!!!!!!! Consider having a new attribute for the functional text labels.
-        // Currently, we distinguish "input key" from "functional key" by checking the
-        // length of the label( > 1) and "functional" attributes (= true).
-        if (mLabel != null && mLabel.length() > 1) {
-            state = getCurrentDrawableState();
-        } else {
-            state = KEY_STATE_NORMAL;
-        }
-        return params.mTextColorStateList.getColorForState(state, DEFAULT_TEXT_COLOR);
+        return isShiftedLetterActivated() ? params.mTextInactivatedColor : params.mTextColor;
     }
 
     public final int selectHintTextSize(final KeyDrawParams params) {
@@ -733,10 +716,14 @@
         return (attrs != null) ? attrs.mAltCode : CODE_UNSPECIFIED;
     }
 
+    public int getIconId() {
+        return mIconId;
+    }
+
     public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) {
         final OptionalAttributes attrs = mOptionalAttributes;
         final int disabledIconId = (attrs != null) ? attrs.mDisabledIconId : ICON_UNDEFINED;
-        final int iconId = mEnabled ? mIconId : disabledIconId;
+        final int iconId = mEnabled ? getIconId() : disabledIconId;
         final Drawable icon = iconSet.getIconDrawable(iconId);
         if (icon != null) {
             icon.setAlpha(alpha);
@@ -748,7 +735,7 @@
         final OptionalAttributes attrs = mOptionalAttributes;
         final int previewIconId = (attrs != null) ? attrs.mPreviewIconId : ICON_UNDEFINED;
         return previewIconId != ICON_UNDEFINED
-                ? iconSet.getIconDrawable(previewIconId) : iconSet.getIconDrawable(mIconId);
+                ? iconSet.getIconDrawable(previewIconId) : iconSet.getIconDrawable(getIconId());
     }
 
     public int getWidth() {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 03d9def..87368d4 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -86,6 +86,9 @@
      * @return the key that the touch point hits.
      */
     public Key detectHitKey(final int x, final int y) {
+        if (mKeyboard == null) {
+            return null;
+        }
         final int touchX = getTouchX(x);
         final int touchY = getTouchY(y);
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 6215e27..2711d24 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
-import android.graphics.Paint;
 import android.preference.PreferenceManager;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
@@ -31,7 +30,7 @@
 import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
 import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
 import com.android.inputmethod.keyboard.internal.KeyboardState;
-import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.keyboard.internal.KeyboardTextsSet;
 import com.android.inputmethod.latin.InputView;
 import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.LatinImeLogger;
@@ -46,7 +45,7 @@
 public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
     private static final String TAG = KeyboardSwitcher.class.getSimpleName();
 
-    static final class KeyboardTheme {
+    public static final class KeyboardTheme {
         public final int mThemeId;
         public final int mStyleId;
 
@@ -81,8 +80,9 @@
     private KeyboardState mState;
 
     private KeyboardLayoutSet mKeyboardLayoutSet;
+    // TODO: The following {@link KeyboardTextsSet} should be in {@link KeyboardLayoutSet}.
+    private final KeyboardTextsSet mKeyboardTextsSet = new KeyboardTextsSet();
     private SettingsValues mCurrentSettingsValues;
-    private Key mSwitchToAlphaKey;
 
     /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
      * what user actually typed. */
@@ -163,8 +163,7 @@
         mCurrentSettingsValues = settingsValues;
         try {
             mState.onLoadKeyboard();
-            final Keyboard symbols = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS);
-            mSwitchToAlphaKey = symbols.getKey(Constants.CODE_SWITCH_ALPHA_SYMBOL);
+            mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocale());
         } catch (KeyboardLayoutSetException e) {
             Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
             LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause());
@@ -290,10 +289,9 @@
     @Override
     public void setEmojiKeyboard() {
         mMainKeyboardFrame.setVisibility(View.GONE);
-        final Paint paint = mKeyboardView.newLabelPaint(mSwitchToAlphaKey);
         mEmojiPalettesView.startEmojiPalettes(
-                mSwitchToAlphaKey.getLabel(), paint.getColor(), paint.getTextSize(),
-                paint.getTypeface());
+                mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL),
+                mKeyboardView.getKeyVisualAttribute());
         mEmojiPalettesView.setVisibility(View.VISIBLE);
     }
 
@@ -342,10 +340,6 @@
         mState.onCodeInput(code, mLatinIME.getCurrentAutoCapsState());
     }
 
-    private boolean isShowingMainKeyboard() {
-        return null != mKeyboardView && mKeyboardView.isShown();
-    }
-
     public boolean isShowingEmojiPalettes() {
         return mEmojiPalettesView != null && mEmojiPalettesView.isShown();
     }
@@ -378,10 +372,6 @@
         }
     }
 
-    public boolean isShowingMainKeyboardOrEmojiPalettes() {
-        return isShowingMainKeyboard() || isShowingEmojiPalettes();
-    }
-
     public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
         if (mKeyboardView != null) {
             mKeyboardView.closing();
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index dd3ab9c..18e51d3 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -146,6 +146,10 @@
         mPaint.setAntiAlias(true);
     }
 
+    public KeyVisualAttributes getKeyVisualAttribute() {
+        return mKeyVisualAttributes;
+    }
+
     private static void blendAlpha(final Paint paint, final int alpha) {
         final int color = paint.getColor();
         paint.setARGB((paint.getAlpha() * alpha) / Constants.Color.ALPHA_OPAQUE,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java
index 12e0632..d57ea5a 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java
@@ -73,9 +73,13 @@
         ll.setLayoutParams(lp);
     }
 
+    public int getActionBarHeight() {
+        return mEmojiActionBarHeight - mBottomPadding;
+    }
+
     public void setActionBarProperties(final LinearLayout ll) {
         final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams();
-        lp.height = mEmojiActionBarHeight - mBottomPadding;
+        lp.height = getActionBarHeight();
         ll.setLayoutParams(lp);
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
index b528b69..1716fa0 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
@@ -16,7 +16,6 @@
 
 package com.android.inputmethod.keyboard.internal;
 
-import android.content.res.ColorStateList;
 import android.graphics.Typeface;
 
 import com.android.inputmethod.latin.utils.ResourceUtils;
@@ -33,7 +32,7 @@
     public int mHintLabelSize;
     public int mPreviewTextSize;
 
-    public ColorStateList mTextColorStateList;
+    public int mTextColor;
     public int mTextInactivatedColor;
     public int mTextShadowColor;
     public int mHintLetterColor;
@@ -58,7 +57,7 @@
         mHintLabelSize = copyFrom.mHintLabelSize;
         mPreviewTextSize = copyFrom.mPreviewTextSize;
 
-        mTextColorStateList = copyFrom.mTextColorStateList;
+        mTextColor = copyFrom.mTextColor;
         mTextInactivatedColor = copyFrom.mTextInactivatedColor;
         mTextShadowColor = copyFrom.mTextShadowColor;
         mHintLetterColor = copyFrom.mHintLetterColor;
@@ -90,8 +89,8 @@
                 attr.mShiftedLetterHintRatio, mShiftedLetterHintSize);
         mHintLabelSize = selectTextSize(keyHeight, attr.mHintLabelRatio, mHintLabelSize);
         mPreviewTextSize = selectTextSize(keyHeight, attr.mPreviewTextRatio, mPreviewTextSize);
-        mTextColorStateList =
-                attr.mTextColorStateList != null ? attr.mTextColorStateList : mTextColorStateList;
+
+        mTextColor = selectColor(attr.mTextColor, mTextColor);
         mTextInactivatedColor = selectColor(attr.mTextInactivatedColor, mTextInactivatedColor);
         mTextShadowColor = selectColor(attr.mTextShadowColor, mTextShadowColor);
         mHintLetterColor = selectColor(attr.mHintLetterColor, mHintLetterColor);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
index c3e0aa6..df386fc 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
@@ -16,7 +16,6 @@
 
 package com.android.inputmethod.keyboard.internal;
 
-import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Typeface;
 import android.util.SparseIntArray;
@@ -38,7 +37,7 @@
     public final float mHintLabelRatio;
     public final float mPreviewTextRatio;
 
-    public final ColorStateList mTextColorStateList;
+    public final int mTextColor;
     public final int mTextInactivatedColor;
     public final int mTextShadowColor;
     public final int mHintLetterColor;
@@ -119,7 +118,7 @@
         mPreviewTextRatio = ResourceUtils.getFraction(keyAttr,
                 R.styleable.Keyboard_Key_keyPreviewTextRatio);
 
-        mTextColorStateList = keyAttr.getColorStateList(R.styleable.Keyboard_Key_keyTextColor);
+        mTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextColor, 0);
         mTextInactivatedColor = keyAttr.getColor(
                 R.styleable.Keyboard_Key_keyTextInactivatedColor, 0);
         mTextShadowColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextShadowColor, 0);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
index da8bf7d..79d088f 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
@@ -102,7 +102,7 @@
         return isValidIconId(iconId) ? ICON_NAMES[iconId] : "unknown<" + iconId + ">";
     }
 
-    static int getIconId(final String name) {
+    public static int getIconId(final String name) {
         Integer iconId = sNameToIdsMap.get(name);
         if (iconId != null) {
             return iconId;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index 89221ba..976038c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -29,6 +29,8 @@
 
 public final class KeyboardTextsSet {
     public static final String PREFIX_TEXT = "!text/";
+    public static final String SWITCH_TO_ALPHA_KEY_LABEL = "label_to_alpha_key";
+
     private static final char BACKSLASH = Constants.CODE_BACKSLASH;
     private static final int MAX_STRING_REFERENCE_INDIRECTION = 10;
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
index 96acb15..ed2db07 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
@@ -90,7 +90,7 @@
         /*   8:20 */ "more_keys_for_s",
         /*   9:20 */ "more_keys_for_n",
         /*  10:20 */ "label_to_alpha_key",
-        /*  11:15 */ "more_keys_for_y",
+        /*  11:14 */ "more_keys_for_y",
         /*  12:13 */ "more_keys_for_d",
         /*  13:12 */ "more_keys_for_z",
         /*  14:10 */ "more_keys_for_t",
@@ -3585,7 +3585,7 @@
     private static final Object[] LANGUAGES_AND_TEXTS = {
     // "locale", TEXT_ARRAY,  /* numberOfNonNullText/lengthOf_TEXT_ARRAY localeName */
         "DEFAULT", LANGUAGE_DEFAULT, /* 171/171 default */
-        "af", LANGUAGE_af,    /*   8/ 12 Afrikaans */
+        "af", LANGUAGE_af,    /*   7/ 12 Afrikaans */
         "ar", LANGUAGE_ar,    /*  58/110 Arabic */
         "az", LANGUAGE_az_AZ, /*   8/ 17 Azerbaijani (Azerbaijan) */
         "be", LANGUAGE_be_BY, /*  10/ 33 Belarusian (Belarus) */
diff --git a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
deleted file mode 100644
index 1c6a14e..0000000
--- a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.inputmethod.latin;
-
-import android.util.Log;
-
-import com.android.inputmethod.latin.makedict.DictEncoder;
-import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.makedict.Ver4DictEncoder;
-import com.android.inputmethod.latin.utils.FileUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Map;
-
-abstract public class AbstractDictionaryWriter {
-    /** Used for Log actions from this class */
-    private static final String TAG = AbstractDictionaryWriter.class.getSimpleName();
-
-    public AbstractDictionaryWriter() {
-    }
-
-    abstract public void clear();
-
-    /**
-     * Add a unigram with an optional shortcut to the dictionary.
-     * @param word The word to add.
-     * @param shortcutTarget A shortcut target for this word, or null if none.
-     * @param frequency The frequency for this unigram.
-     * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
-     *   if shortcutTarget is null.
-     * @param isNotAWord true if this is not a word, i.e. shortcut only.
-     */
-    abstract public void addUnigramWord(final String word, final String shortcutTarget,
-            final int frequency, final int shortcutFreq, final boolean isNotAWord);
-
-    // TODO: Remove lastModifiedTime after making binary dictionary support forgetting curve.
-    abstract public void addBigramWords(final String word0, final String word1,
-            final int frequency, final boolean isValid, final long lastModifiedTime);
-
-    abstract public void removeBigramWords(final String word0, final String word1);
-
-    abstract protected void writeDictionary(final DictEncoder dictEncoder,
-            final Map<String, String> attributeMap) throws IOException, UnsupportedFormatException;
-
-    public void write(final File file, final Map<String, String> attributeMap) {
-        try {
-            FileUtils.deleteRecursively(file);
-            file.mkdir();
-            final DictEncoder dictEncoder = new Ver4DictEncoder(file);
-            writeDictionary(dictEncoder, attributeMap);
-        } catch (IOException e) {
-            Log.e(TAG, "IO exception while writing file", e);
-        } catch (UnsupportedFormatException e) {
-            Log.e(TAG, "Unsupported format", e);
-        }
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index c450a1d..851ecc0 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -28,9 +28,9 @@
 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 import com.android.inputmethod.latin.makedict.WordProperty;
+import com.android.inputmethod.latin.personalization.PersonalizationHelper;
 import com.android.inputmethod.latin.settings.NativeSuggestOptions;
 import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
 import com.android.inputmethod.latin.utils.LanguageModelParam;
 import com.android.inputmethod.latin.utils.StringUtils;
@@ -508,7 +508,9 @@
      */
     @UsedForTesting
     public static int setCurrentTimeForTest(final int currentTime) {
-        return setCurrentTimeForTestNative(currentTime);
+        final int currentNativeTimestamp = setCurrentTimeForTestNative(currentTime);
+        PersonalizationHelper.currentTimeChangedForTesting(currentNativeTimestamp);
+        return currentNativeTimestamp;
     }
 
     @UsedForTesting
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index a700837..acbd919 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -229,7 +229,7 @@
     private static boolean hackCanUseDictionaryFile(final Locale locale, final File f) {
         try {
             // Read the version of the file
-            final DictDecoder dictDecoder = FormatSpec.getDictDecoder(f);
+            final DictDecoder dictDecoder = FormatSpec.getDictDecoder(f, 0, f.length());
             final DictionaryHeader header = dictDecoder.readHeader();
 
             final String version = header.mDictionaryOptions.mAttributes.get(VERSION_KEY);
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index ae9bdf3f..c2941e4 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -78,7 +78,7 @@
     public ContactsBinaryDictionary(final Context context, final Locale locale,
             final File dictFile) {
         super(context, getDictName(NAME, locale, dictFile), locale, Dictionary.TYPE_CONTACTS,
-                false /* isUpdatable */, dictFile);
+                dictFile);
         mLocale = locale;
         mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
         registerObserver(context);
@@ -114,14 +114,14 @@
     }
 
     @Override
-    public void loadDictionaryAsync() {
-        loadDeviceAccountsEmailAddresses();
-        loadDictionaryAsyncForUri(ContactsContract.Profile.CONTENT_URI);
+    public void loadInitialContentsLocked() {
+        loadDeviceAccountsEmailAddressesLocked();
+        loadDictionaryForUriLocked(ContactsContract.Profile.CONTENT_URI);
         // TODO: Switch this URL to the newer ContactsContract too
-        loadDictionaryAsyncForUri(Contacts.CONTENT_URI);
+        loadDictionaryForUriLocked(Contacts.CONTENT_URI);
     }
 
-    private void loadDeviceAccountsEmailAddresses() {
+    private void loadDeviceAccountsEmailAddressesLocked() {
         final List<String> accountVocabulary =
                 AccountUtils.getDeviceAccountsEmailAddresses(mContext);
         if (accountVocabulary == null || accountVocabulary.isEmpty()) {
@@ -131,12 +131,14 @@
             if (DEBUG) {
                 Log.d(TAG, "loadAccountVocabulary: " + word);
             }
-            super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS, 0 /* shortcutFreq */,
-                    false /* isNotAWord */);
+            runGCIfRequiredLocked(true /* mindsBlockByGC */);
+            addWordDynamicallyLocked(word, FREQUENCY_FOR_CONTACTS, null /* shortcut */,
+                    0 /* shortcutFreq */, false /* isNotAWord */, false /* isBlacklisted */,
+                    BinaryDictionary.NOT_A_VALID_TIMESTAMP);
         }
     }
 
-    private void loadDictionaryAsyncForUri(final Uri uri) {
+    private void loadDictionaryForUriLocked(final Uri uri) {
         Cursor cursor = null;
         try {
             cursor = mContext.getContentResolver().query(uri, PROJECTION, null, null, null);
@@ -145,7 +147,7 @@
             }
             if (cursor.moveToFirst()) {
                 sContactCountAtLastRebuild = getContactCount();
-                addWords(cursor);
+                addWordsLocked(cursor);
             }
         } catch (final SQLiteException e) {
             Log.e(TAG, "SQLiteException in the remote Contacts process.", e);
@@ -166,12 +168,12 @@
         return false;
     }
 
-    private void addWords(final Cursor cursor) {
+    private void addWordsLocked(final Cursor cursor) {
         int count = 0;
         while (!cursor.isAfterLast() && count < MAX_CONTACT_COUNT) {
             String name = cursor.getString(INDEX_NAME);
             if (isValidName(name)) {
-                addName(name);
+                addNameLocked(name);
                 ++count;
             } else {
                 if (DEBUG_DUMP) {
@@ -207,7 +209,7 @@
      * Adds the words in a name (e.g., firstname/lastname) to the binary dictionary along with their
      * bigrams depending on locale.
      */
-    private void addName(final String name) {
+    private void addNameLocked(final String name) {
         int len = StringUtils.codePointCount(name);
         String prevWord = null;
         // TODO: Better tokenization for non-Latin writing systems
@@ -226,13 +228,15 @@
                     if (DEBUG) {
                         Log.d(TAG, "addName " + name + ", " + word + ", " + prevWord);
                     }
-                    super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS,
-                            0 /* shortcutFreq */, false /* isNotAWord */);
-                    if (!TextUtils.isEmpty(prevWord)) {
-                        if (mUseFirstLastBigrams) {
-                            super.addBigram(prevWord, word, FREQUENCY_FOR_CONTACTS_BIGRAM,
-                                    0 /* lastModifiedTime */);
-                        }
+                    runGCIfRequiredLocked(true /* mindsBlockByGC */);
+                    addWordDynamicallyLocked(word, FREQUENCY_FOR_CONTACTS,
+                            null /* shortcut */, 0 /* shortcutFreq */, false /* isNotAWord */,
+                            false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP);
+                    if (!TextUtils.isEmpty(prevWord) && mUseFirstLastBigrams) {
+                        runGCIfRequiredLocked(true /* mindsBlockByGC */);
+                        addBigramDynamicallyLocked(prevWord, word,
+                                FREQUENCY_FOR_CONTACTS_BIGRAM,
+                                BinaryDictionary.NOT_A_VALID_TIMESTAMP);
                     }
                     prevWord = word;
                 }
@@ -258,12 +262,12 @@
     }
 
     @Override
-    protected boolean needsToReloadBeforeWriting() {
+    protected boolean needsToReloadAfterCreation() {
         return true;
     }
 
     @Override
-    protected boolean hasContentChanged() {
+    protected boolean haveContentsChanged() {
         final long startTime = SystemClock.uptimeMillis();
         final int contactCount = getContactCount();
         if (contactCount > MAX_CONTACT_COUNT) {
@@ -291,7 +295,7 @@
             if (cursor.moveToFirst()) {
                 while (!cursor.isAfterLast()) {
                     String name = cursor.getString(INDEX_NAME);
-                    if (isValidName(name) && !isNameInDictionary(name)) {
+                    if (isValidName(name) && !isNameInDictionaryLocked(name)) {
                         if (DEBUG) {
                             Log.d(TAG, "Contact name missing: " + name + " (runtime = "
                                     + (SystemClock.uptimeMillis() - startTime) + " ms)");
@@ -321,7 +325,7 @@
     /**
      * Checks if the words in a name are in the current binary dictionary.
      */
-    private boolean isNameInDictionary(final String name) {
+    private boolean isNameInDictionaryLocked(final String name) {
         int len = StringUtils.codePointCount(name);
         String prevWord = null;
         for (int i = 0; i < len; i++) {
@@ -332,11 +336,11 @@
                 final int wordLen = StringUtils.codePointCount(word);
                 if (wordLen < MAX_WORD_LENGTH && wordLen > 1) {
                     if (!TextUtils.isEmpty(prevWord) && mUseFirstLastBigrams) {
-                        if (!super.isValidBigramLocked(prevWord, word)) {
+                        if (!isValidBigramLocked(prevWord, word)) {
                             return false;
                         }
                     } else {
-                        if (!super.isValidWordLocked(word)) {
+                        if (!isValidWordLocked(word)) {
                             return false;
                         }
                     }
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
index 14e1baf..d6178fc 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
@@ -28,6 +28,7 @@
 import com.android.inputmethod.latin.personalization.UserHistoryDictionary;
 import com.android.inputmethod.latin.settings.SettingsValues;
 import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.ExecutorUtils;
 import com.android.inputmethod.latin.utils.LanguageModelParam;
 
 import java.io.File;
@@ -35,6 +36,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CountDownLatch;
@@ -137,7 +139,8 @@
 
     @UsedForTesting
     public DictionaryFacilitatorForSuggest(final Context context, final Locale locale,
-            final ArrayList<String> dictionaryTypes, final HashMap<String, File> dictionaryFiles) {
+            final ArrayList<String> dictionaryTypes, final HashMap<String, File> dictionaryFiles,
+            final Map<String, Map<String, String>> additionalDictAttributes) {
         mContext = context;
         mLocale = locale;
         mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0);
@@ -154,6 +157,10 @@
                 userHistoryDictionary.reloadDictionaryIfRequired();
                 userHistoryDictionary.waitAllTasksForTests();
                 setUserHistoryDictionary(userHistoryDictionary);
+                if (additionalDictAttributes.containsKey(dictType)) {
+                    userHistoryDictionary.clearAndFlushDictionaryWithAdditionalAttributes(
+                            additionalDictAttributes.get(dictType));
+                }
             } else if (dictType.equals(Dictionary.TYPE_PERSONALIZATION)) {
                 final PersonalizationDictionary personalizationDictionary =
                         PersonalizationHelper.getPersonalizationDictionary(context, locale);
@@ -162,6 +169,10 @@
                 personalizationDictionary.reloadDictionaryIfRequired();
                 personalizationDictionary.waitAllTasksForTests();
                 setPersonalizationDictionary(personalizationDictionary);
+                if (additionalDictAttributes.containsKey(dictType)) {
+                    personalizationDictionary.clearAndFlushDictionaryWithAdditionalAttributes(
+                            additionalDictAttributes.get(dictType));
+                }
             } else if (dictType.equals(Dictionary.TYPE_USER)) {
                 final File file = dictionaryFiles.get(dictType);
                 final UserBinaryDictionary userDictionary = new UserBinaryDictionary(
@@ -204,8 +215,7 @@
         if (listener != null) {
             listener.onUpdateMainDictionaryAvailability(hasMainDictionary());
         }
-        new Thread("InitializeBinaryDictionary") {
-            @Override
+        ExecutorUtils.getExecutor("InitializeBinaryDictionary").execute(new Runnable() {
             public void run() {
                 final DictionaryCollection newMainDict =
                         DictionaryFactory.createMainDictionaryFromManager(context, locale);
@@ -215,7 +225,7 @@
                 }
                 mLatchForWaitingLoadingMainDictionary.countDown();
             }
-        }.start();
+        });
     }
 
     // The main dictionary could have been loaded asynchronously.  Don't cache the return value
diff --git a/java/src/com/android/inputmethod/latin/DictionaryWriter.java b/java/src/com/android/inputmethod/latin/DictionaryWriter.java
deleted file mode 100644
index b931c66..0000000
--- a/java/src/com/android/inputmethod/latin/DictionaryWriter.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.inputmethod.latin;
-
-import com.android.inputmethod.latin.makedict.DictEncoder;
-import com.android.inputmethod.latin.makedict.FormatSpec;
-import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
-import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
-import com.android.inputmethod.latin.makedict.ProbabilityInfo;
-import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.utils.CollectionUtils;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * An in memory dictionary for memorizing entries and writing a binary dictionary.
- */
-public class DictionaryWriter extends AbstractDictionaryWriter {
-    private static final int BINARY_DICT_VERSION = FormatSpec.VERSION4;
-    private static final FormatSpec.FormatOptions FORMAT_OPTIONS =
-            new FormatSpec.FormatOptions(BINARY_DICT_VERSION, false /* hasTimestamp */);
-
-    private FusionDictionary mFusionDictionary;
-
-    public DictionaryWriter() {
-        clear();
-    }
-
-    @Override
-    public void clear() {
-        final HashMap<String, String> attributes = CollectionUtils.newHashMap();
-        mFusionDictionary = new FusionDictionary(new PtNodeArray(),
-                new FusionDictionary.DictionaryOptions(attributes));
-    }
-
-    /**
-     * Adds a word unigram to the fusion dictionary.
-     */
-    // TODO: Create "cache dictionary" to cache fresh words for frequently updated dictionaries,
-    // considering performance regression.
-    @Override
-    public void addUnigramWord(final String word, final String shortcutTarget,
-            final int probability, final int shortcutProbability, final boolean isNotAWord) {
-        if (shortcutTarget == null) {
-            mFusionDictionary.add(word, new ProbabilityInfo(probability), null, isNotAWord);
-        } else {
-            // TODO: Do this in the subclass, with this class taking an arraylist.
-            final ArrayList<WeightedString> shortcutTargets = CollectionUtils.newArrayList();
-            shortcutTargets.add(new WeightedString(shortcutTarget, shortcutProbability));
-            mFusionDictionary.add(word, new ProbabilityInfo(probability), shortcutTargets,
-                    isNotAWord);
-        }
-    }
-
-    @Override
-    public void addBigramWords(final String word0, final String word1, final int probability,
-            final boolean isValid, final long lastModifiedTime) {
-        mFusionDictionary.setBigram(word0, word1, new ProbabilityInfo(probability));
-    }
-
-    @Override
-    public void removeBigramWords(final String word0, final String word1) {
-        // This class don't support removing bigram words.
-    }
-
-    @Override
-    protected void writeDictionary(final DictEncoder dictEncoder,
-            final Map<String, String> attributeMap) throws IOException, UnsupportedFormatException {
-        for (final Map.Entry<String, String> entry : attributeMap.entrySet()) {
-            mFusionDictionary.addOptionAttribute(entry.getKey(), entry.getValue());
-        }
-        dictEncoder.writeDictionary(mFusionDictionary, FORMAT_OPTIONS);
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index f9ab941..26545ac 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -29,9 +29,9 @@
 import com.android.inputmethod.latin.utils.AsyncResultHolder;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.CombinedFormatUtils;
+import com.android.inputmethod.latin.utils.ExecutorUtils;
 import com.android.inputmethod.latin.utils.FileUtils;
 import com.android.inputmethod.latin.utils.LanguageModelParam;
-import com.android.inputmethod.latin.utils.PrioritizedSerialExecutor;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -64,6 +64,9 @@
     private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100;
     private static final int TIMEOUT_FOR_READ_OPS_FOR_TESTS_IN_MILLISECONDS = 10000;
 
+    private static final int DEFAULT_MAX_UNIGRAM_COUNT = 10000;
+    private static final int DEFAULT_MAX_BIGRAM_COUNT = 10000;
+
     /**
      * The maximum length of a word in this dictionary.
      */
@@ -80,9 +83,6 @@
     private static final ConcurrentHashMap<String, DictionaryUpdateController>
             sDictNameDictionaryUpdateControllerMap = CollectionUtils.newConcurrentHashMap();
 
-    private static final ConcurrentHashMap<String, PrioritizedSerialExecutor>
-            sDictNameExecutorMap = CollectionUtils.newConcurrentHashMap();
-
     /** The application context. */
     protected final Context mContext;
 
@@ -92,10 +92,6 @@
      */
     private BinaryDictionary mBinaryDictionary;
 
-    // TODO: Remove and handle dictionaries in native code.
-    /** The in-memory dictionary used to generate the binary dictionary. */
-    protected AbstractDictionaryWriter mDictionaryWriter;
-
     /**
      * The name of this dictionary, used as a part of the filename for storing the binary
      * dictionary. Multiple dictionary instances with the same name is supported, with access
@@ -106,9 +102,6 @@
     /** Dictionary locale */
     private final Locale mLocale;
 
-    /** Whether to support dynamically updating the dictionary */
-    private final boolean mIsUpdatable;
-
     /** Dictionary file */
     private final File mDictFile;
 
@@ -128,23 +121,22 @@
             new AtomicReference<Runnable>();
 
     /**
-     * Abstract method for loading the unigrams and bigrams of a given dictionary in a background
-     * thread.
+     * Abstract method for loading initial contents of a given dictionary.
      */
-    protected abstract void loadDictionaryAsync();
+    protected abstract void loadInitialContentsLocked();
 
     /**
-     * Indicates that the source dictionary content has changed and a rebuild of the binary file is
-     * required. If it returns false, the next reload will only read the current binary dictionary
-     * from file. Note that the shared binary dictionary is locked when this is called.
+     * Indicates that the source dictionary contents have changed and a rebuild of the binary file
+     * is required. If it returns false, the next reload will only read the current binary
+     * dictionary from file. Note that the shared binary dictionary is locked when this is called.
      */
-    protected abstract boolean hasContentChanged();
+    protected abstract boolean haveContentsChanged();
 
     private boolean matchesExpectedBinaryDictFormatVersionForThisType(final int formatVersion) {
         return formatVersion == FormatSpec.VERSION4;
     }
 
-    public boolean isValidDictionary() {
+    public boolean isValidDictionaryLocked() {
         return mBinaryDictionary.isValidDictionary();
     }
 
@@ -164,42 +156,6 @@
     }
 
     /**
-     * Gets the executor for the given dictionary name.
-     */
-    private static PrioritizedSerialExecutor getExecutor(final String dictName) {
-        PrioritizedSerialExecutor executor = sDictNameExecutorMap.get(dictName);
-        if (executor == null) {
-            synchronized(sDictNameExecutorMap) {
-                executor = new PrioritizedSerialExecutor();
-                sDictNameExecutorMap.put(dictName, executor);
-            }
-        }
-        return executor;
-    }
-
-    /**
-     * Shutdowns all executors and removes all executors from the executor map for testing.
-     */
-    @UsedForTesting
-    public static void shutdownAllExecutors() {
-        synchronized(sDictNameExecutorMap) {
-            for (final PrioritizedSerialExecutor executor : sDictNameExecutorMap.values()) {
-                executor.shutdown();
-                sDictNameExecutorMap.remove(executor);
-            }
-        }
-    }
-
-    private static AbstractDictionaryWriter getDictionaryWriter(
-            final boolean isDynamicPersonalizationDictionary) {
-        if (isDynamicPersonalizationDictionary) {
-             return null;
-        } else {
-            return new DictionaryWriter();
-        }
-    }
-
-    /**
      * Creates a new expandable binary dictionary.
      *
      * @param context The application context of the parent.
@@ -207,24 +163,18 @@
      *        name is supported.
      * @param locale the dictionary locale.
      * @param dictType the dictionary type, as a human-readable string
-     * @param isUpdatable whether to support dynamically updating the dictionary. Please note that
-     *        dynamic dictionary has negative effects on memory space and computation time.
      * @param dictFile dictionary file path. if null, use default dictionary path based on
      *        dictionary type.
      */
     public ExpandableBinaryDictionary(final Context context, final String dictName,
-            final Locale locale, final String dictType, final boolean isUpdatable,
-            final File dictFile) {
+            final Locale locale, final String dictType, final File dictFile) {
         super(dictType);
         mDictName = dictName;
         mContext = context;
         mLocale = locale;
-        mIsUpdatable = isUpdatable;
         mDictFile = getDictFile(context, dictName, dictFile);
         mBinaryDictionary = null;
         mDictNameDictionaryUpdateController = getDictionaryUpdateController(dictName);
-        // Currently, only dynamic personalization dictionary is updatable.
-        mDictionaryWriter = getDictionaryWriter(isUpdatable);
     }
 
     public static File getDictFile(final Context context, final String dictName,
@@ -243,20 +193,7 @@
      */
     @Override
     public void close() {
-        getExecutor(mDictName).execute(new Runnable() {
-            @Override
-            public void run() {
-                if (mBinaryDictionary!= null) {
-                    mBinaryDictionary.close();
-                    mBinaryDictionary = null;
-                }
-            }
-        });
-    }
-
-    protected void closeBinaryDictionary() {
-        // Ensure that no other threads are accessing the local binary dictionary.
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 if (mBinaryDictionary != null) {
@@ -273,6 +210,10 @@
         attributeMap.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, mLocale.toString());
         attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY,
                 String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
+        attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY,
+                String.valueOf(DEFAULT_MAX_UNIGRAM_COUNT));
+        attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY,
+                String.valueOf(DEFAULT_MAX_BIGRAM_COUNT));
         return attributeMap;
     }
 
@@ -286,65 +227,52 @@
         mBinaryDictionary = null;
     }
 
+    private void createBinaryDictionaryLocked() {
+        BinaryDictionary.createEmptyDictFile(mDictFile.getAbsolutePath(),
+                DICTIONARY_FORMAT_VERSION, mLocale, getHeaderAttributeMap());
+    }
+
+    private void openBinaryDictionaryLocked() {
+        mBinaryDictionary = new BinaryDictionary(
+                mDictFile.getAbsolutePath(), 0 /* offset */, mDictFile.length(),
+                true /* useFullEditDistance */, mLocale, mDictType, true /* isUpdatable */);
+    }
+
     protected void clear() {
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
-                if (mDictionaryWriter == null) {
-                    removeBinaryDictionaryLocked();
-                    BinaryDictionary.createEmptyDictFile(mDictFile.getAbsolutePath(),
-                            DICTIONARY_FORMAT_VERSION, mLocale, getHeaderAttributeMap());
-                    mBinaryDictionary = new BinaryDictionary(
-                            mDictFile.getAbsolutePath(), 0 /* offset */, mDictFile.length(),
-                            true /* useFullEditDistance */, mLocale, mDictType, mIsUpdatable);
-                } else {
-                    mDictionaryWriter.clear();
-                }
+                removeBinaryDictionaryLocked();
+                createBinaryDictionaryLocked();
+                openBinaryDictionaryLocked();
             }
         });
     }
 
     /**
-     * Adds a word unigram to the dictionary. Used for loading a dictionary.
-     * @param word The word to add.
-     * @param shortcutTarget A shortcut target for this word, or null if none.
-     * @param frequency The frequency for this unigram.
-     * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
-     *   if shortcutTarget is null.
-     * @param isNotAWord true if this is not a word, i.e. shortcut only.
-     */
-    protected void addWord(final String word, final String shortcutTarget,
-            final int frequency, final int shortcutFreq, final boolean isNotAWord) {
-        mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, shortcutFreq, isNotAWord);
-    }
-
-    /**
-     * Adds a word bigram in the dictionary. Used for loading a dictionary.
-     */
-    protected void addBigram(final String prevWord, final String word, final int frequency,
-            final long lastModifiedTime) {
-        mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */,
-                lastModifiedTime);
-    }
-
-    /**
      * Check whether GC is needed and run GC if required.
      */
     protected void runGCIfRequired(final boolean mindsBlockByGC) {
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
-                runGCIfRequiredInternalLocked(mindsBlockByGC);
+                runGCAfterAllPrioritizedTasksIfRequiredLocked(mindsBlockByGC);
             }
         });
     }
 
-    private void runGCIfRequiredInternalLocked(final boolean mindsBlockByGC) {
-        // Calls to needsToRunGC() need to be serialized.
+    protected void runGCIfRequiredLocked(final boolean mindsBlockByGC) {
+        if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) {
+            mBinaryDictionary.flushWithGC();
+        }
+    }
+
+    private void runGCAfterAllPrioritizedTasksIfRequiredLocked(final boolean mindsBlockByGC) {
+        // needsToRunGC() have to be called with lock.
         if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) {
             if (setProcessingLargeTaskIfNot()) {
                 // Run GC after currently existing time sensitive operations.
-                getExecutor(mDictName).executePrioritized(new Runnable() {
+                ExecutorUtils.getExecutor(mDictName).executePrioritized(new Runnable() {
                     @Override
                     public void run() {
                         try {
@@ -364,52 +292,50 @@
     protected void addWordDynamically(final String word, final int frequency,
             final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
             final boolean isBlacklisted, final int timestamp) {
-        if (!mIsUpdatable) {
-            Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mDictName);
-            return;
-        }
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
-                runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
-                mBinaryDictionary.addUnigramWord(word, frequency, shortcutTarget, shortcutFreq,
+                runGCAfterAllPrioritizedTasksIfRequiredLocked(true /* mindsBlockByGC */);
+                addWordDynamicallyLocked(word, frequency, shortcutTarget, shortcutFreq,
                         isNotAWord, isBlacklisted, timestamp);
             }
         });
     }
 
+    protected void addWordDynamicallyLocked(final String word, final int frequency,
+            final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
+            final boolean isBlacklisted, final int timestamp) {
+        mBinaryDictionary.addUnigramWord(word, frequency, shortcutTarget, shortcutFreq,
+                isNotAWord, isBlacklisted, timestamp);
+    }
+
     /**
      * Dynamically adds a word bigram in the dictionary. May overwrite an existing entry.
      */
     protected void addBigramDynamically(final String word0, final String word1,
             final int frequency, final int timestamp) {
-        if (!mIsUpdatable) {
-            Log.w(TAG, "addBigramDynamically is called for non-updatable dictionary: "
-                    + mDictName);
-            return;
-        }
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
-                runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
-                mBinaryDictionary.addBigramWords(word0, word1, frequency, timestamp);
+                runGCAfterAllPrioritizedTasksIfRequiredLocked(true /* mindsBlockByGC */);
+                addBigramDynamicallyLocked(word0, word1, frequency, timestamp);
             }
         });
     }
 
+    protected void addBigramDynamicallyLocked(final String word0, final String word1,
+            final int frequency, final int timestamp) {
+        mBinaryDictionary.addBigramWords(word0, word1, frequency, timestamp);
+    }
+
     /**
      * Dynamically remove a word bigram in the dictionary.
      */
     protected void removeBigramDynamically(final String word0, final String word1) {
-        if (!mIsUpdatable) {
-            Log.w(TAG, "removeBigramDynamically is called for non-updatable dictionary: "
-                    + mDictName);
-            return;
-        }
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
-                runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
+                runGCAfterAllPrioritizedTasksIfRequiredLocked(true /* mindsBlockByGC */);
                 mBinaryDictionary.removeBigramWords(word0, word1);
             }
         });
@@ -425,12 +351,7 @@
     protected void addMultipleDictionaryEntriesDynamically(
             final ArrayList<LanguageModelParam> languageModelParams,
             final AddMultipleDictionaryEntriesCallback callback) {
-        if (!mIsUpdatable) {
-            Log.w(TAG, "addMultipleDictionaryEntriesDynamically is called for non-updatable " +
-                    "dictionary: " + mDictName);
-            return;
-        }
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 final boolean locked = setProcessingLargeTaskIfNot();
@@ -461,7 +382,7 @@
         }
         final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder =
                 new AsyncResultHolder<ArrayList<SuggestedWordInfo>>();
-        getExecutor(mDictName).executePrioritized(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).executePrioritized(new Runnable() {
             @Override
             public void run() {
                 if (mBinaryDictionary == null) {
@@ -492,15 +413,11 @@
     @Override
     public boolean isValidWord(final String word) {
         reloadDictionaryIfRequired();
-        return isValidWordInner(word);
-    }
-
-    protected boolean isValidWordInner(final String word) {
         if (processingLargeTask()) {
             return false;
         }
         final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
-        getExecutor(mDictName).executePrioritized(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).executePrioritized(new Runnable() {
             @Override
             public void run() {
                 holder.set(isValidWordLocked(word));
@@ -532,7 +449,7 @@
      * Loads the current binary dictionary from internal storage. Assumes the dictionary file
      * exists.
      */
-    private void loadBinaryDictionary() {
+    private void loadBinaryDictionaryLocked() {
         if (DEBUG) {
             Log.d(TAG, "Loading binary dictionary: " + mDictName + " request="
                     + mDictNameDictionaryUpdateController.mLastUpdateRequestTime + " update="
@@ -548,65 +465,40 @@
             } catch (InterruptedException e) {
             }
         }
-
-        final String filename = mDictFile.getAbsolutePath();
-        final long length = mDictFile.length();
-
-        // Build the new binary dictionary
-        final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0 /* offset */,
-                length, true /* useFullEditDistance */, null, mDictType, mIsUpdatable);
-
-        // Ensure all threads accessing the current dictionary have finished before
-        // swapping in the new one.
-        // TODO: Ensure multi-thread assignment of mBinaryDictionary.
         final BinaryDictionary oldBinaryDictionary = mBinaryDictionary;
-        getExecutor(mDictName).executePrioritized(new Runnable() {
-            @Override
-            public void run() {
-                mBinaryDictionary = newBinaryDictionary;
-                if (oldBinaryDictionary != null) {
-                    oldBinaryDictionary.close();
-                }
-            }
-        });
+        openBinaryDictionaryLocked();
+        if (oldBinaryDictionary != null) {
+            oldBinaryDictionary.close();
+        }
     }
 
     /**
      * Abstract method for checking if it is required to reload the dictionary before writing
      * a binary dictionary.
      */
-    abstract protected boolean needsToReloadBeforeWriting();
+    abstract protected boolean needsToReloadAfterCreation();
 
     /**
-     * Writes a new binary dictionary based on the contents of the fusion dictionary.
+     * Create a new binary dictionary and load initial contents.
      */
-    private void writeBinaryDictionary() {
+    private void createNewDictionaryLocked() {
         if (DEBUG) {
             Log.d(TAG, "Generating binary dictionary: " + mDictName + " request="
                     + mDictNameDictionaryUpdateController.mLastUpdateRequestTime + " update="
                     + mDictNameDictionaryUpdateController.mLastUpdateTime);
         }
-        if (needsToReloadBeforeWriting()) {
-            mDictionaryWriter.clear();
-            loadDictionaryAsync();
-            mDictionaryWriter.write(mDictFile, getHeaderAttributeMap());
+        removeBinaryDictionaryLocked();
+        createBinaryDictionaryLocked();
+        openBinaryDictionaryLocked();
+        loadInitialContentsLocked();
+        mBinaryDictionary.flushWithGC();
+    }
+
+    private void flushDictionaryLocked() {
+        if (mBinaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
+            mBinaryDictionary.flushWithGC();
         } else {
-            if (mBinaryDictionary == null || !isValidDictionary()
-                    // TODO: remove the check below
-                    || !matchesExpectedBinaryDictFormatVersionForThisType(
-                            mBinaryDictionary.getFormatVersion())) {
-                if (mDictFile.exists() && !FileUtils.deleteRecursively(mDictFile)) {
-                    Log.e(TAG, "Can't remove a file: " + mDictFile.getName());
-                }
-                BinaryDictionary.createEmptyDictFile(mDictFile.getAbsolutePath(),
-                        DICTIONARY_FORMAT_VERSION, mLocale, getHeaderAttributeMap());
-            } else {
-                if (mBinaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
-                    mBinaryDictionary.flushWithGC();
-                } else {
-                    mBinaryDictionary.flush();
-                }
-            }
+            mBinaryDictionary.flush();
         }
     }
 
@@ -662,57 +554,43 @@
     private final void reloadDictionary() {
         // Ensure that only one thread attempts to read or write to the shared binary dictionary
         // file at the same time.
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 try {
                     final long time = System.currentTimeMillis();
-                    final boolean dictionaryFileExists = dictionaryFileExists();
-                    if (mDictNameDictionaryUpdateController.isOutOfDate()
-                            || !dictionaryFileExists) {
-                        // If the shared dictionary file does not exist or is out of date, the
-                        // first instance that acquires the lock will generate a new one.
-                        if (hasContentChanged() || !dictionaryFileExists) {
-                            // If the source content has changed or the dictionary does not exist,
-                            // rebuild the binary dictionary. Empty dictionaries are supported (in
-                            // the case where loadDictionaryAsync() adds nothing) in order to
-                            // provide a uniform framework.
-                            mDictNameDictionaryUpdateController.mLastUpdateTime = time;
-                            writeBinaryDictionary();
-                            loadBinaryDictionary();
-                        } else {
-                            // If not, the reload request was unnecessary so revert
-                            // LastUpdateRequestTime to LastUpdateTime.
-                            mDictNameDictionaryUpdateController.mLastUpdateRequestTime =
-                                    mDictNameDictionaryUpdateController.mLastUpdateTime;
-                        }
+                    final boolean openedDictIsOutOfDate =
+                            mDictNameDictionaryUpdateController.isOutOfDate();
+                    if (!dictionaryFileExists()
+                            || (openedDictIsOutOfDate && haveContentsChanged())) {
+                        // If the shared dictionary file does not exist or is out of date and
+                        // contents have been updated, the first instance that acquires the lock
+                        // will generate a new one
+                        mDictNameDictionaryUpdateController.mLastUpdateTime = time;
+                        createNewDictionaryLocked();
+                    } else if (openedDictIsOutOfDate) {
+                        // If not, the reload request was unnecessary so revert
+                        // LastUpdateRequestTime to LastUpdateTime.
+                        mDictNameDictionaryUpdateController.mLastUpdateRequestTime =
+                                mDictNameDictionaryUpdateController.mLastUpdateTime;
                     } else if (mBinaryDictionary == null ||
                             mPerInstanceDictionaryUpdateController.mLastUpdateTime
                                     < mDictNameDictionaryUpdateController.mLastUpdateTime) {
                         // Otherwise, if the local dictionary is older than the shared dictionary,
                         // load the shared dictionary.
-                        loadBinaryDictionary();
+                        loadBinaryDictionaryLocked();
                     }
-                    // If we just loaded the binary dictionary, then mBinaryDictionary is not
-                    // up-to-date yet so it's useless to test it right away. Schedule the check
-                    // for right after it's loaded instead.
-                    getExecutor(mDictName).executePrioritized(new Runnable() {
-                        @Override
-                        public void run() {
-                            if (mBinaryDictionary != null && !(isValidDictionary()
-                                    // TODO: remove the check below
-                                    && matchesExpectedBinaryDictFormatVersionForThisType(
-                                            mBinaryDictionary.getFormatVersion()))) {
-                                // Binary dictionary or its format version is not valid. Regenerate
-                                // the dictionary file. writeBinaryDictionary will remove the
-                                // existing files if appropriate.
-                                mDictNameDictionaryUpdateController.mLastUpdateTime = time;
-                                writeBinaryDictionary();
-                                loadBinaryDictionary();
-                            }
-                            mPerInstanceDictionaryUpdateController.mLastUpdateTime = time;
-                        }
-                    });
+                    if (mBinaryDictionary != null && !(isValidDictionaryLocked()
+                            // TODO: remove the check below
+                            && matchesExpectedBinaryDictFormatVersionForThisType(
+                                    mBinaryDictionary.getFormatVersion()))) {
+                        // Binary dictionary or its format version is not valid. Regenerate
+                        // the dictionary file. writeBinaryDictionary will remove the
+                        // existing files if appropriate.
+                        mDictNameDictionaryUpdateController.mLastUpdateTime = time;
+                        createNewDictionaryLocked();
+                    }
+                    mPerInstanceDictionaryUpdateController.mLastUpdateTime = time;
                 } finally {
                     mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false);
                 }
@@ -726,17 +604,17 @@
     }
 
     /**
-     * Generate binary dictionary using DictionaryWriter.
+     * Flush binary dictionary to dictionary file.
      */
     protected void asyncFlushBinaryDictionary() {
         final Runnable newTask = new Runnable() {
             @Override
             public void run() {
-                writeBinaryDictionary();
+                flushDictionaryLocked();
             }
         };
         final Runnable oldTask = mUnfinishedFlushingTask.getAndSet(newTask);
-        getExecutor(mDictName).replaceAndExecute(oldTask, newTask);
+        ExecutorUtils.getExecutor(mDictName).replaceAndExecute(oldTask, newTask);
     }
 
     /**
@@ -757,7 +635,7 @@
     @UsedForTesting
     public boolean isInUnderlyingBinaryDictionaryForTests(final String word) {
         final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 if (mDictType == Dictionary.TYPE_USER_HISTORY) {
@@ -771,7 +649,7 @@
     @UsedForTesting
     public void waitAllTasksForTests() {
         final CountDownLatch countDownLatch = new CountDownLatch(1);
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 countDownLatch.countDown();
@@ -787,7 +665,7 @@
     @UsedForTesting
     public void dumpAllWordsForDebug() {
         reloadDictionaryIfRequired();
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 Log.d(TAG, "Dump dictionary: " + mDictName);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 793c1c4..4c2454c 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -172,7 +172,11 @@
         }
 
         public void onCreate() {
-            final Resources res = getOwnerInstance().getResources();
+            final LatinIME latinIme = getOwnerInstance();
+            if (latinIme == null) {
+                return;
+            }
+            final Resources res = latinIme.getResources();
             mDelayUpdateSuggestions = res.getInteger(R.integer.config_delay_update_suggestions);
             mDelayUpdateShiftState = res.getInteger(R.integer.config_delay_update_shift_state);
             mDoubleSpacePeriodTimeout =
@@ -182,6 +186,9 @@
         @Override
         public void handleMessage(final Message msg) {
             final LatinIME latinIme = getOwnerInstance();
+            if (latinIme == null) {
+                return;
+            }
             final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
             switch (msg.what) {
             case MSG_UPDATE_SUGGESTION_STRIP:
@@ -239,7 +246,11 @@
         }
 
         public void postResumeSuggestions() {
-            if (!getOwnerInstance().mSettings.getCurrent().isSuggestionStripVisible()) {
+            final LatinIME latinIme = getOwnerInstance();
+            if (latinIme == null) {
+                return;
+            }
+            if (!latinIme.mSettings.getCurrent().isSuggestionStripVisible()) {
                 return;
             }
             removeMessages(MSG_RESUME_SUGGESTIONS);
@@ -326,6 +337,9 @@
             resetPendingImsCallback();
             mIsOrientationChanging = true;
             final LatinIME latinIme = getOwnerInstance();
+            if (latinIme == null) {
+                return;
+            }
             if (latinIme.isInputViewShown()) {
                 latinIme.mKeyboardSwitcher.saveKeyboardState();
             }
@@ -362,8 +376,10 @@
                     mPendingSuccessiveImsCallback = true;
                 }
                 final LatinIME latinIme = getOwnerInstance();
-                executePendingImsCallback(latinIme, editorInfo, restarting);
-                latinIme.onStartInputInternal(editorInfo, restarting);
+                if (latinIme != null) {
+                    executePendingImsCallback(latinIme, editorInfo, restarting);
+                    latinIme.onStartInputInternal(editorInfo, restarting);
+                }
             }
         }
 
@@ -381,9 +397,11 @@
                             PENDING_IMS_CALLBACK_DURATION);
                 }
                 final LatinIME latinIme = getOwnerInstance();
-                executePendingImsCallback(latinIme, editorInfo, restarting);
-                latinIme.onStartInputViewInternal(editorInfo, restarting);
-                mAppliedEditorInfo = editorInfo;
+                if (latinIme != null) {
+                    executePendingImsCallback(latinIme, editorInfo, restarting);
+                    latinIme.onStartInputViewInternal(editorInfo, restarting);
+                    mAppliedEditorInfo = editorInfo;
+                }
             }
         }
 
@@ -393,8 +411,10 @@
                 mHasPendingFinishInputView = true;
             } else {
                 final LatinIME latinIme = getOwnerInstance();
-                latinIme.onFinishInputViewInternal(finishingInput);
-                mAppliedEditorInfo = null;
+                if (latinIme != null) {
+                    latinIme.onFinishInputViewInternal(finishingInput);
+                    mAppliedEditorInfo = null;
+                }
             }
         }
 
@@ -404,8 +424,10 @@
                 mHasPendingFinishInput = true;
             } else {
                 final LatinIME latinIme = getOwnerInstance();
-                executePendingImsCallback(latinIme, null, false);
-                latinIme.onFinishInputInternal();
+                if (latinIme != null) {
+                    executePendingImsCallback(latinIme, null, false);
+                    latinIme.onFinishInputInternal();
+                }
             }
         }
     }
@@ -857,7 +879,7 @@
         }
         // This will set the punctuation suggestions if next word suggestion is off;
         // otherwise it will clear the suggestion strip.
-        setNeutralSuggestionStripInternal(false /* needsInputViewShown */);
+        setNeutralSuggestionStripInternal();
 
         mHandler.cancelUpdateSuggestionStrip();
         mHandler.cancelDoubleSpacePeriodTimer();
@@ -1022,23 +1044,18 @@
                 null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */,
                 false /* isObsoleteSuggestions */, false /* isPrediction */);
         // When in fullscreen mode, show completions generated by the application forcibly
-        setSuggestedWords(suggestedWords, true /* isSuggestionStripVisible */,
-                true /* needsInputViewShown */);
+        setSuggestedWords(suggestedWords, true /* isSuggestionStripVisible */);
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
             ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions);
         }
     }
 
-    private void setSuggestionStripShownInternal(final boolean isSuggestionStripVisible,
-            final boolean needsInputViewShown) {
+    private void setSuggestionStripShownInternal(final boolean isSuggestionStripVisible) {
         // TODO: Modify this if we support suggestions with hard keyboard
         if (!onEvaluateInputViewShown() || !hasSuggestionStripView()) {
             return;
         }
-        final boolean inputViewShown = mKeyboardSwitcher.isShowingMainKeyboardOrEmojiPalettes();
-        final boolean shouldShowSuggestions = isSuggestionStripVisible
-                && (needsInputViewShown ? inputViewShown : true);
-        if (shouldShowSuggestions) {
+        if (isSuggestionStripVisible) {
             mSuggestionStripView.setVisibility(View.VISIBLE);
         } else {
             mSuggestionStripView.setVisibility(isFullscreenMode() ? View.GONE : View.INVISIBLE);
@@ -1325,8 +1342,7 @@
         // Nothing to do so far.
     }
 
-    // TODO[IL]: Define a clear interface for this
-    public boolean isSuggestionStripVisible() {
+    private boolean isSuggestionStripVisible() {
         if (!hasSuggestionStripView()) {
             return false;
         }
@@ -1350,7 +1366,6 @@
         return currentSettings.isSuggestionsRequested();
     }
 
-    @Override
     public boolean hasSuggestionStripView() {
         return null != mSuggestionStripView;
     }
@@ -1370,7 +1385,7 @@
 
     // TODO[IL]: Define a clear interface for this
     public void setSuggestedWords(final SuggestedWords suggestedWords,
-            final boolean isSuggestionStripVisible, final boolean needsInputViewShown) {
+            final boolean isSuggestionStripVisible) {
         mInputLogic.setSuggestedWords(suggestedWords);
         if (!hasSuggestionStripView()) {
             return;
@@ -1390,7 +1405,7 @@
                     SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype()));
         }
         mKeyboardSwitcher.onAutoCorrectionStateChanged(suggestedWords.mWillAutoCorrect);
-        setSuggestionStripShownInternal(isSuggestionStripVisible, needsInputViewShown);
+        setSuggestionStripShownInternal(isSuggestionStripVisible);
     }
 
     // TODO[IL]: Move this out of LatinIME.
@@ -1476,8 +1491,7 @@
             setNeutralSuggestionStrip();
         } else {
             mInputLogic.mWordComposer.setAutoCorrection(autoCorrection);
-            setSuggestedWords(
-                    suggestedWords, isSuggestionStripVisible(), true /* needsInputViewShown */);
+            setSuggestedWords(suggestedWords, isSuggestionStripVisible());
         }
         // Cache the auto-correction in accessibility code so we can speak it if the user
         // touches a key that will insert it.
@@ -1506,14 +1520,14 @@
     // punctuation suggestions (if it's disabled).
     @Override
     public void setNeutralSuggestionStrip() {
-        setNeutralSuggestionStripInternal(true /* needsInputViewShown */);
+        setNeutralSuggestionStripInternal();
     }
 
-    private void setNeutralSuggestionStripInternal(final boolean needsInputViewShown) {
+    private void setNeutralSuggestionStripInternal() {
         final SettingsValues currentSettings = mSettings.getCurrent();
         final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled
                 ? SuggestedWords.EMPTY : currentSettings.mSpacingAndPunctuations.mSuggestPuncList;
-        setSuggestedWords(neutralSuggestions, isSuggestionStripVisible(), needsInputViewShown);
+        setSuggestedWords(neutralSuggestions, isSuggestionStripVisible());
     }
 
     // TODO: Make this private
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 30b20a3..323256d 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -483,12 +483,16 @@
      *
      * @param start the character index where the selection should start.
      * @param end the character index where the selection should end.
-     * @return Returns true on success, false if the input connection is no longer valid either when
-     * setting the selection or when retrieving the text cache at that point.
+     * @return Returns true on success, false on failure: either the input connection is no longer
+     * valid when setting the selection or when retrieving the text cache at that point, or
+     * invalid arguments were passed.
      */
     public boolean setSelection(final int start, final int end) {
         if (DEBUG_BATCH_NESTING) checkBatchEdit();
         if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+        if (start < 0 || end < 0) {
+            return false;
+        }
         mExpectedSelStart = start;
         mExpectedSelEnd = end;
         if (null != mIC) {
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 499a98d..1747eee 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -134,12 +134,17 @@
         mDictionaryFacilitator.getSuggestions(wordComposerForLookup, prevWordForBigram,
                 proximityInfo, blockOffensiveWords, additionalFeaturesOptions, SESSION_TYPING,
                 suggestionsSet, rawSuggestions);
+
+        final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
+        final boolean isAllUpperCase = wordComposer.isAllUpperCase();
         final String firstSuggestion;
         final String whitelistedWord;
         if (suggestionsSet.isEmpty()) {
             whitelistedWord = firstSuggestion = null;
         } else {
-            final SuggestedWordInfo firstSuggestedWordInfo = suggestionsSet.first();
+            final SuggestedWordInfo firstSuggestedWordInfo = getTransformedSuggestedWordInfo(
+                    suggestionsSet.first(), mLocale, isAllUpperCase, isFirstCharCapitalized,
+                    trailingSingleQuotesCount);
             firstSuggestion = firstSuggestedWordInfo.mWord;
             if (SuggestedWordInfo.KIND_WHITELIST != firstSuggestedWordInfo.mKind) {
                 whitelistedWord = null;
@@ -148,6 +153,8 @@
             }
         }
 
+        final boolean isPrediction = !wordComposer.isComposingWord();
+
         // We allow auto-correction if we have a whitelisted word, or if the word is not a valid
         // word of more than 1 char, except if the first suggestion is the same as the typed string
         // because in this case if it's strong enough to auto-correct that will mistakenly designate
@@ -155,10 +162,10 @@
         // TODO: stop relying on indices to find where is the auto-correction in the suggested
         // words, and correct this test.
         final boolean allowsToBeAutoCorrected = (null != whitelistedWord
-                && !whitelistedWord.equals(consideredWord))
+                && !whitelistedWord.equals(typedWord))
                 || (consideredWord.length() > 1 && !mDictionaryFacilitator.isValidWord(
                         consideredWord, wordComposer.isFirstCharCapitalized())
-                        && !consideredWord.equals(firstSuggestion));
+                        && !typedWord.equals(firstSuggestion));
 
         final boolean hasAutoCorrection;
         // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because
@@ -166,7 +173,7 @@
         // same time, it feels wrong that the SuggestedWord object includes information about
         // the current settings. It may also be useful to know, when the setting is off, whether
         // the word *would* have been auto-corrected.
-        if (!isCorrectionEnabled || !allowsToBeAutoCorrected || !wordComposer.isComposingWord()
+        if (!isCorrectionEnabled || !allowsToBeAutoCorrected || isPrediction
                 || suggestionsSet.isEmpty() || wordComposer.hasDigits()
                 || wordComposer.isMostlyCaps() || wordComposer.isResumed()
                 || !mDictionaryFacilitator.hasMainDictionary()
@@ -188,8 +195,6 @@
         final ArrayList<SuggestedWordInfo> suggestionsContainer =
                 CollectionUtils.newArrayList(suggestionsSet);
         final int suggestionsCount = suggestionsContainer.size();
-        final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
-        final boolean isAllUpperCase = wordComposer.isAllUpperCase();
         if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) {
             for (int i = 0; i < suggestionsCount; ++i) {
                 final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
@@ -226,10 +231,9 @@
                 // TODO: this first argument is lying. If this is a whitelisted word which is an
                 // actual word, it says typedWordValid = false, which looks wrong. We should either
                 // rename the attribute or change the value.
-                !allowsToBeAutoCorrected /* typedWordValid */,
+                !isPrediction && !allowsToBeAutoCorrected /* typedWordValid */,
                 hasAutoCorrection, /* willAutoCorrect */
-                false /* isObsoleteSuggestions */,
-                !wordComposer.isComposingWord() /* isPrediction */, sequenceNumber));
+                false /* isObsoleteSuggestions */, isPrediction, sequenceNumber));
     }
 
     // Retrieves suggestions for the batch input
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 46df3e8..06bc90c 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -69,7 +69,7 @@
             final boolean isPrediction,
             final int sequenceNumber) {
         this(suggestedWordInfoList, rawSuggestions,
-                suggestedWordInfoList.isEmpty() ? null
+                (suggestedWordInfoList.isEmpty() || isPrediction) ? null
                         : suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord,
                 typedWordValid, willAutoCorrect, isObsoleteSuggestions, isPrediction,
                 sequenceNumber);
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index 3e3cbf0..8078ab5 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -86,8 +86,7 @@
 
     public UserBinaryDictionary(final Context context, final Locale locale,
             final boolean alsoUseMoreRestrictiveLocales, final File dictFile) {
-        super(context, getDictName(NAME, locale, dictFile), locale, Dictionary.TYPE_USER,
-                false /* isUpdatable */, dictFile);
+        super(context, getDictName(NAME, locale, dictFile), locale, Dictionary.TYPE_USER, dictFile);
         if (null == locale) throw new NullPointerException(); // Catch the error earlier
         final String localeStr = locale.toString();
         if (SubtypeLocaleUtils.NO_LANGUAGE.equals(localeStr)) {
@@ -130,7 +129,7 @@
     }
 
     @Override
-    public void loadDictionaryAsync() {
+    public void loadInitialContentsLocked() {
         // Split the locale. For example "en" => ["en"], "de_DE" => ["de", "DE"],
         // "en_US_foo_bar_qux" => ["en", "US", "foo_bar_qux"] because of the limit of 3.
         // This is correct for locale processing.
@@ -182,7 +181,7 @@
         try {
             cursor = mContext.getContentResolver().query(
                 Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null);
-            addWords(cursor);
+            addWordsLocked(cursor);
         } catch (final SQLiteException e) {
             Log.e(TAG, "SQLiteException in the remote User dictionary process.", e);
         } finally {
@@ -236,7 +235,7 @@
         }
     }
 
-    private void addWords(final Cursor cursor) {
+    private void addWordsLocked(final Cursor cursor) {
         final boolean hasShortcutColumn = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
         if (cursor == null) return;
         if (cursor.moveToFirst()) {
@@ -250,12 +249,16 @@
                 final int adjustedFrequency = scaleFrequencyFromDefaultToLatinIme(frequency);
                 // Safeguard against adding really long words.
                 if (word.length() < MAX_WORD_LENGTH) {
-                    super.addWord(word, null, adjustedFrequency, 0 /* shortcutFreq */,
-                            false /* isNotAWord */);
-                }
-                if (null != shortcut && shortcut.length() < MAX_WORD_LENGTH) {
-                    super.addWord(shortcut, word, adjustedFrequency, USER_DICT_SHORTCUT_FREQUENCY,
-                            true /* isNotAWord */);
+                    runGCIfRequiredLocked(true /* mindsBlockByGC */);
+                    addWordDynamicallyLocked(word, adjustedFrequency, null /* shortcutTarget */,
+                            0 /* shortcutFreq */, false /* isNotAWord */,
+                            false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP);
+                    if (null != shortcut && shortcut.length() < MAX_WORD_LENGTH) {
+                        runGCIfRequiredLocked(true /* mindsBlockByGC */);
+                        addWordDynamicallyLocked(shortcut, adjustedFrequency, word,
+                                USER_DICT_SHORTCUT_FREQUENCY, true /* isNotAWord */,
+                                false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP);
+                    }
                 }
                 cursor.moveToNext();
             }
@@ -263,12 +266,12 @@
     }
 
     @Override
-    protected boolean hasContentChanged() {
+    protected boolean haveContentsChanged() {
         return true;
     }
 
     @Override
-    protected boolean needsToReloadBeforeWriting() {
+    protected boolean needsToReloadAfterCreation() {
         return true;
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 045d06f..f2f9f1e 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -1360,10 +1360,12 @@
                         }});
         } else {
             // We found suggestion spans in the word. We'll create the SuggestedWords out of
-            // them, and make willAutoCorrect false.
+            // them, and make willAutoCorrect false. We make typedWordValid false, because the
+            // color of the word in the suggestion strip changes according to this parameter,
+            // and false gives the correct color.
             final SuggestedWords suggestedWords = new SuggestedWords(suggestions,
                     null /* rawSuggestions */, typedWord,
-                    true /* typedWordValid */, false /* willAutoCorrect */,
+                    false /* typedWordValid */, false /* willAutoCorrect */,
                     false /* isObsoleteSuggestions */, false /* isPrediction */,
                     SuggestedWords.NOT_A_SEQUENCE_NUMBER);
             mIsAutoCorrectionIndicatorOn = false;
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
index b09e205..db96de3 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
@@ -139,7 +139,7 @@
                                     forEnd /* dismissGestureFloatingPreviewText */);
                             if (forEnd) {
                                 mInBatchInput = false;
-                                // The following call schedules onEndBatchInputAsyncInternal
+                                // The following call schedules onEndBatchInputInternal
                                 // to be called on the UI thread.
                                 mLatinIME.mHandler.onEndBatchInput(suggestedWords);
                             }
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
index b534ebe..25e1bcd 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -357,7 +357,7 @@
      * @return true if it's a binary dictionary, false otherwise
      */
     public static boolean isBinaryDictionary(final File file) {
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file);
+        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, 0, file.length());
         if (dictDecoder == null) {
             return false;
         }
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 989ca4b..90e7400 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -237,7 +237,7 @@
             final File file, final long offset, final long length)
             throws FileNotFoundException, IOException, UnsupportedFormatException {
         final byte[] buffer = new byte[HEADER_READING_BUFFER_SIZE];
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file,
+        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, offset, length,
                 new DictDecoder.DictionaryBufferFactory() {
                     @Override
                     public DictBuffer getDictionaryBuffer(File file)
@@ -251,8 +251,7 @@
                             inStream.close();
                         }
                     }
-                }
-        );
+                });
         if (dictDecoder == null) {
             return null;
         }
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java
index a5dc456..678c5ca 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.latin.makedict;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 
@@ -25,6 +26,7 @@
  * An interface of binary dictionary encoder.
  */
 public interface DictEncoder {
+    @UsedForTesting
     public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions)
             throws IOException, UnsupportedFormatException;
 
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java b/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java
index b99e281..b32eb91 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java
@@ -38,6 +38,14 @@
     public static final String DICTIONARY_DATE_KEY = "date";
     public static final String HAS_HISTORICAL_INFO_KEY = "HAS_HISTORICAL_INFO";
     public static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE";
+    public static final String FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP_KEY =
+            "FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP";
+    public static final String FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY =
+            "FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID";
+    public static final String FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS_KEY =
+            "FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS";
+    public static final String MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_COUNT";
+    public static final String MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_COUNT";
     public static final String ATTRIBUTE_VALUE_TRUE = "1";
 
     public DictionaryHeader(final int headerSize, final DictionaryOptions dictionaryOptions,
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index c7635ef..9abecbf 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -326,30 +326,34 @@
      * Returns new dictionary decoder.
      *
      * @param dictFile the dictionary file.
+     * @param offset the offset in the file.
+     * @param length the length of the file, in bytes.
      * @param bufferType The type of buffer, as one of USE_* in DictDecoder.
      * @return new dictionary decoder if the dictionary file exists, otherwise null.
      */
-    public static DictDecoder getDictDecoder(final File dictFile, final int bufferType) {
+    public static DictDecoder getDictDecoder(final File dictFile, final long offset,
+            final long length, final int bufferType) {
         if (dictFile.isDirectory()) {
             return new Ver4DictDecoder(dictFile, bufferType);
         } else if (dictFile.isFile()) {
-            return new Ver2DictDecoder(dictFile, bufferType);
+            return new Ver2DictDecoder(dictFile, offset, length, bufferType);
         }
         return null;
     }
 
-    public static DictDecoder getDictDecoder(final File dictFile,
-            final DictionaryBufferFactory factory) {
+    public static DictDecoder getDictDecoder(final File dictFile, final long offset,
+            final long length, final DictionaryBufferFactory factory) {
         if (dictFile.isDirectory()) {
             return new Ver4DictDecoder(dictFile, factory);
         } else if (dictFile.isFile()) {
-            return new Ver2DictDecoder(dictFile, factory);
+            return new Ver2DictDecoder(dictFile, offset, length, factory);
         }
         return null;
     }
 
-    public static DictDecoder getDictDecoder(final File dictFile) {
-        return getDictDecoder(dictFile, DictDecoder.USE_READONLY_BYTEBUFFER);
+    public static DictDecoder getDictDecoder(final File dictFile, final long offset,
+            final long length) {
+        return getDictDecoder(dictFile, offset, length, DictDecoder.USE_READONLY_BYTEBUFFER);
     }
 
     private FormatSpec() {
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
index bf776cf..ae1e443 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
@@ -116,13 +116,18 @@
     }
 
     protected final File mDictionaryBinaryFile;
+    protected final long mOffset;
+    protected final long mLength;
     // TODO: Remove mBufferFactory and mDictBuffer from this class members because they are now
     // used only for testing.
     private final DictionaryBufferFactory mBufferFactory;
     protected DictBuffer mDictBuffer;
 
-    /* package */ Ver2DictDecoder(final File file, final int factoryFlag) {
+    /* package */ Ver2DictDecoder(final File file, final long offset, final long length,
+            final int factoryFlag) {
         mDictionaryBinaryFile = file;
+        mOffset = offset;
+        mLength = length;
         mDictBuffer = null;
         if ((factoryFlag & MASK_DICTBUFFER) == USE_READONLY_BYTEBUFFER) {
             mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory();
@@ -135,8 +140,11 @@
         }
     }
 
-    /* package */ Ver2DictDecoder(final File file, final DictionaryBufferFactory factory) {
+    /* package */ Ver2DictDecoder(final File file, final long offset, final long length,
+            final DictionaryBufferFactory factory) {
         mDictionaryBinaryFile = file;
+        mOffset = offset;
+        mLength = length;
         mBufferFactory = factory;
     }
 
@@ -164,9 +172,9 @@
     public DictionaryHeader readHeader() throws IOException, UnsupportedFormatException {
         // dictType is not being used in dicttool. Passing an empty string.
         final BinaryDictionary binaryDictionary = new BinaryDictionary(
-                mDictionaryBinaryFile.getAbsolutePath(), 0 /* offset */,
-                mDictionaryBinaryFile.length() /* length */, true /* useFullEditDistance */,
-                null /* locale */, "" /* dictType */, false /* isUpdatable */);
+                mDictionaryBinaryFile.getAbsolutePath(), mOffset, mLength,
+                true /* useFullEditDistance */, null /* locale */, "" /* dictType */,
+                false /* isUpdatable */);
         final DictionaryHeader header = binaryDictionary.getHeader();
         binaryDictionary.close();
         if (header == null) {
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index d3734d6..074ec40 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -27,10 +27,8 @@
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
-import java.util.concurrent.TimeUnit;
 
 /**
  * This class is a base class of a dictionary that supports decaying for the personalized language
@@ -49,14 +47,13 @@
     /** The locale for this dictionary. */
     public final Locale mLocale;
 
-    private final String mDictName;
+    private Map<String, String> mAdditionalAttributeMap = null;
 
     protected DecayingExpandableBinaryDictionaryBase(final Context context,
             final String dictName, final Locale locale, final String dictionaryType,
             final File dictFile) {
-        super(context, dictName, locale, dictionaryType, true /* isUpdatable */, dictFile);
+        super(context, dictName, locale, dictionaryType, dictFile);
         mLocale = locale;
-        mDictName = dictName;
         if (mLocale != null && mLocale.toString().length() > 1) {
             reloadDictionaryIfRequired();
         }
@@ -78,25 +75,24 @@
 
     @Override
     protected Map<String, String> getHeaderAttributeMap() {
-        HashMap<String, String> attributeMap = new HashMap<String, String>();
+        final Map<String, String> attributeMap = super.getHeaderAttributeMap();
+        if (mAdditionalAttributeMap != null) {
+            attributeMap.putAll(mAdditionalAttributeMap);
+        }
         attributeMap.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY,
                 DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
         attributeMap.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY,
                 DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
-        attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, mDictName);
-        attributeMap.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, mLocale.toString());
-        attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY,
-                String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
         return attributeMap;
     }
 
     @Override
-    protected boolean hasContentChanged() {
+    protected boolean haveContentsChanged() {
         return false;
     }
 
     @Override
-    protected boolean needsToReloadBeforeWriting() {
+    protected boolean needsToReloadAfterCreation() {
         return false;
     }
 
@@ -140,8 +136,8 @@
     }
 
     @Override
-    protected void loadDictionaryAsync() {
-        // Never loaded to memory in Java side.
+    protected void loadInitialContentsLocked() {
+        // No initial contents.
     }
 
     @UsedForTesting
@@ -152,7 +148,14 @@
         asyncFlushBinaryDictionary();
     }
 
-    /* package */ void decayIfNeeded() {
+    @UsedForTesting
+    public void clearAndFlushDictionaryWithAdditionalAttributes(
+            final Map<String, String> attributeMap) {
+        mAdditionalAttributeMap = attributeMap;
+        clearAndFlushDictionary();
+    }
+
+    /* package */ void runGCIfRequired() {
         runGCIfRequired(false /* mindsBlockByGC */);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java b/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java
index e9ca662..de2744f 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java
@@ -43,7 +43,7 @@
     /**
      * Interval to update for decaying dictionaries.
      */
-    private static final long DICTIONARY_DECAY_INTERVAL = TimeUnit.MINUTES.toMillis(60);
+    /* package */ static final long DICTIONARY_DECAY_INTERVAL = TimeUnit.MINUTES.toMillis(60);
 
     public static void setUpIntervalAlarmForDictionaryDecaying(Context context) {
         AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
@@ -60,7 +60,7 @@
     public void onReceive(final Context context, final Intent intent) {
         final String action = intent.getAction();
         if (action.equals(DICTIONARY_DECAY_INTENT_ACTION)) {
-            PersonalizationHelper.tryDecayingAllOpeningUserHistoryDictionary();
+            PersonalizationHelper.runGCOnAllOpenedUserHistoryDictionaries();
         }
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
index 5ae2fb6..385b525 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.latin.personalization;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.FileUtils;
 
@@ -27,6 +28,7 @@
 import java.lang.ref.SoftReference;
 import java.util.Locale;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 
 public class PersonalizationHelper {
     private static final String TAG = PersonalizationHelper.class.getSimpleName();
@@ -59,14 +61,35 @@
         }
     }
 
-    public static void tryDecayingAllOpeningUserHistoryDictionary() {
-        for (final ConcurrentHashMap.Entry<String, SoftReference<UserHistoryDictionary>> entry
-                : sLangUserHistoryDictCache.entrySet()) {
-            if (entry.getValue() != null) {
-                final UserHistoryDictionary dict = entry.getValue().get();
-                if (dict != null) {
-                    dict.decayIfNeeded();
-                }
+    private static int sCurrentTimestampForTesting = 0;
+    public static void currentTimeChangedForTesting(final int currentTimestamp) {
+        if (TimeUnit.MILLISECONDS.toSeconds(
+                DictionaryDecayBroadcastReciever.DICTIONARY_DECAY_INTERVAL)
+                        < currentTimestamp - sCurrentTimestampForTesting) {
+            // TODO: Run GC for both PersonalizationDictionary and UserHistoryDictionary.
+            runGCOnAllOpenedUserHistoryDictionaries();
+        }
+    }
+
+    public static void runGCOnAllOpenedUserHistoryDictionaries() {
+        runGCOnAllDictionariesIfRequired(sLangUserHistoryDictCache);
+    }
+
+    @UsedForTesting
+    public static void runGCOnAllOpenedPersonalizationDictionaries() {
+        runGCOnAllDictionariesIfRequired(sLangPersonalizationDictCache);
+    }
+
+    private static <T extends DecayingExpandableBinaryDictionaryBase>
+            void runGCOnAllDictionariesIfRequired(
+                    final ConcurrentHashMap<String, SoftReference<T>> dictionaryMap) {
+        for (final ConcurrentHashMap.Entry<String, SoftReference<T>> entry
+                : dictionaryMap.entrySet()) {
+            final DecayingExpandableBinaryDictionaryBase dict = entry.getValue().get();
+            if (dict != null) {
+                dict.runGCIfRequired();
+            } else {
+                dictionaryMap.remove(entry.getKey());
             }
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 8ea7128..afa8fe3 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -95,9 +95,9 @@
     private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
     private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
 
-    private final int mSuggestionStripOption;
+    private final int mSuggestionStripOptions;
     // These constants are the flag values of
-    // {@link R.styleable#SuggestionStripView_suggestionStripOption} attribute.
+    // {@link R.styleable#SuggestionStripView_suggestionStripOptions} attribute.
     private static final int AUTO_CORRECT_BOLD = 0x01;
     private static final int AUTO_CORRECT_UNDERLINE = 0x02;
     private static final int VALID_TYPED_WORD_BOLD = 0x04;
@@ -122,8 +122,8 @@
 
         final TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripView);
-        mSuggestionStripOption = a.getInt(
-                R.styleable.SuggestionStripView_suggestionStripOption, 0);
+        mSuggestionStripOptions = a.getInt(
+                R.styleable.SuggestionStripView_suggestionStripOptions, 0);
         mAlphaObsoleted = ResourceUtils.getFraction(a,
                 R.styleable.SuggestionStripView_alphaObsoleted, 1.0f);
         mColorValidTypedWord = a.getColor(R.styleable.SuggestionStripView_colorValidTypedWord, 0);
@@ -200,22 +200,24 @@
             return null;
         }
         final String word = suggestedWords.getLabel(indexInSuggestedWords);
-        final boolean isAutoCorrect = indexInSuggestedWords == 1
-                && suggestedWords.mWillAutoCorrect;
-        final boolean isTypedWordValid = indexInSuggestedWords == 0
-                && suggestedWords.mTypedWordValid;
-        if (!isAutoCorrect && !isTypedWordValid) {
+        // TODO: don't use the index to decide whether this is the auto-correction/typed word, as
+        // this is brittle
+        final boolean isAutoCorrection = suggestedWords.mWillAutoCorrect
+                && indexInSuggestedWords == SuggestedWords.INDEX_OF_AUTO_CORRECTION;
+        final boolean isTypedWordValid = suggestedWords.mTypedWordValid
+                && indexInSuggestedWords == SuggestedWords.INDEX_OF_TYPED_WORD;
+        if (!isAutoCorrection && !isTypedWordValid) {
             return word;
         }
 
         final int len = word.length();
         final Spannable spannedWord = new SpannableString(word);
-        final int option = mSuggestionStripOption;
-        if ((isAutoCorrect && (option & AUTO_CORRECT_BOLD) != 0)
-                || (isTypedWordValid && (option & VALID_TYPED_WORD_BOLD) != 0)) {
+        final int options = mSuggestionStripOptions;
+        if ((isAutoCorrection && (options & AUTO_CORRECT_BOLD) != 0)
+                || (isTypedWordValid && (options & VALID_TYPED_WORD_BOLD) != 0)) {
             spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
         }
-        if (isAutoCorrect && (option & AUTO_CORRECT_UNDERLINE) != 0) {
+        if (isAutoCorrection && (options & AUTO_CORRECT_UNDERLINE) != 0) {
             spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
         }
         return spannedWord;
@@ -242,22 +244,23 @@
         return indexInSuggestedWords;
     }
 
-    private int getSuggestionTextColor(final int indexInSuggestedWords,
-            final SuggestedWords suggestedWords) {
+    private int getSuggestionTextColor(final SuggestedWords suggestedWords,
+            final int indexInSuggestedWords) {
         final int positionInStrip =
                 getPositionInSuggestionStrip(indexInSuggestedWords, suggestedWords);
-        // TODO: Need to revisit this logic with bigram suggestions
-        final boolean isSuggested = (indexInSuggestedWords != SuggestedWords.INDEX_OF_TYPED_WORD);
+        // Use identity for strings, not #equals : it's the typed word if it's the same object
+        final boolean isTypedWord =
+                suggestedWords.getWord(indexInSuggestedWords) == suggestedWords.mTypedWord;
 
         final int color;
         if (positionInStrip == mCenterPositionInStrip && suggestedWords.mWillAutoCorrect) {
             color = mColorAutoCorrect;
-        } else if (positionInStrip == mCenterPositionInStrip && suggestedWords.mTypedWordValid) {
+        } else if (isTypedWord && suggestedWords.mTypedWordValid) {
             color = mColorValidTypedWord;
-        } else if (isSuggested) {
-            color = mColorSuggested;
-        } else {
+        } else if (isTypedWord) {
             color = mColorTypedWord;
+        } else {
+            color = mColorSuggested;
         }
         if (LatinImeLogger.sDBG && suggestedWords.size() > 1) {
             // If we auto-correct, then the autocorrection is in slot 0 and the typed word
@@ -270,7 +273,7 @@
             }
         }
 
-        if (suggestedWords.mIsObsoleteSuggestions && isSuggested) {
+        if (suggestedWords.mIsObsoleteSuggestions && !isTypedWord) {
             return applyAlpha(color, mAlphaObsoleted);
         }
         return color;
@@ -438,7 +441,7 @@
             // {@link SuggestionStripView#onClick(View)}.
             wordView.setTag(indexInSuggestedWords);
             wordView.setText(getStyledSuggestedWord(suggestedWords, indexInSuggestedWords));
-            wordView.setTextColor(getSuggestionTextColor(positionInStrip, suggestedWords));
+            wordView.setTextColor(getSuggestionTextColor(suggestedWords, indexInSuggestedWords));
             if (SuggestionStripView.DBG) {
                 mDebugInfoViews.get(positionInStrip).setText(
                         suggestedWords.getDebugString(indexInSuggestedWords));
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
index 60f1c7a..5270845 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
@@ -22,7 +22,6 @@
  * An object that gives basic control of a suggestion strip and some info on it.
  */
 public interface SuggestionStripViewAccessor {
-    public boolean hasSuggestionStripView();
     public void showAddToDictionaryHint(final String word);
     public boolean isShowingAddToDictionaryHint();
     public void dismissAddToDictionaryHint();
diff --git a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java
new file mode 100644
index 0000000..ee9718a
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Utilities to manage executors.
+ */
+public class ExecutorUtils {
+    private static final ConcurrentHashMap<String, PrioritizedSerialExecutor>
+            sExecutorMap = CollectionUtils.newConcurrentHashMap();
+    /**
+     * Gets the executor for the given dictionary name.
+     */
+    public static PrioritizedSerialExecutor getExecutor(final String dictName) {
+        PrioritizedSerialExecutor executor = sExecutorMap.get(dictName);
+        if (executor == null) {
+            synchronized(sExecutorMap) {
+                executor = new PrioritizedSerialExecutor();
+                sExecutorMap.put(dictName, executor);
+            }
+        }
+        return executor;
+    }
+
+    /**
+     * Shutdowns all executors and removes all executors from the executor map for testing.
+     */
+    @UsedForTesting
+    public static void shutdownAllExecutors() {
+        synchronized(sExecutorMap) {
+            for (final PrioritizedSerialExecutor executor : sExecutorMap.values()) {
+                executor.shutdown();
+                sExecutorMap.remove(executor);
+            }
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
index a1d6415..562ff9e 100644
--- a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
+++ b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
@@ -37,9 +37,9 @@
     // non-0. Thus, it's not meaningful to compare 10, 100, and so on.
     // TODO: Revise the logic in ForgettingCurveUtils in native code.
     private static final int UNIGRAM_PROBABILITY_FOR_VALID_WORD = 100;
-    private static final int UNIGRAM_PROBABILITY_FOR_OOV_WORD = 10;
-    private static final int BIGRAM_PROBABILITY_FOR_VALID_WORD = 0;
-    private static final int BIGRAM_PROBABILITY_FOR_OOV_WORD = 0;
+    private static final int UNIGRAM_PROBABILITY_FOR_OOV_WORD = Dictionary.NOT_A_PROBABILITY;
+    private static final int BIGRAM_PROBABILITY_FOR_VALID_WORD = 10;
+    private static final int BIGRAM_PROBABILITY_FOR_OOV_WORD = Dictionary.NOT_A_PROBABILITY;
 
     public final String mTargetWord;
     public final int[] mWord0;
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
index e1b3534..bc4ca8e 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
+++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
@@ -992,7 +992,16 @@
             }
         }
         if (character != NOT_AN_INDEX) {
-            codePointBuf[index] = proximityInfo->getCodePointOf(character);
+            const int codePoint = proximityInfo->getCodePointOf(character);
+            if (codePoint == NOT_A_CODE_POINT) {
+                AKLOGE("Key index(%d) is not found. Cannot construct most probable string",
+                        character);
+                ASSERT(false);
+                // Make the length zero, which means most probable string won't be used.
+                index = 0;
+                break;
+            }
+            codePointBuf[index] = codePoint;
             index++;
         }
         sumLogProbability += minLogProbability;
diff --git a/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h b/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h
index 59748c8..a8dab9f 100644
--- a/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h
+++ b/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h
@@ -44,8 +44,6 @@
 
     virtual float getMultiWordCostMultiplier() const = 0;
 
-    virtual int getLastDecayedTime() const = 0;
-
     virtual void readHeaderValueOrQuestionMark(const char *const key, int *outValue,
             int outValueSize) const = 0;
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp
index cd22430..5df2096 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp
@@ -37,7 +37,8 @@
     if (outProbability) {
         if (bigramEntry.hasHistoricalInfo()) {
             *outProbability =
-                    ForgettingCurveUtils::decodeProbability(bigramEntry.getHistoricalInfo());
+                    ForgettingCurveUtils::decodeProbability(bigramEntry.getHistoricalInfo(),
+                            mHeaderPolicy);
         } else {
             *outProbability = bigramEntry.getProbability();
         }
@@ -160,8 +161,8 @@
             }
         } else if (bigramEntry.hasHistoricalInfo()) {
             const HistoricalInfo historicalInfo = ForgettingCurveUtils::createHistoricalInfoToSave(
-                    bigramEntry.getHistoricalInfo());
-            if (ForgettingCurveUtils::needsToKeep(&historicalInfo)) {
+                    bigramEntry.getHistoricalInfo(), mHeaderPolicy);
+            if (ForgettingCurveUtils::needsToKeep(&historicalInfo, mHeaderPolicy)) {
                 const BigramEntry updatedBigramEntry =
                         bigramEntry.updateHistoricalInfoAndGetEntry(&historicalInfo);
                 if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) {
@@ -230,7 +231,8 @@
     if (mHeaderPolicy->hasHistoricalInfoOfWords()) {
         const HistoricalInfo updatedHistoricalInfo =
                 ForgettingCurveUtils::createUpdatedHistoricalInfo(
-                        originalBigramEntry->getHistoricalInfo(), newProbability, timestamp);
+                        originalBigramEntry->getHistoricalInfo(), newProbability, timestamp,
+                        mHeaderPolicy);
         return originalBigramEntry->updateHistoricalInfoAndGetEntry(&updatedHistoricalInfo);
     } else {
         return originalBigramEntry->updateProbabilityAndGetEntry(newProbability);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
index 3ce57d9..7c7b05c 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
@@ -18,7 +18,7 @@
 
 namespace latinime {
 
-// Note that these are corresponding definitions in Java side in FormatSpec.FileHeader.
+// Note that these are corresponding definitions in Java side in DictionaryHeader.
 const char *const HeaderPolicy::MULTIPLE_WORDS_DEMOTION_RATE_KEY = "MULTIPLE_WORDS_DEMOTION_RATE";
 const char *const HeaderPolicy::REQUIRES_GERMAN_UMLAUT_PROCESSING_KEY =
         "REQUIRES_GERMAN_UMLAUT_PROCESSING";
@@ -33,8 +33,26 @@
 // count.
 const char *const HeaderPolicy::HAS_HISTORICAL_INFO_KEY = "HAS_HISTORICAL_INFO";
 const char *const HeaderPolicy::LOCALE_KEY = "locale"; // match Java declaration
+const char *const HeaderPolicy::FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP_KEY =
+        "FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP";
+const char *const HeaderPolicy::FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY =
+        "FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID";
+const char *const HeaderPolicy::FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS_KEY =
+        "FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS";
+
+const char *const HeaderPolicy::MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_COUNT";
+const char *const HeaderPolicy::MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_COUNT";
+
 const int HeaderPolicy::DEFAULT_MULTIPLE_WORDS_DEMOTION_RATE = 100;
 const float HeaderPolicy::MULTIPLE_WORD_COST_MULTIPLIER_SCALE = 100.0f;
+const int HeaderPolicy::DEFAULT_FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP = 4;
+const int HeaderPolicy::DEFAULT_FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID = 0;
+// 4 days
+const int HeaderPolicy::DEFAULT_FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS =
+        4 * 24 * 60 * 60;
+
+const int HeaderPolicy::DEFAULT_MAX_UNIGRAM_COUNT = 10000;
+const int HeaderPolicy::DEFAULT_MAX_BIGRAM_COUNT = 10000;
 
 // Used for logging. Question mark is used to indicate that the key is not found.
 void HeaderPolicy::readHeaderValueOrQuestionMark(const char *const key, int *outValue,
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
index fc34761..6682424 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
@@ -52,7 +52,20 @@
               mExtendedRegionSize(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap,
                       EXTENDED_REGION_SIZE_KEY, 0 /* defaultValue */)),
               mHasHistoricalInfoOfWords(HeaderReadWriteUtils::readBoolAttributeValue(
-                      &mAttributeMap, HAS_HISTORICAL_INFO_KEY, false /* defaultValue */)) {}
+                      &mAttributeMap, HAS_HISTORICAL_INFO_KEY, false /* defaultValue */)),
+              mForgettingCurveOccurrencesToLevelUp(HeaderReadWriteUtils::readIntAttributeValue(
+                      &mAttributeMap, FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP_KEY,
+                      DEFAULT_FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP)),
+              mForgettingCurveProbabilityValuesTableId(HeaderReadWriteUtils::readIntAttributeValue(
+                      &mAttributeMap, FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY,
+                      DEFAULT_FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID)),
+              mForgettingCurveDurationToLevelDown(HeaderReadWriteUtils::readIntAttributeValue(
+                      &mAttributeMap, FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS_KEY,
+                      DEFAULT_FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS)),
+              mMaxUnigramCount(HeaderReadWriteUtils::readIntAttributeValue(
+                      &mAttributeMap, MAX_UNIGRAM_COUNT_KEY, DEFAULT_MAX_UNIGRAM_COUNT)),
+              mMaxBigramCount(HeaderReadWriteUtils::readIntAttributeValue(
+                      &mAttributeMap, MAX_BIGRAM_COUNT_KEY, DEFAULT_MAX_BIGRAM_COUNT)) {}
 
     // Constructs header information using an attribute map.
     HeaderPolicy(const FormatUtils::FORMAT_VERSION dictFormatVersion,
@@ -71,8 +84,20 @@
                       DATE_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)),
               mUnigramCount(0), mBigramCount(0), mExtendedRegionSize(0),
               mHasHistoricalInfoOfWords(HeaderReadWriteUtils::readBoolAttributeValue(
-                      &mAttributeMap, HAS_HISTORICAL_INFO_KEY, false /* defaultValue */)) {
-        }
+                      &mAttributeMap, HAS_HISTORICAL_INFO_KEY, false /* defaultValue */)),
+              mForgettingCurveOccurrencesToLevelUp(HeaderReadWriteUtils::readIntAttributeValue(
+                      &mAttributeMap, FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP_KEY,
+                      DEFAULT_FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP)),
+              mForgettingCurveProbabilityValuesTableId(HeaderReadWriteUtils::readIntAttributeValue(
+                      &mAttributeMap, FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY,
+                      DEFAULT_FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID)),
+              mForgettingCurveDurationToLevelDown(HeaderReadWriteUtils::readIntAttributeValue(
+                      &mAttributeMap, FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS_KEY,
+                      DEFAULT_FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS)),
+              mMaxUnigramCount(HeaderReadWriteUtils::readIntAttributeValue(
+                      &mAttributeMap, MAX_UNIGRAM_COUNT_KEY, DEFAULT_MAX_UNIGRAM_COUNT)),
+              mMaxBigramCount(HeaderReadWriteUtils::readIntAttributeValue(
+                      &mAttributeMap, MAX_BIGRAM_COUNT_KEY, DEFAULT_MAX_BIGRAM_COUNT)) {}
 
     // Temporary dummy header.
     HeaderPolicy()
@@ -80,7 +105,9 @@
               mAttributeMap(), mLocale(CharUtils::EMPTY_STRING), mMultiWordCostMultiplier(0.0f),
               mRequiresGermanUmlautProcessing(false), mIsDecayingDict(false),
               mDate(0), mLastDecayedTime(0), mUnigramCount(0), mBigramCount(0),
-              mExtendedRegionSize(0), mHasHistoricalInfoOfWords(false) {}
+              mExtendedRegionSize(0), mHasHistoricalInfoOfWords(false),
+              mForgettingCurveOccurrencesToLevelUp(0), mForgettingCurveProbabilityValuesTableId(0),
+              mForgettingCurveDurationToLevelDown(0), mMaxUnigramCount(0), mMaxBigramCount(0) {}
 
     ~HeaderPolicy() {}
 
@@ -159,6 +186,26 @@
         return &mAttributeMap;
     }
 
+    AK_FORCE_INLINE int getForgettingCurveOccurrencesToLevelUp() const {
+        return mForgettingCurveOccurrencesToLevelUp;
+    }
+
+    AK_FORCE_INLINE int getForgettingCurveProbabilityValuesTableId() const {
+        return mForgettingCurveProbabilityValuesTableId;
+    }
+
+    AK_FORCE_INLINE int getForgettingCurveDurationToLevelDown() const {
+        return mForgettingCurveDurationToLevelDown;
+    }
+
+    AK_FORCE_INLINE int getMaxUnigramCount() const {
+        return mMaxUnigramCount;
+    }
+
+    AK_FORCE_INLINE int getMaxBigramCount() const {
+        return mMaxBigramCount;
+    }
+
     void readHeaderValueOrQuestionMark(const char *const key,
             int *outValue, int outValueSize) const;
 
@@ -183,8 +230,18 @@
     static const char *const EXTENDED_REGION_SIZE_KEY;
     static const char *const HAS_HISTORICAL_INFO_KEY;
     static const char *const LOCALE_KEY;
+    static const char *const FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP_KEY;
+    static const char *const FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY;
+    static const char *const FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS_KEY;
+    static const char *const MAX_UNIGRAM_COUNT_KEY;
+    static const char *const MAX_BIGRAM_COUNT_KEY;
     static const int DEFAULT_MULTIPLE_WORDS_DEMOTION_RATE;
     static const float MULTIPLE_WORD_COST_MULTIPLIER_SCALE;
+    static const int DEFAULT_FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP;
+    static const int DEFAULT_FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID;
+    static const int DEFAULT_FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS;
+    static const int DEFAULT_MAX_UNIGRAM_COUNT;
+    static const int DEFAULT_MAX_BIGRAM_COUNT;
 
     const FormatUtils::FORMAT_VERSION mDictFormatVersion;
     const HeaderReadWriteUtils::DictionaryFlags mDictionaryFlags;
@@ -200,6 +257,11 @@
     const int mBigramCount;
     const int mExtendedRegionSize;
     const bool mHasHistoricalInfoOfWords;
+    const int mForgettingCurveOccurrencesToLevelUp;
+    const int mForgettingCurveProbabilityValuesTableId;
+    const int mForgettingCurveDurationToLevelDown;
+    const int mMaxUnigramCount;
+    const int mMaxBigramCount;
 
     const std::vector<int> readLocale() const;
     float readMultipleWordCostMultiplier() const;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp
index cb9d450..279f5b3 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp
@@ -23,6 +23,13 @@
 const BigramEntry BigramDictContent::getBigramEntryAndAdvancePosition(
         int *const bigramEntryPos) const {
     const BufferWithExtendableBuffer *const bigramListBuffer = getContentBuffer();
+    if (*bigramEntryPos < 0 || *bigramEntryPos >=  bigramListBuffer->getTailPosition()) {
+        AKLOGE("Invalid bigram entry position. bigramEntryPos: %d, bufSize: %d",
+                *bigramEntryPos, bigramListBuffer->getTailPosition());
+        ASSERT(false);
+        return BigramEntry(false /* hasNext */, NOT_A_PROBABILITY,
+                Ver4DictConstants::NOT_A_TERMINAL_ID);
+    }
     const int bigramFlags = bigramListBuffer->readUintAndAdvancePosition(
             Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE, bigramEntryPos);
     const bool hasNext = (bigramFlags & Ver4DictConstants::BIGRAM_HAS_NEXT_MASK) != 0;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp
index 29972a4..64d7bc0 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp
@@ -24,6 +24,19 @@
         int *const outCodePoint, int *const outCodePointCount, int *const outProbability,
         bool *const outhasNext, int *const shortcutEntryPos) const {
     const BufferWithExtendableBuffer *const shortcutListBuffer = getContentBuffer();
+    if (*shortcutEntryPos < 0 || *shortcutEntryPos >=  shortcutListBuffer->getTailPosition()) {
+        AKLOGE("Invalid shortcut entry position. shortcutEntryPos: %d, bufSize: %d",
+                *shortcutEntryPos, shortcutListBuffer->getTailPosition());
+        ASSERT(false);
+        if (outhasNext) {
+            *outhasNext = false;
+        }
+        if (outCodePointCount) {
+            *outCodePointCount = 0;
+        }
+        return;
+    }
+
     const int shortcutFlags = shortcutListBuffer->readUintAndAdvancePosition(
             Ver4DictConstants::SHORTCUT_FLAGS_FIELD_SIZE, shortcutEntryPos);
     if (outProbability) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp
index 17fc948..f149781 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp
@@ -65,7 +65,7 @@
                 mProbabilityDictContent->getProbabilityEntry(terminalId);
         if (probabilityEntry.hasHistoricalInfo()) {
             probability = ForgettingCurveUtils::decodeProbability(
-                    probabilityEntry.getHistoricalInfo());
+                    probabilityEntry.getHistoricalInfo(), mHeaderPolicy);
         } else {
             probability = probabilityEntry.getProbability();
         }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h
index 9d93245..1db9ea0 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h
@@ -26,6 +26,7 @@
 namespace latinime {
 
 class BufferWithExtendableBuffer;
+class HeaderPolicy;
 class ProbabilityDictContent;
 
 /*
@@ -35,8 +36,10 @@
 class Ver4PatriciaTrieNodeReader : public PtNodeReader {
  public:
     Ver4PatriciaTrieNodeReader(const BufferWithExtendableBuffer *const buffer,
-            const ProbabilityDictContent *const probabilityDictContent)
-            : mBuffer(buffer), mProbabilityDictContent(probabilityDictContent) {}
+            const ProbabilityDictContent *const probabilityDictContent,
+            const HeaderPolicy *const headerPolicy)
+            : mBuffer(buffer), mProbabilityDictContent(probabilityDictContent),
+              mHeaderPolicy(headerPolicy) {}
 
     ~Ver4PatriciaTrieNodeReader() {}
 
@@ -50,6 +53,7 @@
 
     const BufferWithExtendableBuffer *const mBuffer;
     const ProbabilityDictContent *const mProbabilityDictContent;
+    const HeaderPolicy *const mHeaderPolicy;
 
     const PtNodeParams fetchPtNodeInfoFromBufferAndProcessMovedPtNode(const int ptNodePos,
             const int siblingNodePos) const;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp
index 32576cf..f24c2e1 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp
@@ -159,7 +159,7 @@
                     toBeUpdatedPtNodeParams->getTerminalId());
     if (originalProbabilityEntry.hasHistoricalInfo()) {
         const HistoricalInfo historicalInfo = ForgettingCurveUtils::createHistoricalInfoToSave(
-                originalProbabilityEntry.getHistoricalInfo());
+                originalProbabilityEntry.getHistoricalInfo(), mHeaderPolicy);
         const ProbabilityEntry probabilityEntry =
                 originalProbabilityEntry.createEntryWithUpdatedHistoricalInfo(&historicalInfo);
         if (!mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry(
@@ -168,7 +168,7 @@
                     toBeUpdatedPtNodeParams->getTerminalId());
             return false;
         }
-        const bool isValid = ForgettingCurveUtils::needsToKeep(&historicalInfo);
+        const bool isValid = ForgettingCurveUtils::needsToKeep(&historicalInfo, mHeaderPolicy);
         if (!isValid) {
             if (!markPtNodeAsWillBecomeNonTerminal(toBeUpdatedPtNodeParams)) {
                 AKLOGE("Cannot mark PtNode as willBecomeNonTerminal.");
@@ -382,10 +382,11 @@
         const ProbabilityEntry *const originalProbabilityEntry, const int newProbability,
         const int timestamp) const {
     // TODO: Consolidate historical info and probability.
-    if (mBuffers->getHeaderPolicy()->hasHistoricalInfoOfWords()) {
+    if (mHeaderPolicy->hasHistoricalInfoOfWords()) {
         const HistoricalInfo updatedHistoricalInfo =
                 ForgettingCurveUtils::createUpdatedHistoricalInfo(
-                        originalProbabilityEntry->getHistoricalInfo(), newProbability, timestamp);
+                        originalProbabilityEntry->getHistoricalInfo(), newProbability, timestamp,
+                        mHeaderPolicy);
         return originalProbabilityEntry->createEntryWithUpdatedHistoricalInfo(
                 &updatedHistoricalInfo);
     } else {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h
index 66845bb..f01b3af 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h
@@ -28,6 +28,7 @@
 namespace latinime {
 
 class BufferWithExtendableBuffer;
+class HeaderPolicy;
 class Ver4BigramListPolicy;
 class Ver4DictBuffers;
 class Ver4PatriciaTrieNodeReader;
@@ -40,10 +41,11 @@
 class Ver4PatriciaTrieNodeWriter : public PtNodeWriter {
  public:
     Ver4PatriciaTrieNodeWriter(BufferWithExtendableBuffer *const trieBuffer,
-            Ver4DictBuffers *const buffers, const PtNodeReader *const ptNodeReader,
+            Ver4DictBuffers *const buffers, const HeaderPolicy *const headerPolicy,
+            const PtNodeReader *const ptNodeReader,
             const PtNodeArrayReader *const ptNodeArrayReader,
             Ver4BigramListPolicy *const bigramPolicy, Ver4ShortcutListPolicy *const shortcutPolicy)
-            : mTrieBuffer(trieBuffer), mBuffers(buffers),
+            : mTrieBuffer(trieBuffer), mBuffers(buffers), mHeaderPolicy(headerPolicy),
               mReadingHelper(ptNodeReader, ptNodeArrayReader), mBigramPolicy(bigramPolicy),
               mShortcutPolicy(shortcutPolicy) {}
 
@@ -116,6 +118,7 @@
 
     BufferWithExtendableBuffer *const mTrieBuffer;
     Ver4DictBuffers *const mBuffers;
+    const HeaderPolicy *const mHeaderPolicy;
     DynamicPtReadingHelper mReadingHelper;
     Ver4BigramListPolicy *const mBigramPolicy;
     Ver4ShortcutListPolicy *const mShortcutPolicy;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
index b5d80be..4d1b0da 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
@@ -329,11 +329,15 @@
         snprintf(outResult, maxResultLength, "%d", mBigramCount);
     } else if (strncmp(query, MAX_UNIGRAM_COUNT_QUERY, compareLength) == 0) {
         snprintf(outResult, maxResultLength, "%d",
-                mHeaderPolicy->isDecayingDict() ? ForgettingCurveUtils::MAX_UNIGRAM_COUNT :
+                mHeaderPolicy->isDecayingDict() ?
+                        ForgettingCurveUtils::getUnigramCountHardLimit(
+                                mHeaderPolicy->getMaxUnigramCount()) :
                         static_cast<int>(Ver4DictConstants::MAX_DICTIONARY_SIZE));
     } else if (strncmp(query, MAX_BIGRAM_COUNT_QUERY, compareLength) == 0) {
         snprintf(outResult, maxResultLength, "%d",
-                mHeaderPolicy->isDecayingDict() ? ForgettingCurveUtils::MAX_BIGRAM_COUNT :
+                mHeaderPolicy->isDecayingDict() ?
+                        ForgettingCurveUtils::getBigramCountHardLimit(
+                                mHeaderPolicy->getMaxBigramCount()) :
                         static_cast<int>(Ver4DictConstants::MAX_DICTIONARY_SIZE));
     }
 }
@@ -382,7 +386,8 @@
                     bigramWord1CodePoints + codePointCount);
             const HistoricalInfo *const historicalInfo = bigramEntry.getHistoricalInfo();
             const int probability = bigramEntry.hasHistoricalInfo() ?
-                    ForgettingCurveUtils::decodeProbability(bigramEntry.getHistoricalInfo()) :
+                    ForgettingCurveUtils::decodeProbability(
+                            bigramEntry.getHistoricalInfo(), mHeaderPolicy) :
                     bigramEntry.getProbability();
             bigrams.push_back(WordProperty::BigramProperty(&word1, probability,
                     historicalInfo->getTimeStamp(), historicalInfo->getLevel(),
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h
index 7796e2d..639c153 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h
@@ -47,10 +47,10 @@
                       mBuffers.get()->getTerminalPositionLookupTable(), mHeaderPolicy),
               mShortcutPolicy(mBuffers.get()->getMutableShortcutDictContent(),
                       mBuffers.get()->getTerminalPositionLookupTable()),
-              mNodeReader(mDictBuffer, mBuffers.get()->getProbabilityDictContent()),
+              mNodeReader(mDictBuffer, mBuffers.get()->getProbabilityDictContent(), mHeaderPolicy),
               mPtNodeArrayReader(mDictBuffer),
-              mNodeWriter(mDictBuffer, mBuffers.get(), &mNodeReader, &mPtNodeArrayReader,
-                      &mBigramPolicy, &mShortcutPolicy),
+              mNodeWriter(mDictBuffer, mBuffers.get(), mHeaderPolicy, &mNodeReader,
+                      &mPtNodeArrayReader, &mBigramPolicy, &mShortcutPolicy),
               mUpdatingHelper(mDictBuffer, &mNodeReader, &mNodeWriter),
               mWritingHelper(mBuffers.get()),
               mUnigramCount(mHeaderPolicy->getUnigramCount()),
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
index 93053c3..3907c84 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
@@ -74,14 +74,15 @@
         const HeaderPolicy *const headerPolicy, Ver4DictBuffers *const buffersToWrite,
         int *const outUnigramCount, int *const outBigramCount) {
     Ver4PatriciaTrieNodeReader ptNodeReader(mBuffers->getTrieBuffer(),
-            mBuffers->getProbabilityDictContent());
+            mBuffers->getProbabilityDictContent(), headerPolicy);
     Ver4PtNodeArrayReader ptNodeArrayReader(mBuffers->getTrieBuffer());
     Ver4BigramListPolicy bigramPolicy(mBuffers->getMutableBigramDictContent(),
             mBuffers->getTerminalPositionLookupTable(), headerPolicy);
     Ver4ShortcutListPolicy shortcutPolicy(mBuffers->getMutableShortcutDictContent(),
             mBuffers->getTerminalPositionLookupTable());
     Ver4PatriciaTrieNodeWriter ptNodeWriter(mBuffers->getWritableTrieBuffer(),
-            mBuffers, &ptNodeReader, &ptNodeArrayReader, &bigramPolicy, &shortcutPolicy);
+            mBuffers, headerPolicy, &ptNodeReader, &ptNodeArrayReader, &bigramPolicy,
+            &shortcutPolicy);
 
     DynamicPtReadingHelper readingHelper(&ptNodeReader, &ptNodeArrayReader);
     readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos);
@@ -95,12 +96,11 @@
     }
     const int unigramCount = traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted
             .getValidUnigramCount();
-    if (headerPolicy->isDecayingDict()
-            && unigramCount > ForgettingCurveUtils::MAX_UNIGRAM_COUNT_AFTER_GC) {
-        if (!truncateUnigrams(&ptNodeReader, &ptNodeWriter,
-                ForgettingCurveUtils::MAX_UNIGRAM_COUNT_AFTER_GC)) {
+    const int maxUnigramCount = headerPolicy->getMaxUnigramCount();
+    if (headerPolicy->isDecayingDict() && unigramCount > maxUnigramCount) {
+        if (!truncateUnigrams(&ptNodeReader, &ptNodeWriter, maxUnigramCount)) {
             AKLOGE("Cannot remove unigrams. current: %d, max: %d", unigramCount,
-                    ForgettingCurveUtils::MAX_UNIGRAM_COUNT_AFTER_GC);
+                    maxUnigramCount);
             return false;
         }
     }
@@ -113,11 +113,10 @@
         return false;
     }
     const int bigramCount = traversePolicyToUpdateBigramProbability.getValidBigramEntryCount();
-    if (headerPolicy->isDecayingDict()
-            && bigramCount > ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC) {
-        if (!truncateBigrams(ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC)) {
-            AKLOGE("Cannot remove bigrams. current: %d, max: %d", bigramCount,
-                    ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC);
+    const int maxBigramCount = headerPolicy->getMaxBigramCount();
+    if (headerPolicy->isDecayingDict() && bigramCount > maxBigramCount) {
+        if (!truncateBigrams(maxBigramCount)) {
+            AKLOGE("Cannot remove bigrams. current: %d, max: %d", bigramCount, maxBigramCount);
             return false;
         }
     }
@@ -126,7 +125,8 @@
     PtNodeWriter::DictPositionRelocationMap dictPositionRelocationMap;
     readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos);
     Ver4PatriciaTrieNodeWriter ptNodeWriterForNewBuffers(buffersToWrite->getWritableTrieBuffer(),
-            buffersToWrite, &ptNodeReader, &ptNodeArrayReader, &bigramPolicy, &shortcutPolicy);
+            buffersToWrite, headerPolicy, &ptNodeReader, &ptNodeArrayReader, &bigramPolicy,
+            &shortcutPolicy);
     DynamicPtGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer
             traversePolicyToPlaceAndWriteValidPtNodesToBuffer(&ptNodeWriterForNewBuffers,
                     buffersToWrite->getWritableTrieBuffer(), &dictPositionRelocationMap);
@@ -137,14 +137,14 @@
 
     // Create policy instances for the GCed dictionary.
     Ver4PatriciaTrieNodeReader newPtNodeReader(buffersToWrite->getTrieBuffer(),
-            buffersToWrite->getProbabilityDictContent());
+            buffersToWrite->getProbabilityDictContent(), headerPolicy);
     Ver4PtNodeArrayReader newPtNodeArrayreader(buffersToWrite->getTrieBuffer());
     Ver4BigramListPolicy newBigramPolicy(buffersToWrite->getMutableBigramDictContent(),
             buffersToWrite->getTerminalPositionLookupTable(), headerPolicy);
     Ver4ShortcutListPolicy newShortcutPolicy(buffersToWrite->getMutableShortcutDictContent(),
             buffersToWrite->getTerminalPositionLookupTable());
     Ver4PatriciaTrieNodeWriter newPtNodeWriter(buffersToWrite->getWritableTrieBuffer(),
-            buffersToWrite, &newPtNodeReader, &newPtNodeArrayreader, &newBigramPolicy,
+            buffersToWrite, headerPolicy, &newPtNodeReader, &newPtNodeArrayreader, &newBigramPolicy,
             &newShortcutPolicy);
     // Re-assign terminal IDs for valid terminal PtNodes.
     TerminalPositionLookupTable::TerminalIdMap terminalIdMap;
@@ -202,8 +202,9 @@
         const ProbabilityEntry probabilityEntry =
                 mBuffers->getProbabilityDictContent()->getProbabilityEntry(i);
         const int probability = probabilityEntry.hasHistoricalInfo() ?
-                ForgettingCurveUtils::decodeProbability(probabilityEntry.getHistoricalInfo()) :
-                        probabilityEntry.getProbability();
+                ForgettingCurveUtils::decodeProbability(
+                        probabilityEntry.getHistoricalInfo(), mBuffers->getHeaderPolicy()) :
+                probabilityEntry.getProbability();
         priorityQueue.push(DictProbability(terminalPos, probability,
                 probabilityEntry.getHistoricalInfo()->getTimeStamp()));
     }
@@ -245,8 +246,9 @@
                 continue;
             }
             const int probability = bigramEntry.hasHistoricalInfo() ?
-                    ForgettingCurveUtils::decodeProbability(bigramEntry.getHistoricalInfo()) :
-                            bigramEntry.getProbability();
+                    ForgettingCurveUtils::decodeProbability(
+                            bigramEntry.getHistoricalInfo(), mBuffers->getHeaderPolicy()) :
+                    bigramEntry.getProbability();
             priorityQueue.push(DictProbability(entryPos, probability,
                     bigramEntry.getHistoricalInfo()->getTimeStamp()));
         }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp
index d58d259..35e05d7 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp
@@ -19,33 +19,29 @@
 #include <cmath>
 #include <stdlib.h>
 
-#include "suggest/core/policy/dictionary_header_structure_policy.h"
+#include "suggest/policyimpl/dictionary/header/header_policy.h"
 #include "suggest/policyimpl/dictionary/utils/probability_utils.h"
 #include "utils/time_keeper.h"
 
 namespace latinime {
 
-const int ForgettingCurveUtils::MAX_UNIGRAM_COUNT = 12000;
-const int ForgettingCurveUtils::MAX_UNIGRAM_COUNT_AFTER_GC = 10000;
-const int ForgettingCurveUtils::MAX_BIGRAM_COUNT = 12000;
-const int ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC = 10000;
-
-const int ForgettingCurveUtils::MAX_COMPUTED_PROBABILITY = 127;
+const int ForgettingCurveUtils::MULTIPLIER_TWO_IN_PROBABILITY_SCALE = 8;
 const int ForgettingCurveUtils::DECAY_INTERVAL_SECONDS = 2 * 60 * 60;
 
 const int ForgettingCurveUtils::MAX_LEVEL = 3;
-const int ForgettingCurveUtils::MAX_COUNT = 3;
 const int ForgettingCurveUtils::MIN_VALID_LEVEL = 1;
-const int ForgettingCurveUtils::TIME_STEP_DURATION_IN_SECONDS = 6 * 60 * 60;
 const int ForgettingCurveUtils::MAX_ELAPSED_TIME_STEP_COUNT = 15;
 const int ForgettingCurveUtils::DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD = 14;
 
+const float ForgettingCurveUtils::UNIGRAM_COUNT_HARD_LIMIT_WEIGHT = 1.2;
+const float ForgettingCurveUtils::BIGRAM_COUNT_HARD_LIMIT_WEIGHT = 1.2;
+
 const ForgettingCurveUtils::ProbabilityTable ForgettingCurveUtils::sProbabilityTable;
 
 // TODO: Revise the logic to decide the initial probability depending on the given probability.
 /* static */ const HistoricalInfo ForgettingCurveUtils::createUpdatedHistoricalInfo(
         const HistoricalInfo *const originalHistoricalInfo,
-        const int newProbability, const int timestamp) {
+        const int newProbability, const int timestamp, const HeaderPolicy *const headerPolicy) {
     if (newProbability != NOT_A_PROBABILITY && originalHistoricalInfo->getLevel() == 0) {
         return HistoricalInfo(timestamp, MIN_VALID_LEVEL /* level */, 0 /* count */);
     } else if (!originalHistoricalInfo->isValid()) {
@@ -53,7 +49,7 @@
         return HistoricalInfo(timestamp, 0 /* level */, 1 /* count */);
     } else {
         const int updatedCount = originalHistoricalInfo->getCount() + 1;
-        if (updatedCount > MAX_COUNT) {
+        if (updatedCount >= headerPolicy->getForgettingCurveOccurrencesToLevelUp()) {
             // The count exceeds the max value the level can be incremented.
             if (originalHistoricalInfo->getLevel() >= MAX_LEVEL) {
                 // The level is already max.
@@ -71,9 +67,11 @@
 }
 
 /* static */ int ForgettingCurveUtils::decodeProbability(
-        const HistoricalInfo *const historicalInfo) {
-    const int elapsedTimeStepCount = getElapsedTimeStepCount(historicalInfo->getTimeStamp());
-    return sProbabilityTable.getProbability(historicalInfo->getLevel(),
+        const HistoricalInfo *const historicalInfo, const HeaderPolicy *const headerPolicy) {
+    const int elapsedTimeStepCount = getElapsedTimeStepCount(historicalInfo->getTimeStamp(),
+            headerPolicy->getForgettingCurveDurationToLevelDown());
+    return sProbabilityTable.getProbability(
+            headerPolicy->getForgettingCurveProbabilityValuesTableId(), historicalInfo->getLevel(),
             min(max(elapsedTimeStepCount, 0), MAX_ELAPSED_TIME_STEP_COUNT));
 }
 
@@ -82,24 +80,31 @@
     if (unigramProbability == NOT_A_PROBABILITY) {
         return NOT_A_PROBABILITY;
     } else if (bigramProbability == NOT_A_PROBABILITY) {
-        return min(backoff(unigramProbability), MAX_COMPUTED_PROBABILITY);
+        return min(backoff(unigramProbability), MAX_PROBABILITY);
     } else {
-        return min(max(unigramProbability, bigramProbability), MAX_COMPUTED_PROBABILITY);
+        // TODO: Investigate better way to handle bigram probability.
+        return min(max(unigramProbability, bigramProbability + MULTIPLIER_TWO_IN_PROBABILITY_SCALE),
+                MAX_PROBABILITY);
     }
 }
 
-/* static */ bool ForgettingCurveUtils::needsToKeep(const HistoricalInfo *const historicalInfo) {
+/* static */ bool ForgettingCurveUtils::needsToKeep(const HistoricalInfo *const historicalInfo,
+        const HeaderPolicy *const headerPolicy) {
     return historicalInfo->getLevel() > 0
-            || getElapsedTimeStepCount(historicalInfo->getTimeStamp())
-                    < DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD;
+            || getElapsedTimeStepCount(historicalInfo->getTimeStamp(),
+                    headerPolicy->getForgettingCurveDurationToLevelDown())
+                            < DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD;
 }
 
 /* static */ const HistoricalInfo ForgettingCurveUtils::createHistoricalInfoToSave(
-        const HistoricalInfo *const originalHistoricalInfo) {
+        const HistoricalInfo *const originalHistoricalInfo,
+        const HeaderPolicy *const headerPolicy) {
     if (originalHistoricalInfo->getTimeStamp() == NOT_A_TIMESTAMP) {
         return HistoricalInfo();
     }
-    const int elapsedTimeStep = getElapsedTimeStepCount(originalHistoricalInfo->getTimeStamp());
+    const int durationToLevelDownInSeconds = headerPolicy->getForgettingCurveDurationToLevelDown();
+    const int elapsedTimeStep = getElapsedTimeStepCount(
+            originalHistoricalInfo->getTimeStamp(), durationToLevelDownInSeconds);
     if (elapsedTimeStep <= MAX_ELAPSED_TIME_STEP_COUNT) {
         // No need to update historical info.
         return *originalHistoricalInfo;
@@ -108,19 +113,18 @@
     const int maxLevelDownAmonut = elapsedTimeStep / (MAX_ELAPSED_TIME_STEP_COUNT + 1);
     const int levelDownAmount = (maxLevelDownAmonut >= originalHistoricalInfo->getLevel()) ?
             originalHistoricalInfo->getLevel() : maxLevelDownAmonut;
-    const int adjustedTimestamp = originalHistoricalInfo->getTimeStamp() +
-            levelDownAmount * (MAX_ELAPSED_TIME_STEP_COUNT + 1) * TIME_STEP_DURATION_IN_SECONDS;
-    return HistoricalInfo(adjustedTimestamp,
+    const int adjustedTimestampInSeconds = originalHistoricalInfo->getTimeStamp() +
+            levelDownAmount * durationToLevelDownInSeconds;
+    return HistoricalInfo(adjustedTimestampInSeconds,
             originalHistoricalInfo->getLevel() - levelDownAmount, 0 /* count */);
 }
 
 /* static */ bool ForgettingCurveUtils::needsToDecay(const bool mindsBlockByDecay,
-        const int unigramCount, const int bigramCount,
-        const DictionaryHeaderStructurePolicy *const headerPolicy) {
-    if (unigramCount >= ForgettingCurveUtils::MAX_UNIGRAM_COUNT) {
+        const int unigramCount, const int bigramCount, const HeaderPolicy *const headerPolicy) {
+    if (unigramCount >= getUnigramCountHardLimit(headerPolicy->getMaxUnigramCount())) {
         // Unigram count exceeds the limit.
         return true;
-    } else if (bigramCount >= ForgettingCurveUtils::MAX_BIGRAM_COUNT) {
+    } else if (bigramCount >= getBigramCountHardLimit(headerPolicy->getMaxBigramCount())) {
         // Bigram count exceeds the limit.
         return true;
     }
@@ -137,36 +141,70 @@
 
 // See comments in ProbabilityUtils::backoff().
 /* static */ int ForgettingCurveUtils::backoff(const int unigramProbability) {
-    if (unigramProbability == NOT_A_PROBABILITY) {
-        return NOT_A_PROBABILITY;
-    } else {
-        return max(unigramProbability - 8, 0);
+    // See TODO comments in ForgettingCurveUtils::getProbability().
+    return unigramProbability;
+}
+
+/* static */ int ForgettingCurveUtils::getElapsedTimeStepCount(const int timestamp,
+        const int durationToLevelDownInSeconds) {
+    const int elapsedTimeInSeconds = TimeKeeper::peekCurrentTime() - timestamp;
+    const int timeStepDurationInSeconds =
+            durationToLevelDownInSeconds / (MAX_ELAPSED_TIME_STEP_COUNT + 1);
+    return elapsedTimeInSeconds / timeStepDurationInSeconds;
+}
+
+const int ForgettingCurveUtils::ProbabilityTable::PROBABILITY_TABLE_COUNT = 4;
+const int ForgettingCurveUtils::ProbabilityTable::WEAK_PROBABILITY_TABLE_ID = 0;
+const int ForgettingCurveUtils::ProbabilityTable::MODEST_PROBABILITY_TABLE_ID = 1;
+const int ForgettingCurveUtils::ProbabilityTable::STRONG_PROBABILITY_TABLE_ID = 2;
+const int ForgettingCurveUtils::ProbabilityTable::AGGRESSIVE_PROBABILITY_TABLE_ID = 3;
+const int ForgettingCurveUtils::ProbabilityTable::WEAK_MAX_PROBABILITY = 127;
+const int ForgettingCurveUtils::ProbabilityTable::MODEST_BASE_PROBABILITY = 32;
+const int ForgettingCurveUtils::ProbabilityTable::STRONG_BASE_PROBABILITY = 35;
+const int ForgettingCurveUtils::ProbabilityTable::AGGRESSIVE_BASE_PROBABILITY = 40;
+
+
+ForgettingCurveUtils::ProbabilityTable::ProbabilityTable() : mTables() {
+    mTables.resize(PROBABILITY_TABLE_COUNT);
+    for (int tableId = 0; tableId < PROBABILITY_TABLE_COUNT; ++tableId) {
+        mTables[tableId].resize(MAX_LEVEL + 1);
+        for (int level = 0; level <= MAX_LEVEL; ++level) {
+            mTables[tableId][level].resize(MAX_ELAPSED_TIME_STEP_COUNT + 1);
+            const float initialProbability = getBaseProbabilityForLevel(tableId, level);
+            const float endProbability = getBaseProbabilityForLevel(tableId, level - 1);
+            for (int timeStepCount = 0; timeStepCount <= MAX_ELAPSED_TIME_STEP_COUNT;
+                    ++timeStepCount) {
+                if (level == 0) {
+                    mTables[tableId][level][timeStepCount] = NOT_A_PROBABILITY;
+                    continue;
+                }
+                const float probability = initialProbability
+                        * powf(initialProbability / endProbability,
+                                -1.0f * static_cast<float>(timeStepCount)
+                                        / static_cast<float>(MAX_ELAPSED_TIME_STEP_COUNT + 1));
+                mTables[tableId][level][timeStepCount] =
+                        min(max(static_cast<int>(probability), 1), MAX_PROBABILITY);
+            }
+        }
     }
 }
 
-/* static */ int ForgettingCurveUtils::getElapsedTimeStepCount(const int timestamp) {
-    return (TimeKeeper::peekCurrentTime() - timestamp) / TIME_STEP_DURATION_IN_SECONDS;
-}
-
-ForgettingCurveUtils::ProbabilityTable::ProbabilityTable() : mTable() {
-    mTable.resize(MAX_LEVEL + 1);
-    for (int level = 0; level <= MAX_LEVEL; ++level) {
-        mTable[level].resize(MAX_ELAPSED_TIME_STEP_COUNT + 1);
-        const float initialProbability =
-                static_cast<float>(MAX_COMPUTED_PROBABILITY / (1 << (MAX_LEVEL - level)));
-        for (int timeStepCount = 0; timeStepCount <= MAX_ELAPSED_TIME_STEP_COUNT; ++timeStepCount) {
-            if (level == 0) {
-                mTable[level][timeStepCount] = NOT_A_PROBABILITY;
-                continue;
-            }
-            const int elapsedTime = timeStepCount * TIME_STEP_DURATION_IN_SECONDS;
-            const float probability = initialProbability
-                    * powf(2.0f, -1.0f * static_cast<float>(elapsedTime)
-                            / static_cast<float>(TIME_STEP_DURATION_IN_SECONDS
-                                    * (MAX_ELAPSED_TIME_STEP_COUNT + 1)));
-            mTable[level][timeStepCount] =
-                    min(max(static_cast<int>(probability), 1), MAX_COMPUTED_PROBABILITY);
-        }
+/* static */ int ForgettingCurveUtils::ProbabilityTable::getBaseProbabilityForLevel(
+        const int tableId, const int level) {
+    if (tableId == WEAK_PROBABILITY_TABLE_ID) {
+        // Max probability is 127.
+        return static_cast<float>(WEAK_MAX_PROBABILITY / (1 << (MAX_LEVEL - level)));
+    } else if (tableId == MODEST_PROBABILITY_TABLE_ID) {
+        // Max probability is 128.
+        return static_cast<float>(MODEST_BASE_PROBABILITY * (level + 1));
+    } else if (tableId == STRONG_PROBABILITY_TABLE_ID) {
+        // Max probability is 140.
+        return static_cast<float>(STRONG_BASE_PROBABILITY * (level + 1));
+    } else if (tableId == AGGRESSIVE_PROBABILITY_TABLE_ID) {
+        // Max probability is 160.
+        return static_cast<float>(AGGRESSIVE_BASE_PROBABILITY * (level + 1));
+    } else {
+        return NOT_A_PROBABILITY;
     }
 }
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h
index b373534..bb86909 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h
@@ -24,31 +24,39 @@
 
 namespace latinime {
 
-class DictionaryHeaderStructurePolicy;
+class HeaderPolicy;
 
 class ForgettingCurveUtils {
  public:
-    static const int MAX_UNIGRAM_COUNT;
-    static const int MAX_UNIGRAM_COUNT_AFTER_GC;
-    static const int MAX_BIGRAM_COUNT;
-    static const int MAX_BIGRAM_COUNT_AFTER_GC;
-
     static const HistoricalInfo createUpdatedHistoricalInfo(
             const HistoricalInfo *const originalHistoricalInfo, const int newProbability,
-            const int timestamp);
+            const int timestamp, const HeaderPolicy *const headerPolicy);
 
     static const HistoricalInfo createHistoricalInfoToSave(
-            const HistoricalInfo *const originalHistoricalInfo);
+            const HistoricalInfo *const originalHistoricalInfo,
+            const HeaderPolicy *const headerPolicy);
 
-    static int decodeProbability(const HistoricalInfo *const historicalInfo);
+    static int decodeProbability(const HistoricalInfo *const historicalInfo,
+            const HeaderPolicy *const headerPolicy);
 
     static int getProbability(const int encodedUnigramProbability,
             const int encodedBigramProbability);
 
-    static bool needsToKeep(const HistoricalInfo *const historicalInfo);
+    static bool needsToKeep(const HistoricalInfo *const historicalInfo,
+            const HeaderPolicy *const headerPolicy);
 
     static bool needsToDecay(const bool mindsBlockByDecay, const int unigramCount,
-            const int bigramCount, const DictionaryHeaderStructurePolicy *const headerPolicy);
+            const int bigramCount, const HeaderPolicy *const headerPolicy);
+
+    AK_FORCE_INLINE static int getUnigramCountHardLimit(const int maxUnigramCount) {
+        return static_cast<int>(static_cast<float>(maxUnigramCount)
+                * UNIGRAM_COUNT_HARD_LIMIT_WEIGHT);
+    }
+
+    AK_FORCE_INLINE static int getBigramCountHardLimit(const int maxBigramCount) {
+        return static_cast<int>(static_cast<float>(maxBigramCount)
+                * BIGRAM_COUNT_HARD_LIMIT_WEIGHT);
+    }
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(ForgettingCurveUtils);
@@ -57,32 +65,46 @@
      public:
         ProbabilityTable();
 
-        int getProbability(const int level, const int elapsedTimeStepCount) const {
-            return mTable[level][elapsedTimeStepCount];
+        int getProbability(const int tableId, const int level,
+                const int elapsedTimeStepCount) const {
+            return mTables[tableId][level][elapsedTimeStepCount];
         }
 
      private:
         DISALLOW_COPY_AND_ASSIGN(ProbabilityTable);
 
-        std::vector<std::vector<int> > mTable;
+        static const int PROBABILITY_TABLE_COUNT;
+        static const int WEAK_PROBABILITY_TABLE_ID;
+        static const int MODEST_PROBABILITY_TABLE_ID;
+        static const int STRONG_PROBABILITY_TABLE_ID;
+        static const int AGGRESSIVE_PROBABILITY_TABLE_ID;
+
+        static const int WEAK_MAX_PROBABILITY;
+        static const int MODEST_BASE_PROBABILITY;
+        static const int STRONG_BASE_PROBABILITY;
+        static const int AGGRESSIVE_BASE_PROBABILITY;
+
+        std::vector<std::vector<std::vector<int> > > mTables;
+
+        static int getBaseProbabilityForLevel(const int tableId, const int level);
     };
 
-    static const int MAX_COMPUTED_PROBABILITY;
+    static const int MULTIPLIER_TWO_IN_PROBABILITY_SCALE;
     static const int DECAY_INTERVAL_SECONDS;
 
     static const int MAX_LEVEL;
-    static const int MAX_COUNT;
     static const int MIN_VALID_LEVEL;
-    static const int TIME_STEP_DURATION_IN_SECONDS;
     static const int MAX_ELAPSED_TIME_STEP_COUNT;
     static const int DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD;
-    static const int HALF_LIFE_TIME_IN_SECONDS;
+
+    static const float UNIGRAM_COUNT_HARD_LIMIT_WEIGHT;
+    static const float BIGRAM_COUNT_HARD_LIMIT_WEIGHT;
 
     static const ProbabilityTable sProbabilityTable;
 
     static int backoff(const int unigramProbability);
 
-    static int getElapsedTimeStepCount(const int timestamp);
+    static int getElapsedTimeStepCount(const int timestamp, const int durationToLevelDown);
 };
 } // namespace latinime
 #endif /* LATINIME_FORGETTING_CURVE_UTILS_H */
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
new file mode 100644
index 0000000..9939a43
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.ContextThemeWrapper;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
+import com.android.inputmethod.keyboard.KeyboardLayoutSet.Builder;
+import com.android.inputmethod.keyboard.KeyboardSwitcher.KeyboardTheme;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.RichInputMethodManager;
+import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
+import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.ResourceUtils;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+@SmallTest
+public class KeyboardLayoutSetTestsBase extends AndroidTestCase {
+    private static final int NUMBER_OF_SUBTYPES = 63;
+    private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 40;
+
+    private static final KeyboardTheme DEFAULT_KEYBOARD_THEME =
+            KeyboardSwitcher.KEYBOARD_THEMES[KeyboardSwitcher.THEME_INDEX_DEFAULT];
+
+    // All input method subtypes of LatinIME.
+    private final ArrayList<InputMethodSubtype> mAllSubtypesList = CollectionUtils.newArrayList();
+    private final ArrayList<InputMethodSubtype> mAsciiCapableSubtypesList =
+            CollectionUtils.newArrayList();
+
+    private Context mThemeContext;
+    private int mScreenMetrics;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mScreenMetrics = mContext.getResources().getInteger(R.integer.config_screen_metrics);
+
+        mThemeContext = new ContextThemeWrapper(mContext, DEFAULT_KEYBOARD_THEME.mStyleId);
+        RichInputMethodManager.init(mThemeContext);
+        final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
+
+        final InputMethodInfo imi = richImm.getInputMethodInfoOfThisIme();
+        final int subtypeCount = imi.getSubtypeCount();
+        for (int index = 0; index < subtypeCount; index++) {
+            final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+            mAllSubtypesList.add(subtype);
+            if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) {
+                mAsciiCapableSubtypesList.add(subtype);
+            }
+        }
+    }
+
+    protected final boolean isPhone() {
+        return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_PHONE
+                || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE;
+    }
+
+    private static String toString(final ArrayList<InputMethodSubtype> subtypeList) {
+        final StringBuilder sb = new StringBuilder();
+        for (int index = 0; index < subtypeList.size(); index++) {
+            final InputMethodSubtype subtype = subtypeList.get(index);
+            sb.append((index + 1) + ": ");
+            sb.append(SubtypeLocaleUtils.getSubtypeNameForLogging(subtype));
+            sb.append("\n");
+        }
+        return sb.toString();
+    }
+
+    public final void testAllSubtypesCount() {
+        assertEquals(toString(mAllSubtypesList),
+                NUMBER_OF_SUBTYPES, mAllSubtypesList.size());
+    }
+
+    public final void testAsciiCapableSubtypesCount() {
+        assertEquals(toString(mAsciiCapableSubtypesList),
+                NUMBER_OF_ASCII_CAPABLE_SUBTYPES, mAsciiCapableSubtypesList.size());
+    }
+
+    protected final InputMethodSubtype getSubtype(final Locale locale,
+            final String keyboardLayout) {
+        for (final InputMethodSubtype subtype : mAllSubtypesList) {
+            final Locale subtypeLocale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
+            final String subtypeLayout = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
+            if (locale.equals(subtypeLocale) && keyboardLayout.equals(subtypeLayout)) {
+                // Found subtype that matches locale and keyboard layout.
+                return subtype;
+            }
+        }
+        for (final InputMethodSubtype subtype : mAsciiCapableSubtypesList) {
+            final Locale subtypeLocale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
+            if (locale.equals(subtypeLocale)) {
+                // Create additional subtype.
+                return AdditionalSubtypeUtils.createAdditionalSubtype(
+                        locale.toString(), keyboardLayout, null /* extraValue */);
+            }
+        }
+        throw new RuntimeException(
+                "Unknown subtype: locale=" + locale + " keyboardLayout=" + keyboardLayout);
+    }
+
+    protected final KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype,
+            final EditorInfo editorInfo) {
+        return createKeyboardLayoutSet(subtype, editorInfo, false /* isShortcutImeEnabled */,
+                false /* showsVoiceInputKey */, false /* isLanguageSwitchKeyEnabled */);
+    }
+
+    protected final KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype,
+            final EditorInfo editorInfo, final boolean isShortcutImeEnabled,
+            final boolean showsVoiceInputKey, final boolean isLanguageSwitchKeyEnabled) {
+        final Context context = mThemeContext;
+        final Resources res = context.getResources();
+        final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
+        final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
+        final Builder builder = new Builder(context, editorInfo);
+        builder.setKeyboardGeometry(keyboardWidth, keyboardHeight)
+                .setSubtype(subtype)
+                .setOptions(isShortcutImeEnabled, showsVoiceInputKey, isLanguageSwitchKeyEnabled);
+        return builder.build();
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSetTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSetTests.java
new file mode 100644
index 0000000..17d7687
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSetTests.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.RichInputMethodManager;
+import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.RunInLocale;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+@SmallTest
+public final class KeyboardTextsSetTests extends AndroidTestCase {
+    // All input method subtypes of LatinIME.
+    private List<InputMethodSubtype> mAllSubtypesList;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        RichInputMethodManager.init(getContext());
+        final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
+
+        final ArrayList<InputMethodSubtype> allSubtypesList = CollectionUtils.newArrayList();
+        final InputMethodInfo imi = richImm.getInputMethodInfoOfThisIme();
+        final int subtypeCount = imi.getSubtypeCount();
+        for (int index = 0; index < subtypeCount; index++) {
+            final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+            allSubtypesList.add(subtype);
+        }
+        mAllSubtypesList = Collections.unmodifiableList(allSubtypesList);
+    }
+
+    // Test that the text {@link KeyboardTextsSet#SWITCH_TO_ALPHA_KEY_LABEL} exists for all
+    // subtypes. The text is needed to implement Emoji Keyboard, see
+    // {@link KeyboardSwitcher#setEmojiKeyboard()}.
+    public void testSwitchToAlphaKeyLabel() {
+        final KeyboardTextsSet textsSet = new KeyboardTextsSet();
+        for (final InputMethodSubtype subtype : mAllSubtypesList) {
+            final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
+            textsSet.setLocale(locale);
+            final String switchToAlphaKeyLabel = textsSet.getText(
+                    KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL);
+            assertNotNull("Switch to alpha key label of " + locale, switchToAlphaKeyLabel);
+            assertFalse("Switch to alpha key label of " + locale, switchToAlphaKeyLabel.isEmpty());
+        }
+    }
+
+    private static final String[] TEXT_NAMES_FROM_RESOURCE = {
+        // Labels for action.
+        "label_go_key",
+        "label_send_key",
+        "label_next_key",
+        "label_done_key",
+        "label_previous_key",
+        // Other labels.
+        "label_pause_key",
+        "label_wait_key",
+    };
+
+    // Test that the text from resources are correctly loaded for all subtypes.
+    public void testTextFromResources() {
+        final Context context = getContext();
+        final KeyboardTextsSet textsSet = new KeyboardTextsSet();
+        for (final InputMethodSubtype subtype : mAllSubtypesList) {
+            final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
+            textsSet.setLocale(locale);
+            final RunInLocale<Void> job = new RunInLocale<Void>() {
+                @Override
+                protected Void job(final Resources res) {
+                    textsSet.loadStringResources(context);
+                    return null;
+                }
+            };
+            job.runInLocale(context.getResources(), locale);
+            for (final String name : TEXT_NAMES_FROM_RESOURCE) {
+                final String text = textsSet.getText(name);
+                assertNotNull(name + " of " + locale, text);
+                assertFalse(name + " of " + locale, text.isEmpty());
+            }
+        }
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index f4b16a7..fa51236 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -150,7 +150,7 @@
         binaryDictionary.flushWithGC();
         binaryDictionary.close();
 
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(dictFile);
+        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(dictFile, 0, dictFile.length());
         try {
             final FusionDictionary dict =
                     dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index e21e340..0ee0fb5 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -251,7 +251,8 @@
 
         FusionDictionary dict = null;
         try {
-            final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, bufferType);
+            final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, 0, file.length(),
+                    bufferType);
             now = System.currentTimeMillis();
             dict = dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);
             diff  = System.currentTimeMillis() - now;
@@ -413,7 +414,8 @@
 
         long now = -1, diff = -1;
         try {
-            final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, bufferType);
+            final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, 0, file.length(),
+                    bufferType);
             now = System.currentTimeMillis();
             dictDecoder.readUnigramsAndBigramsBinary(resultWords, resultFreqs, resultBigrams);
             diff = System.currentTimeMillis() - now;
@@ -537,7 +539,8 @@
         addBigrams(dict, words, bigrams);
         timeWritingDictToFile(file, dict, formatOptions);
 
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, DictDecoder.USE_BYTEARRAY);
+        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, 0, file.length(),
+                DictDecoder.USE_BYTEARRAY);
         try {
             dictDecoder.openDictBuffer();
         } catch (IOException e) {
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java
index a85753e..9dc2b10 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java
@@ -68,7 +68,8 @@
         }
 
         assertNotNull(testFile);
-        final Ver2DictDecoder dictDecoder = new Ver2DictDecoder(testFile, factory);
+        final Ver2DictDecoder dictDecoder = new Ver2DictDecoder(testFile, 0, testFile.length(),
+                factory);
         try {
             dictDecoder.openDictBuffer();
         } catch (Exception e) {
@@ -110,7 +111,8 @@
             Log.e(TAG, "IOException while the creating temporary file", e);
         }
 
-        final Ver2DictDecoder dictDecoder = new Ver2DictDecoder(testFile, factory);
+        final Ver2DictDecoder dictDecoder = new Ver2DictDecoder(testFile, 0, testFile.length(),
+                factory);
 
         // the default return value of getBuffer() must be null.
         assertNull("the default return value of getBuffer() is not null",
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index b1239f0..6ace2de 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -267,13 +267,13 @@
             assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word));
         }
         forcePassingShortTime();
-        dict.decayIfNeeded();
+        dict.runGCIfRequired();
         dict.waitAllTasksForTests();
         for (final String word : words) {
             assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word));
         }
         forcePassingLongTime();
-        dict.decayIfNeeded();
+        dict.runGCIfRequired();
         dict.waitAllTasksForTests();
         for (final String word : words) {
             assertFalse(dict.isInUnderlyingBinaryDictionaryForTests(word));
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
index d1df81b..e31ac2a 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
@@ -192,7 +192,7 @@
                             new BufferedInputStream(new FileInputStream(decodedSpec.mFile)));
                 } else {
                     final DictDecoder dictDecoder = FormatSpec.getDictDecoder(decodedSpec.mFile,
-                            DictDecoder.USE_BYTEARRAY);
+                            0, decodedSpec.mFile.length(), DictDecoder.USE_BYTEARRAY);
                     if (report) {
                         System.out.println("Format : Binary dictionary format");
                         System.out.println("Packaging : " + decodedSpec.describeChain());
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
index 80d71fc..68d7850 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
@@ -264,7 +264,7 @@
     private static FusionDictionary readBinaryFile(final String binaryFilename)
             throws FileNotFoundException, IOException, UnsupportedFormatException {
         final File file = new File(binaryFilename);
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file);
+        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, 0, file.length());
         return dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);
     }
 
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/tools/dicttool/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
new file mode 100644
index 0000000..a4ad6b5
--- /dev/null
+++ b/tools/dicttool/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.personalization;
+
+public class PersonalizationHelper {
+    public static void currentTimeChangedForTesting(final int currentTimestamp) {
+    }
+}
diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
index 7a4f6f7..faf00b4 100644
--- a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
+++ b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
@@ -77,7 +77,8 @@
             assertEquals("Wrong decode spec", BinaryDictOffdeviceUtils.COMPRESSION, step);
         }
         assertEquals("Wrong decode spec", 3, decodeSpec.mDecoderSpec.size());
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(decodeSpec.mFile);
+        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(decodeSpec.mFile, 0,
+                decodeSpec.mFile.length());
         final FusionDictionary resultDict =
                 dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);
         assertEquals("Wrong version attribute", VERSION, resultDict.mOptions.mAttributes.get(
diff --git a/tools/make-keyboard-text/res/values-af/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-af/donottranslate-more-keys.xml
index ee96f44..5a90e62 100644
--- a/tools/make-keyboard-text/res/values-af/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-af/donottranslate-more-keys.xml
@@ -62,7 +62,6 @@
     <!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
          U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE -->
     <string name="more_keys_for_n">&#x00F1;,&#x0144;</string>
-    <string name="more_keys_for_y">&#x00FD;,&#x0177;,&#x00FF;,&#x0133;</string>
     <!-- U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
          U+0133: "ij" LATIN SMALL LIGATURE IJ -->
     <string name="more_keys_for_y">&#x00FD;,&#x0133;</string>