Merge "Remove Dict dependency on WordComposer and ProximityInfo"
diff --git a/dictionaries/de_wordlist.combined.gz b/dictionaries/de_wordlist.combined.gz
index 803211c..92c9554 100644
--- a/dictionaries/de_wordlist.combined.gz
+++ b/dictionaries/de_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz
index 1fa9b85..217660f 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 2e039ff..8aed9c5 100644
--- a/dictionaries/en_US_wordlist.combined.gz
+++ b/dictionaries/en_US_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_wordlist.combined.gz b/dictionaries/en_wordlist.combined.gz
index e845346..7fe6618 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 3391e64..71e7309 100644
--- a/dictionaries/es_wordlist.combined.gz
+++ b/dictionaries/es_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/fr_wordlist.combined.gz b/dictionaries/fr_wordlist.combined.gz
index 1b9fd73..afe44a6 100644
--- a/dictionaries/fr_wordlist.combined.gz
+++ b/dictionaries/fr_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/it_wordlist.combined.gz b/dictionaries/it_wordlist.combined.gz
index 5a5cbdc..ed58a12 100644
--- a/dictionaries/it_wordlist.combined.gz
+++ b/dictionaries/it_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/nl_wordlist.combined.gz b/dictionaries/nl_wordlist.combined.gz
index 37ba8ab..19c3a7e 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 ba71a55..2b84eec 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 02df1c1..7aac61e 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 bcd50ab..5bf9a60 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 401ad08..5e92662 100644
--- a/dictionaries/ru_wordlist.combined.gz
+++ b/dictionaries/ru_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/sv_wordlist.combined.gz b/dictionaries/sv_wordlist.combined.gz
index b6ebab3..db44ae4 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 306cea1..d3c8825 100644
--- a/dictionaries/tr_wordlist.combined.gz
+++ b/dictionaries/tr_wordlist.combined.gz
Binary files differ
diff --git a/java/res/raw/main_de.dict b/java/res/raw/main_de.dict
index 45b2883..c3c2cbe 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 5bbb857..b9e5bc7 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 fae1318..076d5aa 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 19532d9..0e86860 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 ff11b97..609ef13 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 9fa5044..c338651 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 76b5f80..d0af707 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.xml b/java/res/values-af/strings.xml
index cbdf434..ca8b816 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serwies (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradisioneel)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Geen taal nie (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index 1ac1f8c..9beccdf 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ሂንግሊሽ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ሰርቢያኛ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ተለምዷዊ)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (እስግ)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"ምንም ቋንቋ (ፊደላት)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ፊደላት (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ፊደላት (QWERTZ)"</string>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index dbab0ef..62d5f3d 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"هنجليزية (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"الصربية (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (التقليدية)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"بدون لغة (أبجدية)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"‏الأبجدية (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"‏الأبجدية (QWERTZ)"</string>
diff --git a/java/res/values-az-rAZ/strings.xml b/java/res/values-az-rAZ/strings.xml
index 463faf5..b13c905 100644
--- a/java/res/values-az-rAZ/strings.xml
+++ b/java/res/values-az-rAZ/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hingilis (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Ənənəvi)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Dil yoxdur (Əlifba)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Əlifba (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Əlifba (QWERTZ)"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 21df084..805014b 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Сръбска (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиционна клавиатура)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Без език (латиница)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиница (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиница (QWERTZ)"</string>
diff --git a/java/res/values-bn-rBD/strings.xml b/java/res/values-bn-rBD/strings.xml
index edd3b57..5487bb5 100644
--- a/java/res/values-bn-rBD/strings.xml
+++ b/java/res/values-bn-rBD/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"হিংলিশ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"সার্বিয়ান (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ঐতিহ্যবাহি)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"কোনো ভাষা নয় (বর্ণমালা)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"বর্ণমালা (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"বর্ণমালা (QWERTZ)"</string>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index 17615e5..ef792b8 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbi (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Cap idioma (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 996bc70..4fd0e71 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"srbština (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradiční)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktní)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Žádný jazyk (latinka)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinka (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinka (QWERTZ)"</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index fe1820b..0e92e47 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbisk (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionelt)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Intet sprog (Alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 13c0125..1ced6d0 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbisch (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionell)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompakt)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Keine Sprache (lat. Alphabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Lat. Alphabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Lat. Alphabet (QWERTZ)"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index f4f58e1..d9c4a40 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Σερβικά (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Παραδοσιακά)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Συμπαγές)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Καμία γλώσσα (Αλφάβητο)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Αλφάβητο (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Αλφάβητο (QWERTZ)"</string>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 249f8f3..3ed2826 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet (QWERTZ)"</string>
diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml
index 249f8f3..3ed2826 100644
--- a/java/res/values-en-rIN/strings.xml
+++ b/java/res/values-en-rIN/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet (QWERTZ)"</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index f0aebbe..8142b1d 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index a470bc9..a49ae31 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacto)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
diff --git a/java/res/values-et-rEE/strings.xml b/java/res/values-et-rEE/strings.xml
index ffbaba0..440331e 100644
--- a/java/res/values-et-rEE/strings.xml
+++ b/java/res/values-et-rEE/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi-inglise (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditsiooniline)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktne)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Keel puudub (tähestik)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Tähestik (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Tähestik (QWERTZ)"</string>
diff --git a/java/res/values-eu-rES/strings.xml b/java/res/values-eu-rES/strings.xml
index f9d1dfd..9496d95 100644
--- a/java/res/values-eu-rES/strings.xml
+++ b/java/res/values-eu-rES/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglisha (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbiarra (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradizionala)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Ez dago hizkuntzarik (alfabetoa)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabetoa (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabetoa (QWERTZ)"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index 3162784..a0390df 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"هندی انگلیسی (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"صربی (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (سنتی)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"بدون زبان (حروف الفبا)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"‏حروف الفبا (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"‏حروف الفبا (QWERTZ)"</string>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index b8fcd81..b133cc9 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindienglanti (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"serbialainen (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (perinteinen)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Ei kieltä (aakkoset)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Aakkoset (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Aakkoset (QWERTZ)"</string>
diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml
index e5492a1..ee52205 100644
--- a/java/res/values-fr-rCA/strings.xml
+++ b/java/res/values-fr-rCA/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbe (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionnel)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Aucune langue (alphabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet latin (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet latin (QWERTZ)"</string>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index df6167c..2a93c99 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi/Anglais (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbe (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionnel)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Aucune langue (latin)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet latin (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet latin (QWERTZ)"</string>
diff --git a/java/res/values-gl-rES/strings.xml b/java/res/values-gl-rES/strings.xml
index 57979b6..d266ad5 100644
--- a/java/res/values-gl-rES/strings.xml
+++ b/java/res/values-gl-rES/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 3cf27b5..9fc08ac 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"हिंग्लिश (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"सर्बियाई (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (पारंपरिक)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"भाषा उपलब्ध नहीं है (लैटिन वर्णाक्षर)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णाक्षर (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णाक्षर (QWERTZ)"</string>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index b13a997..7a5a665 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Srpski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionalni)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Nema jezika (abeceda)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abeceda (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abeceda (QWERTZ)"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 93099fe..71314b0 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (hindi-angol, <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Szerb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (hagyományos)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompakt)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nincs nyelv (ábécé)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Ábécé (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Ábécé (QWERTZ)"</string>
diff --git a/java/res/values-hy-rAM/strings.xml b/java/res/values-hy-rAM/strings.xml
index 920867d..7d30119 100644
--- a/java/res/values-hy-rAM/strings.xml
+++ b/java/res/values-hy-rAM/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Հինգլիշ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Սերբերեն (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ավանդական)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Ոչ մի լեզվով (Այբուբեն)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Այբուբեն (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Այբուբեն (QWERTZ)"</string>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index 7381cd6..bd86f01 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradisional)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Rapat)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Tidak ada bahasa (Abjad)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abjad (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abjad (QWERTZ)"</string>
diff --git a/java/res/values-is-rIS/strings.xml b/java/res/values-is-rIS/strings.xml
index affa407..2bc3bfb 100644
--- a/java/res/values-is-rIS/strings.xml
+++ b/java/res/values-is-rIS/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbneskt (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (hefðbundið)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Ekkert tungumál (stafróf)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Stafróf (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Stafróf (QWERTZ)"</string>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 5572b2c..2bca3b4 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbo (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradizionale)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compatto)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nessuna lingua (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index 468f7cb..c99d512 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"אנגלית הודית (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"סרבית (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (מסורתית)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (קומפקטית)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"ללא שפה (אלף-בית)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"‏אלף-בית (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"‏אלף-בית (QWERTZ)"</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 5ae8fcc..4caaae3 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ヒングリッシュ(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"セルビア語(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(伝統言語)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"言語なし(アルファベット)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"アルファベット(QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"アルファベット(QWERTZ)"</string>
diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml
index 4f221a9..dc2308c 100644
--- a/java/res/values-ka-rGE/strings.xml
+++ b/java/res/values-ka-rGE/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ჰინგლისური (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"სერბული (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ტრადიციული)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"ენის გარეშე (ანბანი)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ანბანი (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ანბანი (QWERTZ)"</string>
diff --git a/java/res/values-kk-rKZ/strings.xml b/java/res/values-kk-rKZ/strings.xml
index e92b7c2..838a07a 100644
--- a/java/res/values-kk-rKZ/strings.xml
+++ b/java/res/values-kk-rKZ/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Серб (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (дәстүрлі)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Тіл жоқ (әліпби)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Әліпби (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Әліпби (QWERTZ)"</string>
diff --git a/java/res/values-km-rKH/strings.xml b/java/res/values-km-rKH/strings.xml
index f1a9ecb..8aea853 100644
--- a/java/res/values-km-rKH/strings.xml
+++ b/java/res/values-km-rKH/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"សើប (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (អក្សរ​ពេញ)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (បង្រួម)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"គ្មាន​ភាសា (អក្សរ​ក្រម)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"តាម​លំដាប់​អក្សរក្រម (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"តាម​លំដាប់​អក្សរក្រម (QWERTZ)"</string>
diff --git a/java/res/values-kn-rIN/strings.xml b/java/res/values-kn-rIN/strings.xml
index 02f8fec..04d9b1c 100644
--- a/java/res/values-kn-rIN/strings.xml
+++ b/java/res/values-kn-rIN/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ಹಿಂಗ್ಲಿಷ್ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ಸರ್ಬಿಯನ್ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ಸಾಂಪ್ರದಾಯಿಕ)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ಕಾಂಪ್ಯಾಕ್ಟ್‌‌)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"ಯಾವುದೇ ಭಾಷೆಯಿಲ್ಲ (ವರ್ಣಮಾಲೆ)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ವರ್ಣಮಾಲೆ (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ವರ್ಣಮಾಲೆ (QWERTZ)"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 1c46120..e393517 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"인도 영어(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"세르비아어(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(번체)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"언어 없음(알파벳)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"알파벳(QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"알파벳(QWERTZ)"</string>
diff --git a/java/res/values-ky-rKG/strings.xml b/java/res/values-ky-rKG/strings.xml
index 3fd9062..90c2db3 100644
--- a/java/res/values-ky-rKG/strings.xml
+++ b/java/res/values-ky-rKG/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Сербче (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Салттык)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Тил жок (Алфавит)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Алфавит (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Алфавит (QWERTZ)"</string>
diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml
index 55f97c3..b23eb31 100644
--- a/java/res/values-lo-rLA/strings.xml
+++ b/java/res/values-lo-rLA/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ຮິງ​ລິສ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ເຊີ​ບຽນ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ດັ້ງ​ເດີມ)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"ບໍ່ມີພາສາ (ໂຕອັກສອນ)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ໂຕອັກສອນ (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ໂຕອັກສອນ (QWERTZ)"</string>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index d1782fa..cc8d0ea 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi ir anglų derinys (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbų k. (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicinė)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktiška)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Kalbos nėra (abėcėlė)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abėcėlė (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abėcėlė (QWERTZ)"</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index 8de28be..ed7e9d4 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi–angļu valoda (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbu (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionālā)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Nav valodas (alfabēts)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabēts (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabēts (QWERTZ)"</string>
diff --git a/java/res/values-mk-rMK/strings.xml b/java/res/values-mk-rMK/strings.xml
index e4ce6ec..f3503f7 100644
--- a/java/res/values-mk-rMK/strings.xml
+++ b/java/res/values-mk-rMK/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Српски (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиционален)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Компактна)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Нема јазик (азбука)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Азбука (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Азбука (QWERTZ)"</string>
diff --git a/java/res/values-ml-rIN/strings.xml b/java/res/values-ml-rIN/strings.xml
index c5b399b..acf00c2 100644
--- a/java/res/values-ml-rIN/strings.xml
+++ b/java/res/values-ml-rIN/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ഹിംഗ്ലീഷ് (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"സെർബിയൻ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (പരമ്പരാഗതം)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"ഭാഷയില്ല (അക്ഷരമാല)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"അക്ഷരമാല (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"അക്ഷരമാല (QWERTZ)"</string>
diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml
index 4b1ef62..938c90f 100644
--- a/java/res/values-mn-rMN/strings.xml
+++ b/java/res/values-mn-rMN/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Серьби хэл (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (уламжлалт)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Компакт)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Хэл байхгүй (Цагаан толгой)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Цагаан толгой (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Цагаан толгой (QWERTZ)"</string>
diff --git a/java/res/values-mr-rIN/strings.xml b/java/res/values-mr-rIN/strings.xml
index 7ebdd05..492226a 100644
--- a/java/res/values-mr-rIN/strings.xml
+++ b/java/res/values-mr-rIN/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"हिंग्लिश (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"सर्बियन (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (पारंपारिक)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"भाषा नाही (वर्णमाला)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णमाला (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णमाला (QWERTZ)"</string>
diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml
index 01781a4..f33bdb4 100644
--- a/java/res/values-ms-rMY/strings.xml
+++ b/java/res/values-ms-rMY/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Bahasa Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradisional)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Tiada bahasa (Abjad)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abjad (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abjad (QWERTZ)"</string>
diff --git a/java/res/values-my-rMM/strings.xml b/java/res/values-my-rMM/strings.xml
index 2b5a4d4..3e5a491 100644
--- a/java/res/values-my-rMM/strings.xml
+++ b/java/res/values-my-rMM/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ဟင်ဂလိပ် (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ဆားဘီယား (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ရိုးရာ)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"ဘာသာစကားမရှိ (ဗျည်းအက္ခရာ)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ဗျည်းအက္ခရာ (ကွာတီ)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ဗျည်းအက္ခရာ (ကွာတီ)"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index d69e1e9..849d7ab 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbisk (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradisjonelt)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompakt)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Ingen språk (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-ne-rNP/strings.xml b/java/res/values-ne-rNP/strings.xml
index 5afe6d1..ec17c4e 100644
--- a/java/res/values-ne-rNP/strings.xml
+++ b/java/res/values-ne-rNP/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"हिङ्लिस (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"सर्बियाई (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (परम्परागत)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"कुनै भाषा होइन (वर्णमाला)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णमाला (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णमाला (QWERTZ)"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index d5406a8..d09db3f 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi-Engels (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Servisch (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditioneel)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compact)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Geen taal (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index 30e87bc..39cf742 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"serbski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradycyjny)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktowa)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Bez języka (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index 512647a..e063cba 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sérvio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacto)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Sem idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 73ff94a..2e0cd3b 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Híndi-inglês (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sérvio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Nenhum idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index a87eeeb..8e3ebf2 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sârbă (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradițională)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Nicio limbă (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index 3aef94d..bd45c68 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Сербский (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (классическая)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Язык не определен (латиница)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиница (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиница (QWERTZ)"</string>
diff --git a/java/res/values-si-rLK/strings.xml b/java/res/values-si-rLK/strings.xml
index 847b423..2e5f3d9 100644
--- a/java/res/values-si-rLK/strings.xml
+++ b/java/res/values-si-rLK/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"හින්ග්ලිෂ් (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"සර්බියානු (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (සාම්ප්‍රදායික)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"භාෂාවක් නැත (අකාරාදිය)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"අකාරාදිය (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"අකාරාදිය (QWERTZ)"</string>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index 01e0500..80c91c8 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"srbčina (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradičná)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Žiadny jazyk (latinka)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinka (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinka (QWERTZ)"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 8872b56..396334c 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindujska angleščina (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Srbščina (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionalna)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Brez jezika (latinice)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinica (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinica (QWERTZ)"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 0d08933..28f6cae 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -95,13 +95,15 @@
     <string name="subtype_en_US" msgid="6160452336634534239">"енглески (САД)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"шпански (САД)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"хенглески"</string>
-    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"srpski"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"српски (латиница)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"енглески (УК) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"енглески (САД) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"шпански (САД) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"хенглески (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"srpski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"српски (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиционални)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Нема језика (абецеда)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Абецеда (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Абецеда (QWERTZ)"</string>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index f3945a3..0c7d1a7 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi/engelska (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbiska (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionell)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompakt)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Inget språk (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index 10198ea..ae1a3a8 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Kiserbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cha Jadi)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Hakuna lugha (Alfabeti)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeti (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeti (QWERTZ)"</string>
diff --git a/java/res/values-ta-rIN/strings.xml b/java/res/values-ta-rIN/strings.xml
index fc9f4a7..0a2d138 100644
--- a/java/res/values-ta-rIN/strings.xml
+++ b/java/res/values-ta-rIN/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ஹிங்கிலிஷ் (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"செர்பியன் (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (பாரம்பரியமானது)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"மொழியில்லை (அகரவரிசை)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"அகரவரிசை (க்வெர்டி)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"அகரவரிசை (க்வெர்ட்ச்)"</string>
diff --git a/java/res/values-te-rIN/strings.xml b/java/res/values-te-rIN/strings.xml
index c3d732c..15e9aaf 100644
--- a/java/res/values-te-rIN/strings.xml
+++ b/java/res/values-te-rIN/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"హింగ్లీష్ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"సెర్బియన్ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (సాంప్రదాయకం)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"భాష లేదు (ఆల్ఫాబెట్)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ఆల్ఫాబెట్ (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ఆల్ఫాబెట్ (QWERTZ)"</string>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index 674c03b..5c239db 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ภาษาอังกฤษผสมกับฮินดู (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"เซอร์เบีย (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ดั้งเดิม)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"ไม่มีภาษา (ตัวอักษรละติน)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ตัวอักษร (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ตัวอักษร (QWERTZ)"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index 6fa06c9..8d755e4 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Walang wika (Alpabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alpabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alpabeto (QWERTZ)"</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index 5802d79..86426fa 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hingilizce (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sırpça (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Geleneksel)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Dil yok (Alfabe)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabe (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabe (QWERTZ)"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index 639658d..33ab584 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хінґліш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"сербська (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиційна)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Стандартна (латиниця)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиниця (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиниця (QWERTZ)"</string>
diff --git a/java/res/values-ur-rPK/strings.xml b/java/res/values-ur-rPK/strings.xml
index fbe6626..f00e503 100644
--- a/java/res/values-ur-rPK/strings.xml
+++ b/java/res/values-ur-rPK/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ہنگلش (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"سربیائی (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (روایتی)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"کوئی زبان نہیں (الفابیٹ)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"‏حروف تہجی (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"‏حروف تہجی (QWERTZ)"</string>
diff --git a/java/res/values-uz-rUZ/strings.xml b/java/res/values-uz-rUZ/strings.xml
index b696e37..62143d7 100644
--- a/java/res/values-uz-rUZ/strings.xml
+++ b/java/res/values-uz-rUZ/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (an’anaviy)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Til aniqlanmadi (lotin)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Lotin (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Lotin (QWERTZ)"</string>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 1f9590e..fa0e11b 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Tiếng Anh-Hindi (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Tiếng Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Truyền thống)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"Không ngôn ngữ nào (Bảng chữ cái)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Bảng chữ cái (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Bảng chữ cái (QWERTZ)"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index 7524e32..4562c4b 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"印地英语(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"塞尔维亚语(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>布局)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(传统)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"无语言(字母)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string>
diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml
index 3c013ad..e584a88 100644
--- a/java/res/values-zh-rHK/strings.xml
+++ b/java/res/values-zh-rHK/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"印度英文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"塞爾維亞文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (傳統)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"無語言 (字母)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 28bbc8a..aa534c8 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -102,6 +102,8 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"印度英文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"塞爾維亞文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (傳統)"</string>
+    <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
+    <skip />
     <string name="subtype_no_language" msgid="7137390094240139495">"無語言 (字母)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index 0c80aee..0177129 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -102,6 +102,7 @@
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"I-Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Isi-Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"Isi-<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradition)"</string>
+    <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Okuqoqene ndawonye)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Alikho ulimi (Alfabhethi)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabhethi (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabhethi (QWERTZ)"</string>
diff --git a/java/res/values/donottranslate-debug-settings.xml b/java/res/values/donottranslate-debug-settings.xml
index c612010..491043f 100644
--- a/java/res/values/donottranslate-debug-settings.xml
+++ b/java/res/values/donottranslate-debug-settings.xml
@@ -43,6 +43,10 @@
     <string name="prefs_key_popup_dismiss_end_y_scale_settings">Key popup dismiss end Y scale</string>
     <!-- Title of the settings for reading an external dictionary file -->
     <string name="prefs_read_external_dictionary">Read external dictionary file</string>
+    <!-- Title of the settings to enable keyboard resizing -->
+    <string name="prefs_resize_keyboard">Enable keyboard resizing</string>
+    <!-- Title of the settings for setting keyboard height -->
+    <string name="prefs_keyboard_height_scale">Keyboard height scale</string>
     <!-- Message to show when there are no files to install as an external dictionary [CHAR LIMIT=100] -->
     <string name="read_external_dictionary_no_files_message">No dictionary files in the Downloads folder</string>
     <!-- Title of the dialog that selects a file to install as an external dictionary [CHAR LIMIT=50] -->
diff --git a/java/res/xml/prefs_screen_debug.xml b/java/res/xml/prefs_screen_debug.xml
index 13edf3e..905bc04 100644
--- a/java/res/xml/prefs_screen_debug.xml
+++ b/java/res/xml/prefs_screen_debug.xml
@@ -76,6 +76,17 @@
         android:key="pref_key_preview_dismiss_duration"
         android:title="@string/prefs_key_popup_dismiss_duration_settings"
         latin:maxValue="100" /> <!-- milliseconds -->
+    <CheckBoxPreference
+        android:key="pref_resize_keyboard"
+        android:title="@string/prefs_resize_keyboard"
+        android:defaultValue="false"
+        android:persistent="true" />
+    <com.android.inputmethod.latin.settings.SeekBarDialogPreference
+        android:dependency="pref_resize_keyboard"
+        android:key="pref_keyboard_height_scale"
+        android:title="@string/prefs_keyboard_height_scale"
+        latin:minValue="50"
+        latin:maxValue="120" /> <!-- percentage -->
     <PreferenceScreen
         android:key="read_external_dictionary"
         android:title="@string/prefs_read_external_dictionary" />
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 3bcce4f..89acc3c 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -110,7 +110,7 @@
                 mThemeContext, editorInfo);
         final Resources res = mThemeContext.getResources();
         final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
-        final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
+        final int keyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues);
         builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
         builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
         builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey);
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 6feb1e7..b07693c 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -41,6 +41,8 @@
 
 import java.util.HashSet;
 
+import javax.annotation.Nullable;
+
 /**
  * A view that renders a virtual {@link Keyboard}.
  *
@@ -557,9 +559,10 @@
      * @param key key in the attached {@link Keyboard}.
      * @see #invalidateAllKeys
      */
-    public void invalidateKey(final Key key) {
-        if (mInvalidateAllKeys) return;
-        if (key == null) return;
+    public void invalidateKey(@Nullable final Key key) {
+        if (key == null || mInvalidateAllKeys) {
+            return;
+        }
         mInvalidatedKeys.add(key);
         final int x = key.getX() + getPaddingLeft();
         final int y = key.getY() + getPaddingTop();
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index f3aaaef..f23db04 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -39,7 +39,7 @@
 import com.android.inputmethod.accessibility.AccessibilityUtils;
 import com.android.inputmethod.accessibility.MainKeyboardAccessibilityDelegate;
 import com.android.inputmethod.annotations.ExternallyReferenced;
-import com.android.inputmethod.keyboard.internal.DrawingHandler;
+import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
 import com.android.inputmethod.keyboard.internal.DrawingPreviewPlacerView;
 import com.android.inputmethod.keyboard.internal.GestureFloatingTextDrawingPreview;
 import com.android.inputmethod.keyboard.internal.GestureTrailsDrawingPreview;
@@ -64,6 +64,9 @@
 import java.util.Locale;
 import java.util.WeakHashMap;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 /**
  * A view that is responsible for detecting key presses and touch movements.
  *
@@ -108,7 +111,7 @@
  * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
  */
 public final class MainKeyboardView extends KeyboardView implements PointerTracker.DrawingProxy,
-        MoreKeysPanel.Controller, DrawingHandler.Callbacks, TimerHandler.Callbacks {
+        MoreKeysPanel.Controller {
     private static final String TAG = MainKeyboardView.class.getSimpleName();
 
     /** Listener for {@link KeyboardActionListener}. */
@@ -164,11 +167,9 @@
     private final KeyDetector mKeyDetector;
     private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper;
 
-    private final TimerHandler mKeyTimerHandler;
+    private final TimerHandler mTimerHandler;
     private final int mLanguageOnSpacebarHorizontalMargin;
 
-    private final DrawingHandler mDrawingHandler = new DrawingHandler(this);
-
     private MainKeyboardAccessibilityDelegate mAccessibilityDelegate;
 
     public MainKeyboardView(final Context context, final AttributeSet attrs) {
@@ -178,7 +179,8 @@
     public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
         super(context, attrs, defStyle);
 
-        mDrawingPreviewPlacerView = new DrawingPreviewPlacerView(context, attrs);
+        final DrawingPreviewPlacerView drawingPreviewPlacerView =
+                new DrawingPreviewPlacerView(context, attrs);
 
         final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes(
                 attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
@@ -186,7 +188,7 @@
                 R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
         final int gestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
                 R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
-        mKeyTimerHandler = new TimerHandler(
+        mTimerHandler = new TimerHandler(
                 this, ignoreAltCodeKeyTimeout, gestureRecognitionUpdateTime);
 
         final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension(
@@ -196,7 +198,7 @@
         mKeyDetector = new KeyDetector(
                 keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
 
-        PointerTracker.init(mainKeyboardViewAttr, mKeyTimerHandler, this /* DrawingProxy */);
+        PointerTracker.init(mainKeyboardViewAttr, mTimerHandler, this /* DrawingProxy */);
 
         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
         final boolean forceNonDistinctMultitouch = prefs.getBoolean(
@@ -246,15 +248,17 @@
 
         mGestureFloatingTextDrawingPreview = new GestureFloatingTextDrawingPreview(
                 mainKeyboardViewAttr);
-        mGestureFloatingTextDrawingPreview.setDrawingView(mDrawingPreviewPlacerView);
+        mGestureFloatingTextDrawingPreview.setDrawingView(drawingPreviewPlacerView);
 
         mGestureTrailsDrawingPreview = new GestureTrailsDrawingPreview(mainKeyboardViewAttr);
-        mGestureTrailsDrawingPreview.setDrawingView(mDrawingPreviewPlacerView);
+        mGestureTrailsDrawingPreview.setDrawingView(drawingPreviewPlacerView);
 
         mSlidingKeyInputDrawingPreview = new SlidingKeyInputDrawingPreview(mainKeyboardViewAttr);
-        mSlidingKeyInputDrawingPreview.setDrawingView(mDrawingPreviewPlacerView);
+        mSlidingKeyInputDrawingPreview.setDrawingView(drawingPreviewPlacerView);
         mainKeyboardViewAttr.recycle();
 
+        mDrawingPreviewPlacerView = drawingPreviewPlacerView;
+
         final LayoutInflater inflater = LayoutInflater.from(getContext());
         mMoreKeysKeyboardContainer = inflater.inflate(moreKeysKeyboardLayoutId, null);
         mMoreKeysKeyboardForActionContainer = inflater.inflate(
@@ -307,17 +311,24 @@
         animatorToStart.setCurrentPlayTime(startTime);
     }
 
-    // Implements {@link TimerHander.Callbacks} method.
+    // Implements {@link DrawingProxy#startWhileTypingAnimation(int)}.
+    /**
+     * Called when a while-typing-animation should be started.
+     * @param fadeInOrOut {@link DrawingProxy#FADE_IN} starts while-typing-fade-in animation.
+     * {@link DrawingProxy#FADE_OUT} starts while-typing-fade-out animation.
+     */
     @Override
-    public void startWhileTypingFadeinAnimation() {
-        cancelAndStartAnimators(
-                mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator);
-    }
-
-    @Override
-    public void startWhileTypingFadeoutAnimation() {
-        cancelAndStartAnimators(
-                mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator);
+    public void startWhileTypingAnimation(final int fadeInOrOut) {
+        switch (fadeInOrOut) {
+        case DrawingProxy.FADE_IN:
+            cancelAndStartAnimators(
+                    mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator);
+            break;
+        case DrawingProxy.FADE_OUT:
+            cancelAndStartAnimators(
+                    mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator);
+            break;
+        }
     }
 
     @ExternallyReferenced
@@ -379,7 +390,7 @@
     @Override
     public void setKeyboard(final Keyboard keyboard) {
         // Remove any pending messages, except dismissing preview and key repeat.
-        mKeyTimerHandler.cancelLongPressTimers();
+        mTimerHandler.cancelLongPressTimers();
         super.setKeyboard(keyboard);
         mKeyDetector.setKeyboard(
                 keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
@@ -451,17 +462,10 @@
         windowContentView.addView(mDrawingPreviewPlacerView);
     }
 
-    // Implements {@link DrawingHandler.Callbacks} method.
     @Override
-    public void dismissAllKeyPreviews() {
-        mKeyPreviewChoreographer.dismissAllKeyPreviews();
-        PointerTracker.setReleasedKeyGraphicsToAllKeys();
-    }
-
-    @Override
-    public void showKeyPreview(final Key key) {
+    public void showKeyPreview(@Nonnull final Key key) {
         // If the key is invalid or has no key preview, we must not show key preview.
-        if (key == null || key.noKeyPreview()) {
+        if (key.noKeyPreview()) {
             return;
         }
         final Keyboard keyboard = getKeyboard();
@@ -480,22 +484,21 @@
                 getWidth(), mOriginCoords, mDrawingPreviewPlacerView, isHardwareAccelerated());
     }
 
-    // Implements {@link TimerHandler.Callbacks} method.
+    // Implements {@link DrawingProxy#dismissKeyPreviewWithoutDelay(Key)}.
     @Override
-    public void dismissKeyPreviewWithoutDelay(final Key key) {
+    public void dismissKeyPreviewWithoutDelay(@Nonnull final Key key) {
         mKeyPreviewChoreographer.dismissKeyPreview(key, false /* withAnimation */);
-        // To redraw key top letter.
         invalidateKey(key);
     }
 
     @Override
-    public void dismissKeyPreview(final Key key) {
-        if (!isHardwareAccelerated()) {
-            // TODO: Implement preference option to control key preview method and duration.
-            mDrawingHandler.dismissKeyPreview(mKeyPreviewDrawParams.getLingerTimeout(), key);
+    public void dismissKeyPreview(@Nonnull final Key key) {
+        if (isHardwareAccelerated()) {
+            mKeyPreviewChoreographer.dismissKeyPreview(key, true /* withAnimation */);
             return;
         }
-        mKeyPreviewChoreographer.dismissKeyPreview(key, true /* withAnimation */);
+        // TODO: Implement preference option to control key preview method and duration.
+        mTimerHandler.postDismissKeyPreview(key, mKeyPreviewDrawParams.getLingerTimeout());
     }
 
     public void setSlidingKeyInputPreviewEnabled(final boolean enabled) {
@@ -503,14 +506,13 @@
     }
 
     @Override
-    public void showSlidingKeyInputPreview(final PointerTracker tracker) {
+    public void showSlidingKeyInputPreview(@Nullable final PointerTracker tracker) {
         locatePreviewPlacerView();
-        mSlidingKeyInputDrawingPreview.setPreviewPosition(tracker);
-    }
-
-    @Override
-    public void dismissSlidingKeyInputPreview() {
-        mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview();
+        if (tracker != null) {
+            mSlidingKeyInputDrawingPreview.setPreviewPosition(tracker);
+        } else {
+            mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview();
+        }
     }
 
     private void setGesturePreviewMode(final boolean isGestureTrailEnabled,
@@ -519,20 +521,26 @@
         mGestureTrailsDrawingPreview.setPreviewEnabled(isGestureTrailEnabled);
     }
 
-    // Implements {@link DrawingHandler.Callbacks} method.
-    @Override
-    public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) {
+    public void showGestureFloatingPreviewText(@Nonnull final SuggestedWords suggestedWords,
+            final boolean dismissDelayed) {
         locatePreviewPlacerView();
-        mGestureFloatingTextDrawingPreview.setSuggetedWords(suggestedWords);
+        final GestureFloatingTextDrawingPreview gestureFloatingTextDrawingPreview =
+                mGestureFloatingTextDrawingPreview;
+        gestureFloatingTextDrawingPreview.setSuggetedWords(suggestedWords);
+        if (dismissDelayed) {
+            mTimerHandler.postDismissGestureFloatingPreviewText(
+                    mGestureFloatingPreviewTextLingerTimeout);
+        }
     }
 
-    public void dismissGestureFloatingPreviewText() {
-        locatePreviewPlacerView();
-        mDrawingHandler.dismissGestureFloatingPreviewText(mGestureFloatingPreviewTextLingerTimeout);
+    // Implements {@link DrawingProxy#dismissGestureFloatingPreviewTextWithoutDelay()}.
+    @Override
+    public void dismissGestureFloatingPreviewTextWithoutDelay() {
+        mGestureFloatingTextDrawingPreview.dismissGestureFloatingPreviewText();
     }
 
     @Override
-    public void showGestureTrail(final PointerTracker tracker,
+    public void showGestureTrail(@Nonnull final PointerTracker tracker,
             final boolean showsFloatingPreviewText) {
         locatePreviewPlacerView();
         if (showsFloatingPreviewText) {
@@ -599,13 +607,13 @@
         return moreKeysKeyboardView;
     }
 
-    // Implements {@link TimerHandler.Callbacks} method.
+    // Implements {@link DrawingProxy@onLongPress(PointerTracker)}.
     /**
      * Called when a key is long pressed.
      * @param tracker the pointer tracker which pressed the parent key
      */
     @Override
-    public void onLongPress(final PointerTracker tracker) {
+    public void onLongPress(@Nonnull final PointerTracker tracker) {
         if (isShowingMoreKeysPanel()) {
             return;
         }
@@ -660,7 +668,7 @@
         moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
         tracker.onShowMoreKeysPanel(moreKeysPanel);
         // TODO: Implement zoom in animation of more keys panel.
-        dismissKeyPreviewWithoutDelay(key);
+        mKeyPreviewChoreographer.dismissKeyPreview(key, false /* withAnimation */);
     }
 
     public boolean isInDraggingFinger() {
@@ -673,6 +681,12 @@
     @Override
     public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
         locatePreviewPlacerView();
+        // Dismiss another {@link MoreKeysPanel} that may be being showed.
+        onDismissMoreKeysPanel();
+        // Dismiss all key previews that may be being showed.
+        PointerTracker.setReleasedKeyGraphicsToAllKeys();
+        // Dismiss sliding key input preview that may be being showed.
+        mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview();
         panel.showInParent(mDrawingPreviewPlacerView);
         mMoreKeysPanel = panel;
     }
@@ -695,15 +709,15 @@
     }
 
     public void startDoubleTapShiftKeyTimer() {
-        mKeyTimerHandler.startDoubleTapShiftKeyTimer();
+        mTimerHandler.startDoubleTapShiftKeyTimer();
     }
 
     public void cancelDoubleTapShiftKeyTimer() {
-        mKeyTimerHandler.cancelDoubleTapShiftKeyTimer();
+        mTimerHandler.cancelDoubleTapShiftKeyTimer();
     }
 
     public boolean isInDoubleTapShiftKeyTimeout() {
-        return mKeyTimerHandler.isInDoubleTapShiftKeyTimeout();
+        return mTimerHandler.isInDoubleTapShiftKeyTimeout();
     }
 
     @Override
@@ -712,9 +726,9 @@
             return false;
         }
         if (mNonDistinctMultitouchHelper != null) {
-            if (me.getPointerCount() > 1 && mKeyTimerHandler.isInKeyRepeat()) {
+            if (me.getPointerCount() > 1 && mTimerHandler.isInKeyRepeat()) {
                 // Key repeating timer will be canceled if 2 or more keys are in action.
-                mKeyTimerHandler.cancelKeyRepeatTimers();
+                mTimerHandler.cancelKeyRepeatTimers();
             }
             // Non distinct multitouch screen support
             mNonDistinctMultitouchHelper.processMotionEvent(me, mKeyDetector);
@@ -738,11 +752,10 @@
     }
 
     public void cancelAllOngoingEvents() {
-        mKeyTimerHandler.cancelAllMessages();
-        mDrawingHandler.cancelAllMessages();
-        dismissAllKeyPreviews();
-        dismissGestureFloatingPreviewText();
-        dismissSlidingKeyInputPreview();
+        mTimerHandler.cancelAllMessages();
+        PointerTracker.setReleasedKeyGraphicsToAllKeys();
+        mGestureFloatingTextDrawingPreview.dismissGestureFloatingPreviewText();
+        mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview();
         PointerTracker.dismissAllMoreKeysPanels();
         PointerTracker.cancelAllPointerTrackers();
     }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 5a540ff..467f515 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -41,6 +41,9 @@
 
 import java.util.ArrayList;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 public final class PointerTracker implements PointerTrackerQueue.Element,
         BatchInputArbiterListener {
     private static final String TAG = PointerTracker.class.getSimpleName();
@@ -50,12 +53,18 @@
     private static boolean DEBUG_MODE = DebugFlags.DEBUG_ENABLED || DEBUG_EVENT;
 
     public interface DrawingProxy {
-        public void invalidateKey(Key key);
-        public void showKeyPreview(Key key);
-        public void dismissKeyPreview(Key key);
-        public void showSlidingKeyInputPreview(PointerTracker tracker);
-        public void dismissSlidingKeyInputPreview();
-        public void showGestureTrail(PointerTracker tracker, boolean showsFloatingPreviewText);
+        public void invalidateKey(@Nullable Key key);
+        public void showKeyPreview(@Nonnull Key key);
+        public void dismissKeyPreview(@Nonnull Key key);
+        public void dismissKeyPreviewWithoutDelay(@Nonnull Key key);
+        public void onLongPress(@Nonnull PointerTracker tracker);
+        public static final int FADE_IN = 0;
+        public static final int FADE_OUT = 1;
+        public void startWhileTypingAnimation(final int fadeInOrOut);
+        public void showSlidingKeyInputPreview(@Nullable PointerTracker tracker);
+        public void showGestureTrail(@Nonnull PointerTracker tracker,
+                boolean showsFloatingPreviewText);
+        public void dismissGestureFloatingPreviewTextWithoutDelay();
     }
 
     public interface TimerProxy {
@@ -163,6 +172,7 @@
 
     // The position and time at which first down event occurred.
     private long mDownTime;
+    @Nonnull
     private int[] mDownCoordinates = CoordinateUtils.newInstance();
     private long mUpTime;
 
@@ -416,6 +426,7 @@
         return mIsInDraggingFinger;
     }
 
+    @Nullable
     public Key getKey() {
         return mCurrentKey;
     }
@@ -429,12 +440,12 @@
         return mKeyDetector.detectHitKey(x, y);
     }
 
-    private void setReleasedKeyGraphics(final Key key) {
-        sDrawingProxy.dismissKeyPreview(key);
+    private void setReleasedKeyGraphics(@Nullable final Key key) {
         if (key == null) {
             return;
         }
 
+        sDrawingProxy.dismissKeyPreview(key);
         // Even if the key is disabled, update the key release graphics just in case.
         updateReleaseKeyGraphics(key);
 
@@ -518,7 +529,7 @@
         return mGestureStrokeDrawingPoints;
     }
 
-    public void getLastCoordinates(final int[] outCoords) {
+    public void getLastCoordinates(@Nonnull final int[] outCoords) {
         CoordinateUtils.set(outCoords, mLastX, mLastY);
     }
 
@@ -526,7 +537,7 @@
         return mDownTime;
     }
 
-    public void getDownCoordinates(final int[] outCoords) {
+    public void getDownCoordinates(@Nonnull final int[] outCoords) {
         CoordinateUtils.copy(outCoords, mDownCoordinates);
     }
 
@@ -765,7 +776,7 @@
     private void resetKeySelectionByDraggingFinger() {
         mIsInDraggingFinger = false;
         mIsInSlidingKeyInput = false;
-        sDrawingProxy.dismissSlidingKeyInputPreview();
+        sDrawingProxy.showSlidingKeyInputPreview(null /* tracker */);
     }
 
     private void onGestureMoveEvent(final int x, final int y, final long eventTime,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java
index 755a99a..c76a9ac 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java
@@ -22,6 +22,8 @@
 import com.android.inputmethod.keyboard.MainKeyboardView;
 import com.android.inputmethod.keyboard.PointerTracker;
 
+import javax.annotation.Nonnull;
+
 /**
  * Abstract base class for previews that are drawn on DrawingPreviewPlacerView, e.g.,
  * GestureFloatingTextDrawingPreview, GestureTrailsDrawingPreview, and
@@ -32,7 +34,7 @@
     private boolean mPreviewEnabled;
     private boolean mHasValidGeometry;
 
-    public void setDrawingView(final DrawingPreviewPlacerView drawingView) {
+    public void setDrawingView(@Nonnull final DrawingPreviewPlacerView drawingView) {
         mDrawingView = drawingView;
         drawingView.addPreview(this);
     }
@@ -61,7 +63,7 @@
      * @param width the width of {@link MainKeyboardView}.
      * @param height the height of {@link MainKeyboardView}.
      */
-    public void setKeyboardViewGeometry(final int[] originCoords, final int width,
+    public void setKeyboardViewGeometry(@Nonnull final int[] originCoords, final int width,
             final int height) {
         mHasValidGeometry = (width > 0 && height > 0);
     }
@@ -72,11 +74,11 @@
      * Draws the preview
      * @param canvas The canvas where the preview is drawn.
      */
-    public abstract void drawPreview(final Canvas canvas);
+    public abstract void drawPreview(@Nonnull final Canvas canvas);
 
     /**
      * Set the position of the preview.
      * @param tracker The new location of the preview is based on the points in PointerTracker.
      */
-    public abstract void setPreviewPosition(final PointerTracker tracker);
+    public abstract void setPreviewPosition(@Nonnull final PointerTracker tracker);
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java
deleted file mode 100644
index 1a55359..0000000
--- a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java
+++ /dev/null
@@ -1,79 +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.keyboard.internal;
-
-import android.os.Message;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.internal.DrawingHandler.Callbacks;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
-
-import javax.annotation.Nonnull;
-
-// TODO: Separate this class into KeyPreviewHandler and BatchInputPreviewHandler or so.
-public class DrawingHandler extends LeakGuardHandlerWrapper<Callbacks> {
-    public interface Callbacks {
-        public void dismissKeyPreviewWithoutDelay(Key key);
-        public void dismissAllKeyPreviews();
-        public void showGestureFloatingPreviewText(SuggestedWords suggestedWords);
-    }
-
-    private static final int MSG_DISMISS_KEY_PREVIEW = 0;
-    private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
-
-    public DrawingHandler(@Nonnull final Callbacks ownerInstance) {
-        super(ownerInstance);
-    }
-
-    @Override
-    public void handleMessage(final Message msg) {
-        final Callbacks callbacks = getOwnerInstance();
-        if (callbacks == null) {
-            return;
-        }
-        switch (msg.what) {
-        case MSG_DISMISS_KEY_PREVIEW:
-            callbacks.dismissKeyPreviewWithoutDelay((Key)msg.obj);
-            break;
-        case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT:
-            callbacks.showGestureFloatingPreviewText(SuggestedWords.getEmptyInstance());
-            break;
-        }
-    }
-
-    public void dismissKeyPreview(final long delay, final Key key) {
-        sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, key), delay);
-    }
-
-    private void cancelAllDismissKeyPreviews() {
-        removeMessages(MSG_DISMISS_KEY_PREVIEW);
-        final Callbacks callbacks = getOwnerInstance();
-        if (callbacks == null) {
-            return;
-        }
-        callbacks.dismissAllKeyPreviews();
-    }
-
-    public void dismissGestureFloatingPreviewText(final long delay) {
-        sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay);
-    }
-
-    public void cancelAllMessages() {
-        cancelAllDismissKeyPreviews();
-    }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java
index 37ea0f1..330ec52 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java
@@ -29,6 +29,8 @@
 import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.utils.CoordinateUtils;
 
+import javax.annotation.Nonnull;
+
 /**
  * The class for single gesture preview text. The class for multiple gesture preview text will be
  * derived from it.
@@ -110,7 +112,11 @@
         // Nothing to do here.
     }
 
-    public void setSuggetedWords(final SuggestedWords suggestedWords) {
+    public void dismissGestureFloatingPreviewText() {
+        setSuggetedWords(SuggestedWords.getEmptyInstance());
+    }
+
+    public void setSuggetedWords(@Nonnull final SuggestedWords suggestedWords) {
         if (!isPreviewEnabled()) {
             return;
         }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
index ddb193e..d376487 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
@@ -28,7 +28,6 @@
 
 import java.util.ArrayDeque;
 import java.util.HashMap;
-import java.util.HashSet;
 
 /**
  * This class controls pop up key previews. This class decides:
@@ -69,12 +68,6 @@
         return mShowingKeyPreviewViews.containsKey(key);
     }
 
-    public void dismissAllKeyPreviews() {
-        for (final Key key : new HashSet<>(mShowingKeyPreviewViews.keySet())) {
-            dismissKeyPreview(key, false /* withAnimation */);
-        }
-    }
-
     public void dismissKeyPreview(final Key key, final boolean withAnimation) {
         if (key == null) {
             return;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
index 076abbf..4565225 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
@@ -22,33 +22,29 @@
 
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
 import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
-import com.android.inputmethod.keyboard.internal.TimerHandler.Callbacks;
 import com.android.inputmethod.latin.common.Constants;
 import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
 
 import javax.annotation.Nonnull;
 
-// TODO: Separate this class into KeyTimerHandler and BatchInputTimerHandler or so.
-public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> implements TimerProxy {
-    public interface Callbacks {
-        public void startWhileTypingFadeinAnimation();
-        public void startWhileTypingFadeoutAnimation();
-        public void onLongPress(PointerTracker tracker);
-    }
-
+public final class TimerHandler extends LeakGuardHandlerWrapper<DrawingProxy>
+        implements TimerProxy {
     private static final int MSG_TYPING_STATE_EXPIRED = 0;
     private static final int MSG_REPEAT_KEY = 1;
     private static final int MSG_LONGPRESS_KEY = 2;
     private static final int MSG_LONGPRESS_SHIFT_KEY = 3;
     private static final int MSG_DOUBLE_TAP_SHIFT_KEY = 4;
     private static final int MSG_UPDATE_BATCH_INPUT = 5;
+    private static final int MSG_DISMISS_KEY_PREVIEW = 6;
+    private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 7;
 
     private final int mIgnoreAltCodeKeyTimeout;
     private final int mGestureRecognitionUpdateTime;
 
-    public TimerHandler(@Nonnull final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout,
-            final int gestureRecognitionUpdateTime) {
+    public TimerHandler(@Nonnull final DrawingProxy ownerInstance,
+            final int ignoreAltCodeKeyTimeout, final int gestureRecognitionUpdateTime) {
         super(ownerInstance);
         mIgnoreAltCodeKeyTimeout = ignoreAltCodeKeyTimeout;
         mGestureRecognitionUpdateTime = gestureRecognitionUpdateTime;
@@ -56,26 +52,35 @@
 
     @Override
     public void handleMessage(final Message msg) {
-        final Callbacks callbacks = getOwnerInstance();
-        if (callbacks == null) {
+        final DrawingProxy drawingProxy = getOwnerInstance();
+        if (drawingProxy == null) {
             return;
         }
-        final PointerTracker tracker = (PointerTracker) msg.obj;
         switch (msg.what) {
         case MSG_TYPING_STATE_EXPIRED:
-            callbacks.startWhileTypingFadeinAnimation();
+            drawingProxy.startWhileTypingAnimation(DrawingProxy.FADE_IN);
             break;
         case MSG_REPEAT_KEY:
-            tracker.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */);
+            final PointerTracker tracker1 = (PointerTracker) msg.obj;
+            tracker1.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */);
             break;
         case MSG_LONGPRESS_KEY:
         case MSG_LONGPRESS_SHIFT_KEY:
             cancelLongPressTimers();
-            callbacks.onLongPress(tracker);
+            final PointerTracker tracker2 = (PointerTracker) msg.obj;
+            drawingProxy.onLongPress(tracker2);
             break;
         case MSG_UPDATE_BATCH_INPUT:
-            tracker.updateBatchInputByTimer(SystemClock.uptimeMillis());
-            startUpdateBatchInputTimer(tracker);
+            final PointerTracker tracker3 = (PointerTracker) msg.obj;
+            tracker3.updateBatchInputByTimer(SystemClock.uptimeMillis());
+            startUpdateBatchInputTimer(tracker3);
+            break;
+        case MSG_DISMISS_KEY_PREVIEW:
+            final Key key = (Key) msg.obj;
+            drawingProxy.dismissKeyPreviewWithoutDelay(key);
+            break;
+        case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT:
+            drawingProxy.dismissGestureFloatingPreviewTextWithoutDelay();
             break;
         }
     }
@@ -141,8 +146,8 @@
 
         final boolean isTyping = isTypingState();
         removeMessages(MSG_TYPING_STATE_EXPIRED);
-        final Callbacks callbacks = getOwnerInstance();
-        if (callbacks == null) {
+        final DrawingProxy drawingProxy = getOwnerInstance();
+        if (drawingProxy == null) {
             return;
         }
 
@@ -150,7 +155,7 @@
         final int typedCode = typedKey.getCode();
         if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) {
             if (isTyping) {
-                callbacks.startWhileTypingFadeinAnimation();
+                drawingProxy.startWhileTypingAnimation(DrawingProxy.FADE_IN);
             }
             return;
         }
@@ -160,7 +165,7 @@
         if (isTyping) {
             return;
         }
-        callbacks.startWhileTypingFadeoutAnimation();
+        drawingProxy.startWhileTypingAnimation(DrawingProxy.FADE_OUT);
     }
 
     @Override
@@ -215,8 +220,18 @@
         removeMessages(MSG_UPDATE_BATCH_INPUT);
     }
 
+    public void postDismissKeyPreview(@Nonnull final Key key, final long delay) {
+        sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, key), delay);
+    }
+
+    public void postDismissGestureFloatingPreviewText(final long delay) {
+        sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay);
+    }
+
     public void cancelAllMessages() {
         cancelAllKeyTimers();
         cancelAllUpdateBatchInputTimers();
+        removeMessages(MSG_DISMISS_KEY_PREVIEW);
+        removeMessages(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 5fa28d5..3fa1270 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1501,14 +1501,12 @@
     }
 
     // This method must run on the UI Thread.
-    void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
+    void showGesturePreviewAndSuggestionStrip(@Nonnull final SuggestedWords suggestedWords,
             final boolean dismissGestureFloatingPreviewText) {
         showSuggestionStrip(suggestedWords);
         final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
-        mainKeyboardView.showGestureFloatingPreviewText(suggestedWords);
-        if (dismissGestureFloatingPreviewText) {
-            mainKeyboardView.dismissGestureFloatingPreviewText();
-        }
+        mainKeyboardView.showGestureFloatingPreviewText(suggestedWords,
+                dismissGestureFloatingPreviewText /* dismissDelayed */);
     }
 
     // Called from PointerTracker through the KeyboardActionListener interface
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 89f2803..9b4619d 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -160,24 +160,36 @@
                 || (consideredWord.length() > 1 && !didRemoveTypedWord);
 
         final boolean hasAutoCorrection;
-        // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because
-        // any attempt to do auto-correction is already shielded with a test for this flag; at the
-        // 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 || resultsArePredictions
-                || suggestionResults.isEmpty() || wordComposer.hasDigits()
-                || wordComposer.isMostlyCaps() || wordComposer.isResumed()
+        // If correction is not enabled, we never auto-correct. This is for example for when
+        // the setting "Auto-correction" is "off": we still suggest, but we don't auto-correct.
+        if (!isCorrectionEnabled
+                // If the word does not allow to be auto-corrected, then we don't auto-correct.
+                || !allowsToBeAutoCorrected
+                // If we are doing prediction, then we never auto-correct of course
+                || resultsArePredictions
+                // If we don't have suggestion results, we can't evaluate the first suggestion
+                // for auto-correction
+                || suggestionResults.isEmpty()
+                // If the word has digits, we never auto-correct because it's likely the word
+                // was type with a lot of care
+                || wordComposer.hasDigits()
+                // If the word is mostly caps, we never auto-correct because this is almost
+                // certainly intentional (and careful input)
+                || wordComposer.isMostlyCaps()
+                // We never auto-correct when suggestions are resumed because it would be unexpected
+                || wordComposer.isResumed()
+                // If we don't have a main dictionary, we never want to auto-correct. The reason
+                // for this is, the user may have a contact whose name happens to match a valid
+                // word in their language, and it will unexpectedly auto-correct. For example, if
+                // the user types in English with no dictionary and has a "Will" in their contact
+                // list, "will" would always auto-correct to "Will" which is unwanted. Hence, no
+                // main dict => no auto-correct. Also, it would probably get obnoxious quickly.
+                // TODO: now that we have personalization, we may want to re-evaluate this decision
                 || !mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary()
+                // If the first suggestion is a shortcut we never auto-correct to it, regardless
+                // of how strong it is (whitelist entries are not KIND_SHORTCUT but KIND_WHITELIST).
+                // TODO: we may want to have shortcut-only entries auto-correct in the future.
                 || suggestionResults.first().isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) {
-            // If we don't have a main dictionary, we never want to auto-correct. The reason for
-            // this is, the user may have a contact whose name happens to match a valid word in
-            // their language, and it will unexpectedly auto-correct. For example, if the user
-            // types in English with no dictionary and has a "Will" in their contact list, "will"
-            // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no
-            // auto-correct.
-            // Also, shortcuts should never auto-correct unless they are whitelist entries.
-            // TODO: we may want to have shortcut-only entries auto-correct in the future.
             hasAutoCorrection = false;
         } else {
             final SuggestedWordInfo firstSuggestion = suggestionResults.first();
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index b84de69..c51e20f 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -28,6 +28,8 @@
 import java.util.Arrays;
 import java.util.HashSet;
 
+import javax.annotation.Nonnull;
+
 public class SuggestedWords {
     public static final int INDEX_OF_TYPED_WORD = 0;
     public static final int INDEX_OF_AUTO_CORRECTION = 1;
@@ -46,6 +48,7 @@
     public static final int MAX_SUGGESTIONS = 18;
 
     private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = new ArrayList<>(0);
+    @Nonnull
     private static final SuggestedWords EMPTY = new SuggestedWords(
             EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false /* typedWordValid */,
             false /* willAutoCorrect */, false /* isObsoleteSuggestions */, INPUT_STYLE_NONE);
@@ -210,6 +213,7 @@
         return result;
     }
 
+    @Nonnull
     public static final SuggestedWords getEmptyInstance() {
         return SuggestedWords.EMPTY;
     }
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 78d79ae..5925bdc 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -171,14 +171,17 @@
     // ExpandableDictionary.matchesExpectedBinaryDictFormatVersionForThisType().
     public static final int VERSION2 = 2;
     public static final int VERSION201 = 201;
+    public static final int VERSION202 = 202;
     public static final int MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE = VERSION201;
     // Dictionary version used for testing.
     public static final int VERSION4_ONLY_FOR_TESTING = 399;
     public static final int VERSION401 = 401;
     public static final int VERSION4 = 402;
     public static final int VERSION4_DEV = 403;
-    static final int MINIMUM_SUPPORTED_VERSION = VERSION2;
-    static final int MAXIMUM_SUPPORTED_VERSION = VERSION4_DEV;
+    static final int MINIMUM_SUPPORTED_STATIC_VERSION = VERSION202;
+    static final int MAXIMUM_SUPPORTED_STATIC_VERSION = VERSION202;
+    static final int MINIMUM_SUPPORTED_DYNAMIC_VERSION = VERSION4;
+    static final int MAXIMUM_SUPPORTED_DYNAMIC_VERSION = VERSION4_DEV;
 
     // TODO: Make this value adaptative to content data, store it in the header, and
     // use it in the reading code.
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
index 4985c2f..6fffb8e 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
@@ -29,6 +29,8 @@
     public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch";
     public static final String PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS =
             "pref_has_custom_key_preview_animation_params";
+    public static final String PREF_RESIZE_KEYBOARD = "pref_resize_keyboard";
+    public static final String PREF_KEYBOARD_HEIGHT_SCALE = "pref_keyboard_height_scale";
     public static final String PREF_KEY_PREVIEW_DISMISS_DURATION =
             "pref_key_preview_dismiss_duration";
     public static final String PREF_KEY_PREVIEW_DISMISS_END_X_SCALE =
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
index 2e5c3c4..068f56d 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
@@ -89,6 +89,8 @@
                 defaultKeyPreviewDismissEndScale);
         setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE,
                 defaultKeyPreviewDismissEndScale);
+        setupKeyboardHeight(
+                DebugSettings.PREF_KEYBOARD_HEIGHT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE);
 
         mServiceNeedsRestart = false;
         mDebugMode = (TwoStatePreference) findPreference(DebugSettings.PREF_DEBUG_MODE);
@@ -250,4 +252,51 @@
             public void feedbackValue(final int value) {}
         });
     }
+
+    private void setupKeyboardHeight(final String prefKey, final float defaultValue) {
+        final SharedPreferences prefs = getSharedPreferences();
+        final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey);
+        if (pref == null) {
+            return;
+        }
+        pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
+            private static final float PERCENTAGE_FLOAT = 100.0f;
+            private float getValueFromPercentage(final int percentage) {
+                return percentage / PERCENTAGE_FLOAT;
+            }
+
+            private int getPercentageFromValue(final float floatValue) {
+                return (int)(floatValue * PERCENTAGE_FLOAT);
+            }
+
+            @Override
+            public void writeValue(final int value, final String key) {
+                prefs.edit().putFloat(key, getValueFromPercentage(value)).apply();
+            }
+
+            @Override
+            public void writeDefaultValue(final String key) {
+                prefs.edit().remove(key).apply();
+            }
+
+            @Override
+            public int readValue(final String key) {
+                return getPercentageFromValue(
+                        Settings.readKeyboardHeight(prefs, key, defaultValue));
+            }
+
+            @Override
+            public int readDefaultValue(final String key) {
+                return getPercentageFromValue(defaultValue);
+            }
+
+            @Override
+            public String getValueText(final int value) {
+                return String.format(Locale.ROOT, "%d%%", value);
+            }
+
+            @Override
+            public void feedbackValue(final int value) {}
+        });
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
index 0fd94b0..5c416ab 100644
--- a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
+++ b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
@@ -47,12 +47,14 @@
         DebugSettings.PREF_DEBUG_MODE,
         DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH,
         DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS,
+        DebugSettings.PREF_KEYBOARD_HEIGHT_SCALE,
         DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION,
         DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE,
         DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE,
         DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION,
         DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE,
         DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE,
+        DebugSettings.PREF_RESIZE_KEYBOARD,
         DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI,
         DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW
     };
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 19db606..16c0534 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -363,6 +363,12 @@
         return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds : defaultValue;
     }
 
+    public static float readKeyboardHeight(final SharedPreferences prefs,
+            final String prefKey, final float defaultValue) {
+        final float percentage = prefs.getFloat(prefKey, UNDEFINED_PREFERENCE_VALUE_FLOAT);
+        return (percentage != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? percentage : defaultValue;
+    }
+
     public static boolean readUseFullscreenMode(final Resources res) {
         return res.getBoolean(R.bool.config_use_fullscreen_mode);
     }
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index bdb4e64..509b41f 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -50,6 +50,7 @@
     private static final String FLOAT_MAX_VALUE_MARKER_STRING = "floatMaxValue";
     private static final String FLOAT_NEGATIVE_INFINITY_MARKER_STRING = "floatNegativeInfinity";
     private static final int TIMEOUT_TO_GET_TARGET_PACKAGE = 5; // seconds
+    public static final float DEFAULT_SIZE_SCALE = 1.0f; // 100%
 
     // From resources:
     public final SpacingAndPunctuations mSpacingAndPunctuations;
@@ -110,6 +111,8 @@
     // Debug settings
     public final boolean mIsInternal;
     public final boolean mHasCustomKeyPreviewAnimationParams;
+    public final boolean mHasKeyboardResize;
+    public final float mKeyboardHeightScale;
     public final int mKeyPreviewShowUpDuration;
     public final int mKeyPreviewDismissDuration;
     public final float mKeyPreviewShowUpStartXScale;
@@ -185,6 +188,9 @@
         mIsInternal = Settings.isInternal(prefs);
         mHasCustomKeyPreviewAnimationParams = prefs.getBoolean(
                 DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS, false);
+        mHasKeyboardResize = prefs.getBoolean(DebugSettings.PREF_RESIZE_KEYBOARD, false);
+        mKeyboardHeightScale = Settings.readKeyboardHeight(
+                prefs, DebugSettings.PREF_KEYBOARD_HEIGHT_SCALE, DEFAULT_SIZE_SCALE);
         mKeyPreviewShowUpDuration = Settings.readKeyPreviewAnimationDuration(
                 prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION,
                 res.getInteger(R.integer.config_key_preview_show_up_duration));
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index b18fcc7..b71bd1f 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -344,12 +344,6 @@
         if (mSuggestedWords.size() <= mStartIndexOfMoreSuggestions) {
             return false;
         }
-        // Dismiss another {@link MoreKeysPanel} that may be being showed, for example
-        // {@link MoreKeysKeyboardView}.
-        mMainKeyboardView.onDismissMoreKeysPanel();
-        // Dismiss all key previews and sliding key input preview that may be being showed.
-        mMainKeyboardView.dismissAllKeyPreviews();
-        mMainKeyboardView.dismissSlidingKeyInputPreview();
         final int stripWidth = getWidth();
         final View container = mMoreSuggestionsContainer;
         final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight();
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
index 7274857..1d7e7d6 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
@@ -282,7 +282,6 @@
     }
 
     private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer {
-
         private AlphabetIndexer mIndexer;
 
         private ViewBinder mViewBinder = new ViewBinder() {
diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
index f9839eb..01f5e10 100644
--- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
@@ -22,16 +22,27 @@
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+/**
+ * Utility methods for working with collections.
+ */
 public final class CollectionUtils {
     private CollectionUtils() {
         // This utility class is not publicly instantiable.
     }
 
+    /**
+     * Converts a sub-range of the given array to an ArrayList of the appropriate type.
+     * @param array Array to be converted.
+     * @param start First index inclusive to be converted.
+     * @param end Last index exclusive to be converted.
+     * @throws IllegalArgumentException if start or end are out of range or start &gt; end.
+     */
     @Nonnull
     public static <E> ArrayList<E> arrayAsList(@Nonnull final E[] array, final int start,
             final int end) {
         if (start < 0 || start > end || end > array.length) {
-            throw new IllegalArgumentException();
+            throw new IllegalArgumentException("Invalid start: " + start + " end: " + end
+                    + " with array.length: " + array.length);
         }
 
         final ArrayList<E> list = new ArrayList<>(end - start);
diff --git a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
index 4e0f5f5..8699f2c 100644
--- a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
@@ -36,7 +36,8 @@
     public static final String WORD_TAG = "word";
     public static final String BEGINNING_OF_SENTENCE_TAG = "beginning_of_sentence";
     public static final String NOT_A_WORD_TAG = "not_a_word";
-    public static final String BLACKLISTED_TAG = "blacklisted";
+    public static final String POSSIBLY_OFFENSIVE_TAG = "possibly_offensive";
+    public static final String TRUE_VALUE = "true";
 
     public static String formatAttributeMap(final HashMap<String, String> attributeMap) {
         final StringBuilder builder = new StringBuilder();
@@ -61,13 +62,13 @@
         builder.append(",");
         builder.append(formatProbabilityInfo(wordProperty.mProbabilityInfo));
         if (wordProperty.mIsBeginningOfSentence) {
-            builder.append("," + BEGINNING_OF_SENTENCE_TAG + "=true");
+            builder.append("," + BEGINNING_OF_SENTENCE_TAG + "=" + TRUE_VALUE);
         }
         if (wordProperty.mIsNotAWord) {
-            builder.append("," + NOT_A_WORD_TAG + "=true");
+            builder.append("," + NOT_A_WORD_TAG + "=" + TRUE_VALUE);
         }
         if (wordProperty.mIsPossiblyOffensive) {
-            builder.append("," + BLACKLISTED_TAG + "=true");
+            builder.append("," + POSSIBLY_OFFENSIVE_TAG + "=" + TRUE_VALUE);
         }
         builder.append("\n");
         if (wordProperty.mHasShortcuts) {
@@ -111,4 +112,8 @@
         }
         return builder.toString();
     }
+
+    public static boolean isLiteralTrue(final String value) {
+        return TRUE_VALUE.equalsIgnoreCase(value);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java
index d1fc642..cc0d470 100644
--- a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java
@@ -26,6 +26,7 @@
 
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.settings.SettingsValues;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -186,6 +187,15 @@
         return dm.widthPixels;
     }
 
+    public static int getKeyboardHeight(final Resources res, final SettingsValues settingsValues) {
+        final int defaultKeyboardHeight = getDefaultKeyboardHeight(res);
+        if (settingsValues.mHasKeyboardResize) {
+            // mKeyboardHeightScale Ranges from [.5,1.2], from xml/prefs_screen_debug.xml
+            return (int)(defaultKeyboardHeight * settingsValues.mKeyboardHeightScale);
+        }
+        return defaultKeyboardHeight;
+    }
+
     public static int getDefaultKeyboardHeight(final Resources res) {
         final DisplayMetrics dm = res.getDisplayMetrics();
         final String keyboardHeightInDp = getDeviceOverrideValue(
diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk
index 7299ed3..55bb683 100644
--- a/native/jni/NativeFileList.mk
+++ b/native/jni/NativeFileList.mk
@@ -71,7 +71,9 @@
         ver4_patricia_trie_writing_helper.cpp \
         ver4_pt_node_array_reader.cpp) \
     $(addprefix suggest/policyimpl/dictionary/structure/v4/content/, \
+        dynamic_language_model_probability_utils.cpp \
         language_model_dict_content.cpp \
+        language_model_dict_content_global_counters.cpp \
         shortcut_dict_content.cpp \
         sparse_table_dict_content.cpp \
         terminal_position_lookup_table.cpp) \
@@ -83,6 +85,7 @@
         forgetting_curve_utils.cpp \
         format_utils.cpp \
         mmapped_buffer.cpp \
+        probability_utils.cpp \
         sparse_table.cpp \
         trie_map.cpp ) \
     suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \
@@ -128,11 +131,13 @@
     suggest/core/layout/normal_distribution_2d_test.cpp \
     suggest/policyimpl/dictionary/header/header_read_write_utils_test.cpp \
     suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp \
+    suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters_test.cpp \
     suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp \
     suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table_test.cpp \
     suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer_test.cpp \
     suggest/policyimpl/dictionary/utils/byte_array_utils_test.cpp \
     suggest/policyimpl/dictionary/utils/format_utils_test.cpp \
+    suggest/policyimpl/dictionary/utils/probability_utils_test.cpp \
     suggest/policyimpl/dictionary/utils/sparse_table_test.cpp \
     suggest/policyimpl/dictionary/utils/trie_map_test.cpp \
     suggest/policyimpl/utils/damerau_levenshtein_edit_distance_policy_test.cpp \
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index bfe17cc..6a5df9d 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -81,6 +81,9 @@
     }
     const WordAttributes wordAttributes = mDictStructurePolicy->getWordAttributesInContext(
             mPrevWordIds, targetWordId, nullptr /* multiBigramMap */);
+    if (wordAttributes.getProbability() == NOT_A_PROBABILITY) {
+        return;
+    }
     mSuggestionResults->addPrediction(targetWordCodePoints, codePointCount,
             wordAttributes.getProbability());
 }
diff --git a/native/jni/src/suggest/core/dictionary/ngram_listener.h b/native/jni/src/suggest/core/dictionary/ngram_listener.h
index e9b3c1a..2eb5e9f 100644
--- a/native/jni/src/suggest/core/dictionary/ngram_listener.h
+++ b/native/jni/src/suggest/core/dictionary/ngram_listener.h
@@ -26,6 +26,8 @@
  */
 class NgramListener {
  public:
+    // ngramProbability is always 0 for v403 decaying dictionary.
+    // TODO: Remove ngramProbability.
     virtual void onVisitEntry(const int ngramProbability, const int targetWordId) = 0;
     virtual ~NgramListener() {};
 
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
index 3283f6d..23103b9 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
@@ -76,6 +76,52 @@
             weightOfLangModelVsSpatialModelToOutputSuggestions, outSuggestionResults);
 }
 
+/* static */ bool SuggestionsOutputUtils::shouldBlockWord(
+        const SuggestOptions *const suggestOptions, const DicNode *const terminalDicNode,
+        const WordAttributes wordAttributes, const bool isLastWord) {
+    const bool currentWordExactMatch =
+            ErrorTypeUtils::isExactMatch(terminalDicNode->getContainedErrorTypes());
+    // When we have to block offensive words, non-exact matched offensive words should not be
+    // output.
+    const bool shouldBlockOffensiveWords = suggestOptions->blockOffensiveWords();
+
+    const bool isBlockedOffensiveWord = shouldBlockOffensiveWords &&
+            wordAttributes.isPossiblyOffensive();
+
+    // This function is called in two situations:
+    //
+    // 1) At the end of a search, in which case terminalDicNode will point to the last DicNode
+    //    of the search, and isLastWord will be true.
+    //                    "fuck"
+    //                        |
+    //                        \ terminalDicNode (isLastWord=true, currentWordExactMatch=true)
+    //    In this case, if the current word is an exact match, we will always let the word
+    //    through, even if the user is blocking offensive words (it's exactly what they typed!)
+    //
+    // 2) In the middle of the search, when we hit a terminal node, to decide whether or not
+    //    to start a new search at root, to try to match the rest of the input. In this case,
+    //    terminalDicNode will point to the terminal node we just hit, and isLastWord will be
+    //    false.
+    //                    "fuckvthis"
+    //                        |
+    //                        \ terminalDicNode (isLastWord=false, currentWordExactMatch=true)
+    //
+    // In this case, we should NOT allow the match through (correcting "fuckthis" to "fuck this"
+    // when offensive words are blocked would be a bad idea).
+    //
+    // In the case of a multi-word correction where the offensive word is typed last (eg.
+    // for the input "allfuck"), this function will be called with isLastWord==true, but
+    // currentWordExactMatch==false. So we are OK in this case as well.
+    //                    "allfuck"
+    //                           |
+    //                           \ terminalDicNode (isLastWord=true, currentWordExactMatch=false)
+    if (isLastWord && currentWordExactMatch) {
+        return false;
+    } else {
+        return isBlockedOffensiveWord;
+    }
+}
+
 /* static */ void SuggestionsOutputUtils::outputSuggestionsOfDicNode(
         const Scoring *const scoringPolicy, DicTraverseSession *traverseSession,
         const DicNode *const terminalDicNode, const float weightOfLangModelVsSpatialModel,
@@ -98,24 +144,16 @@
     const bool isExactMatchWithIntentionalOmission =
             ErrorTypeUtils::isExactMatchWithIntentionalOmission(
                     terminalDicNode->getContainedErrorTypes());
-    const bool isFirstCharUppercase = terminalDicNode->isFirstCharUppercase();
-    // Heuristic: We exclude probability=0 first-char-uppercase words from exact match.
-    // (e.g. "AMD" and "and")
-    const bool isSafeExactMatch = isExactMatch
-            && !(wordAttributes.isPossiblyOffensive() && isFirstCharUppercase);
     const int outputTypeFlags =
             (wordAttributes.isPossiblyOffensive() ? Dictionary::KIND_FLAG_POSSIBLY_OFFENSIVE : 0)
-            | ((isSafeExactMatch && boostExactMatches) ? Dictionary::KIND_FLAG_EXACT_MATCH : 0)
+            | ((isExactMatch && boostExactMatches) ? Dictionary::KIND_FLAG_EXACT_MATCH : 0)
             | (isExactMatchWithIntentionalOmission ?
                     Dictionary::KIND_FLAG_EXACT_MATCH_WITH_INTENTIONAL_OMISSION : 0);
-
     // Entries that are blacklisted or do not represent a word should not be output.
     const bool isValidWord = !(wordAttributes.isBlacklisted() || wordAttributes.isNotAWord());
-    // When we have to block offensive words, non-exact matched offensive words should not be
-    // output.
-    const bool blockOffensiveWords = traverseSession->getSuggestOptions()->blockOffensiveWords();
-    const bool isBlockedOffensiveWord = blockOffensiveWords && wordAttributes.isPossiblyOffensive()
-            && !isSafeExactMatch;
+
+    const bool shouldBlockThisWord = shouldBlockWord(traverseSession->getSuggestOptions(),
+            terminalDicNode, wordAttributes, true /* isLastWord */);
 
     // Increase output score of top typing suggestion to ensure autocorrection.
     // TODO: Better integration with java side autocorrection logic.
@@ -127,7 +165,7 @@
 
     // Don't output invalid or blocked offensive words. However, we still need to submit their
     // shortcuts if any.
-    if (isValidWord && !isBlockedOffensiveWord) {
+    if (isValidWord && !shouldBlockThisWord) {
         int codePoints[MAX_WORD_LENGTH];
         terminalDicNode->outputResult(codePoints);
         const int indexToPartialCommit = outputSecondWordFirstLetterInputIndex ?
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.h b/native/jni/src/suggest/core/result/suggestions_output_utils.h
index bf84978..eca1f78 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.h
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.h
@@ -18,6 +18,7 @@
 #define LATINIME_SUGGESTIONS_OUTPUT_UTILS
 
 #include "defines.h"
+#include "suggest/core/dictionary/word_attributes.h"
 
 namespace latinime {
 
@@ -25,11 +26,19 @@
 class DicNode;
 class DicTraverseSession;
 class Scoring;
+class SuggestOptions;
 class SuggestionResults;
 
 class SuggestionsOutputUtils {
  public:
     /**
+     * Returns true if we should block the incoming word, in the context of the user's
+     * preferences to include or not include possibly offensive words
+     */
+    static bool shouldBlockWord(const SuggestOptions *const suggestOptions,
+            const DicNode *const terminalDicNode, const WordAttributes wordAttributes,
+            const bool isLastWord);
+    /**
      * Outputs the final list of suggestions (i.e., terminal nodes).
      */
     static void outputSuggestions(const Scoring *const scoringPolicy,
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index 68a3645..c372d66 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -416,6 +416,11 @@
             traverseSession->getDictionaryStructurePolicy()->getWordAttributesInContext(
                     dicNode->getPrevWordIds(), dicNode->getWordId(),
                     traverseSession->getMultiBigramMap());
+    if (SuggestionsOutputUtils::shouldBlockWord(traverseSession->getSuggestOptions(),
+            dicNode, wordAttributes, false /* isLastWord */)) {
+        return;
+    }
+
     if (!TRAVERSAL->isGoodToTraverseNextWord(dicNode, wordAttributes.getProbability())) {
         return;
     }
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 44c2f44..abc7f99 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
@@ -134,9 +134,11 @@
         // same so we use them for both here.
         switch (mDictFormatVersion) {
             case FormatUtils::VERSION_2:
-                return FormatUtils::VERSION_2;
             case FormatUtils::VERSION_201:
-                return FormatUtils::VERSION_201;
+                AKLOGE("Dictionary versions 2 and 201 are incompatible with this version");
+                return FormatUtils::UNKNOWN_VERSION;
+            case FormatUtils::VERSION_202:
+                return FormatUtils::VERSION_202;
             case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
                 return FormatUtils::VERSION_4_ONLY_FOR_TESTING;
             case FormatUtils::VERSION_4:
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
index 41a8b13..d69a53f 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
@@ -111,7 +111,8 @@
     switch (version) {
         case FormatUtils::VERSION_2:
         case FormatUtils::VERSION_201:
-            // Version 2 or 201 dictionary writing is not supported.
+        case FormatUtils::VERSION_202:
+            // None of the static dictionaries (v2x) support writing
             return false;
         case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
         case FormatUtils::VERSION_4:
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp
index 08e39ce..9455222 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp
@@ -140,7 +140,7 @@
 
 const WordAttributes Ver4PatriciaTriePolicy::getWordAttributes(const int probability,
         const PtNodeParams &ptNodeParams) const {
-    return WordAttributes(probability, ptNodeParams.isBlacklisted(), ptNodeParams.isNotAWord(),
+    return WordAttributes(probability, false /* isBlacklisted */, ptNodeParams.isNotAWord(),
             ptNodeParams.getProbability() == 0);
 }
 
@@ -164,7 +164,7 @@
     }
     const int ptNodePos = getTerminalPtNodePosFromWordId(wordId);
     const PtNodeParams ptNodeParams(mNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos));
-    if (ptNodeParams.isDeleted() || ptNodeParams.isBlacklisted() || ptNodeParams.isNotAWord()) {
+    if (ptNodeParams.isDeleted() || ptNodeParams.isNotAWord()) {
         return NOT_A_PROBABILITY;
     }
     if (prevWordIds.empty()) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
index 372c9e3..a19a384 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
@@ -115,7 +115,8 @@
     switch (formatVersion) {
         case FormatUtils::VERSION_2:
         case FormatUtils::VERSION_201:
-            AKLOGE("Given path is a directory but the format is version 2 or 201. path: %s", path);
+        case FormatUtils::VERSION_202:
+            AKLOGE("Given path is a directory but the format is version 2xx. path: %s", path);
             break;
         case FormatUtils::VERSION_4: {
             return newPolicyForV4Dict<backward::v402::Ver4DictConstants,
@@ -177,6 +178,9 @@
     switch (FormatUtils::detectFormatVersion(mmappedBuffer->getReadOnlyByteArrayView())) {
         case FormatUtils::VERSION_2:
         case FormatUtils::VERSION_201:
+            AKLOGE("Dictionary versions 2 and 201 are incompatible with this version");
+            break;
+        case FormatUtils::VERSION_202:
             return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(
                     new PatriciaTriePolicy(std::move(mmappedBuffer)));
         case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
index 585e87a..e52706e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
@@ -144,17 +144,6 @@
         return PatriciaTrieReadingUtils::isTerminal(mFlags);
     }
 
-    AK_FORCE_INLINE bool isBlacklisted() const {
-        // Note: this method will be removed in the next change.
-        // It is used in getProbabilityOfWord and getWordAttributes for both v402 and v403.
-        // * getProbabilityOfWord will be changed to no longer return NOT_A_PROBABILITY
-        //   when isBlacklisted (i.e. to only check if isNotAWord or isDeleted)
-        // * getWordAttributes will be changed to always return blacklisted=false and
-        //   isPossiblyOffensive according to the function below (instead of the current
-        //   behaviour of checking if the probability is zero)
-        return PatriciaTrieReadingUtils::isPossiblyOffensive(mFlags);
-    }
-
     AK_FORCE_INLINE bool isPossiblyOffensive() const {
         return PatriciaTrieReadingUtils::isPossiblyOffensive(mFlags);
     }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
index 66fd18a..5987361 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 #include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h"
 
 #include "defines.h"
@@ -317,8 +316,8 @@
 
 const WordAttributes PatriciaTriePolicy::getWordAttributes(const int probability,
         const PtNodeParams &ptNodeParams) const {
-    return WordAttributes(probability, ptNodeParams.isBlacklisted(), ptNodeParams.isNotAWord(),
-            ptNodeParams.getProbability() == 0);
+    return WordAttributes(probability, false /* isBlacklisted */, ptNodeParams.isNotAWord(),
+            ptNodeParams.isPossiblyOffensive());
 }
 
 int PatriciaTriePolicy::getProbability(const int unigramProbability,
@@ -345,10 +344,9 @@
     const int ptNodePos = getTerminalPtNodePosFromWordId(wordId);
     const PtNodeParams ptNodeParams =
             mPtNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos);
-    if (ptNodeParams.isNotAWord() || ptNodeParams.isBlacklisted()) {
-        // If this is not a word, or if it's a blacklisted entry, it should behave as
-        // having no probability outside of the suggestion process (where it should be used
-        // for shortcuts).
+    if (ptNodeParams.isNotAWord()) {
+        // If this is not a word, it should behave as having no probability outside of the
+        // suggestion process (where it should be used for shortcuts).
         return NOT_A_PROBABILITY;
     }
     if (!prevWordIds.empty()) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.cpp
new file mode 100644
index 0000000..b0fbb3e
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#include "suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.h"
+
+namespace latinime {
+
+// These counts are used to provide stable probabilities even if the user's input count is small.
+const int DynamicLanguageModelProbabilityUtils::ASSUMED_MIN_COUNT_FOR_UNIGRAMS = 8192;
+const int DynamicLanguageModelProbabilityUtils::ASSUMED_MIN_COUNT_FOR_BIGRAMS = 2;
+const int DynamicLanguageModelProbabilityUtils::ASSUMED_MIN_COUNT_FOR_TRIGRAMS = 2;
+
+// These are encoded backoff weights.
+// Note that we give positive value for trigrams that means the weight is more than 1.
+// TODO: Apply backoff for main dictionaries and quit giving a positive backoff weight.
+const int DynamicLanguageModelProbabilityUtils::ENCODED_BACKOFF_WEIGHT_FOR_UNIGRAMS = -32;
+const int DynamicLanguageModelProbabilityUtils::ENCODED_BACKOFF_WEIGHT_FOR_BIGRAMS = 0;
+const int DynamicLanguageModelProbabilityUtils::ENCODED_BACKOFF_WEIGHT_FOR_TRIGRAMS = 8;
+
+// This value is used to remove too old entries from the dictionary.
+const int DynamicLanguageModelProbabilityUtils::DURATION_TO_DISCARD_ENTRY_IN_SECONDS =
+        300 * 24 * 60 * 60; // 300 days
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.h
new file mode 100644
index 0000000..88bc58f
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.h
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+#ifndef LATINIME_DYNAMIC_LANGUAGE_MODEL_PROBABILITY_UTILS_H
+#define LATINIME_DYNAMIC_LANGUAGE_MODEL_PROBABILITY_UTILS_H
+
+#include <algorithm>
+
+#include "defines.h"
+#include "suggest/core/dictionary/property/historical_info.h"
+#include "utils/time_keeper.h"
+
+namespace latinime {
+
+class DynamicLanguageModelProbabilityUtils {
+ public:
+    static float computeRawProbabilityFromCounts(const int count, const int contextCount,
+            const int matchedWordCountInContext) {
+        int minCount = 0;
+        switch (matchedWordCountInContext) {
+            case 1:
+                minCount = ASSUMED_MIN_COUNT_FOR_UNIGRAMS;
+                break;
+            case 2:
+                minCount = ASSUMED_MIN_COUNT_FOR_BIGRAMS;
+                break;
+            case 3:
+                minCount = ASSUMED_MIN_COUNT_FOR_TRIGRAMS;
+                break;
+            default:
+                AKLOGE("computeRawProbabilityFromCounts is called with invalid "
+                        "matchedWordCountInContext (%d).", matchedWordCountInContext);
+                ASSERT(false);
+                return 0.0f;
+        }
+        return static_cast<float>(count) / static_cast<float>(std::max(contextCount, minCount));
+    }
+
+    static float backoff(const int ngramProbability, const int matchedWordCountInContext) {
+        int probability = NOT_A_PROBABILITY;
+
+        switch (matchedWordCountInContext) {
+            case 1:
+                probability = ngramProbability + ENCODED_BACKOFF_WEIGHT_FOR_UNIGRAMS;
+                break;
+            case 2:
+                probability = ngramProbability + ENCODED_BACKOFF_WEIGHT_FOR_BIGRAMS;
+                break;
+            case 3:
+                probability = ngramProbability + ENCODED_BACKOFF_WEIGHT_FOR_TRIGRAMS;
+                break;
+            default:
+                AKLOGE("backoff is called with invalid matchedWordCountInContext (%d).",
+                        matchedWordCountInContext);
+                ASSERT(false);
+                return NOT_A_PROBABILITY;
+        }
+        return std::min(std::max(probability, NOT_A_PROBABILITY), MAX_PROBABILITY);
+    }
+
+    static int getDecayedProbability(const int probability, const HistoricalInfo historicalInfo) {
+        const int elapsedTime = TimeKeeper::peekCurrentTime() - historicalInfo.getTimestamp();
+        if (elapsedTime < 0) {
+            AKLOGE("The elapsed time is negatime value. Timestamp overflow?");
+            return NOT_A_PROBABILITY;
+        }
+        // TODO: Improve this logic.
+        // We don't modify probability depending on the elapsed time.
+        return probability;
+    }
+
+    static int shouldRemoveEntryDuringGC(const HistoricalInfo historicalInfo) {
+        // TODO: Improve this logic.
+        const int elapsedTime = TimeKeeper::peekCurrentTime() - historicalInfo.getTimestamp();
+        return elapsedTime > DURATION_TO_DISCARD_ENTRY_IN_SECONDS;
+    }
+
+    static int getPriorityToPreventFromEviction(const HistoricalInfo historicalInfo) {
+        // TODO: Improve this logic.
+        // More recently input entries get higher priority.
+        return historicalInfo.getTimestamp();
+    }
+
+private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicLanguageModelProbabilityUtils);
+
+    static_assert(MAX_PREV_WORD_COUNT_FOR_N_GRAM <= 2, "Max supported Ngram is Trigram.");
+
+    static const int ASSUMED_MIN_COUNT_FOR_UNIGRAMS;
+    static const int ASSUMED_MIN_COUNT_FOR_BIGRAMS;
+    static const int ASSUMED_MIN_COUNT_FOR_TRIGRAMS;
+
+    static const int ENCODED_BACKOFF_WEIGHT_FOR_UNIGRAMS;
+    static const int ENCODED_BACKOFF_WEIGHT_FOR_BIGRAMS;
+    static const int ENCODED_BACKOFF_WEIGHT_FOR_TRIGRAMS;
+
+    static const int DURATION_TO_DISCARD_ENTRY_IN_SECONDS;
+};
+
+} // namespace latinime
+#endif /* LATINIME_DYNAMIC_LANGUAGE_MODEL_PROBABILITY_UTILS_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp
index 509bd68..31b1ea6 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp
@@ -19,14 +19,16 @@
 #include <algorithm>
 #include <cstring>
 
-#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h"
+#include "suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.h"
+#include "suggest/policyimpl/dictionary/utils/probability_utils.h"
 
 namespace latinime {
 
-const int LanguageModelDictContent::DUMMY_PROBABILITY_FOR_VALID_WORDS = 1;
+const int LanguageModelDictContent::TRIE_MAP_BUFFER_INDEX = 0;
+const int LanguageModelDictContent::GLOBAL_COUNTERS_BUFFER_INDEX = 1;
 
 bool LanguageModelDictContent::save(FILE *const file) const {
-    return mTrieMap.save(file);
+    return mTrieMap.save(file) && mGlobalCounters.save(file);
 }
 
 bool LanguageModelDictContent::runGC(
@@ -37,7 +39,8 @@
 }
 
 const WordAttributes LanguageModelDictContent::getWordAttributes(const WordIdArrayView prevWordIds,
-        const int wordId, const HeaderPolicy *const headerPolicy) const {
+        const int wordId, const bool mustMatchAllPrevWords,
+        const HeaderPolicy *const headerPolicy) const {
     int bitmapEntryIndices[MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1];
     bitmapEntryIndices[0] = mTrieMap.getRootBitmapEntryIndex();
     int maxPrevWordCount = 0;
@@ -51,7 +54,15 @@
         bitmapEntryIndices[i + 1] = nextBitmapEntryIndex;
     }
 
+    const ProbabilityEntry unigramProbabilityEntry = getProbabilityEntry(wordId);
+    if (mHasHistoricalInfo && unigramProbabilityEntry.getHistoricalInfo()->getCount() == 0) {
+        // The word should be treated as a invalid word.
+        return WordAttributes();
+    }
     for (int i = maxPrevWordCount; i >= 0; --i) {
+        if (mustMatchAllPrevWords && prevWordIds.size() > static_cast<size_t>(i)) {
+            break;
+        }
         const TrieMap::Result result = mTrieMap.get(wordId, bitmapEntryIndices[i]);
         if (!result.mIsValid) {
             continue;
@@ -60,36 +71,39 @@
                 ProbabilityEntry::decode(result.mValue, mHasHistoricalInfo);
         int probability = NOT_A_PROBABILITY;
         if (mHasHistoricalInfo) {
-            const int rawProbability = ForgettingCurveUtils::decodeProbability(
-                    probabilityEntry.getHistoricalInfo(), headerPolicy);
-            if (rawProbability == NOT_A_PROBABILITY) {
-                // The entry should not be treated as a valid entry.
-                continue;
-            }
+            const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo();
+            int contextCount = 0;
             if (i == 0) {
                 // unigram
-                probability = rawProbability;
+                contextCount = mGlobalCounters.getTotalCount();
             } else {
                 const ProbabilityEntry prevWordProbabilityEntry = getNgramProbabilityEntry(
                         prevWordIds.skip(1 /* n */).limit(i - 1), prevWordIds[0]);
                 if (!prevWordProbabilityEntry.isValid()) {
                     continue;
                 }
-                if (prevWordProbabilityEntry.representsBeginningOfSentence()) {
-                    probability = rawProbability;
-                } else {
-                    const int prevWordRawProbability = ForgettingCurveUtils::decodeProbability(
-                            prevWordProbabilityEntry.getHistoricalInfo(), headerPolicy);
-                    probability = std::min(MAX_PROBABILITY - prevWordRawProbability
-                            + rawProbability, MAX_PROBABILITY);
+                if (prevWordProbabilityEntry.representsBeginningOfSentence()
+                        && historicalInfo->getCount() == 1) {
+                    // BoS ngram requires multiple contextCount.
+                    continue;
                 }
+                contextCount = prevWordProbabilityEntry.getHistoricalInfo()->getCount();
             }
+            const float rawProbability =
+                    DynamicLanguageModelProbabilityUtils::computeRawProbabilityFromCounts(
+                            historicalInfo->getCount(), contextCount, i + 1);
+            const int encodedRawProbability =
+                    ProbabilityUtils::encodeRawProbability(rawProbability);
+            const int decayedProbability =
+                    DynamicLanguageModelProbabilityUtils::getDecayedProbability(
+                            encodedRawProbability, *historicalInfo);
+            probability = DynamicLanguageModelProbabilityUtils::backoff(
+                    decayedProbability, i + 1 /* n */);
         } else {
             probability = probabilityEntry.getProbability();
         }
         // TODO: Some flags in unigramProbabilityEntry should be overwritten by flags in
         // probabilityEntry.
-        const ProbabilityEntry unigramProbabilityEntry = getProbabilityEntry(wordId);
         return WordAttributes(probability, unigramProbabilityEntry.isBlacklisted(),
                 unigramProbabilityEntry.isNotAWord(),
                 unigramProbabilityEntry.isPossiblyOffensive());
@@ -165,7 +179,8 @@
                 ProbabilityEntry::decode(entry.value(), mHasHistoricalInfo);
         if (probabilityEntry.isValid()) {
             const WordAttributes wordAttributes = getWordAttributes(
-                    WordIdArrayView(*prevWordIds), wordId, headerPolicy);
+                    WordIdArrayView(*prevWordIds), wordId, true /* mustMatchAllPrevWords */,
+                    headerPolicy);
             outBummpedFullEntryInfo->emplace_back(*prevWordIds, wordId,
                     wordAttributes, probabilityEntry);
         }
@@ -212,6 +227,9 @@
     if (!setProbabilityEntry(wordId, &updatedUnigramProbabilityEntry)) {
         return false;
     }
+    mGlobalCounters.incrementTotalCount();
+    mGlobalCounters.updateMaxValueOfCounters(
+            updatedUnigramProbabilityEntry.getHistoricalInfo()->getCount());
     for (size_t i = 0; i < prevWordIds.size(); ++i) {
         if (prevWordIds[i] == NOT_A_WORD_ID) {
             break;
@@ -225,6 +243,8 @@
         if (!setNgramProbabilityEntry(limitedPrevWordIds, wordId, &updatedNgramProbabilityEntry)) {
             return false;
         }
+        mGlobalCounters.updateMaxValueOfCounters(
+                updatedNgramProbabilityEntry.getHistoricalInfo()->getCount());
         if (!originalNgramProbabilityEntry.isValid()) {
             entryCountersToUpdate->incrementNgramCount(i + 2);
         }
@@ -235,10 +255,9 @@
 const ProbabilityEntry LanguageModelDictContent::createUpdatedEntryFrom(
         const ProbabilityEntry &originalProbabilityEntry, const bool isValid,
         const HistoricalInfo historicalInfo, const HeaderPolicy *const headerPolicy) const {
-    const HistoricalInfo updatedHistoricalInfo = ForgettingCurveUtils::createUpdatedHistoricalInfo(
-            originalProbabilityEntry.getHistoricalInfo(), isValid ?
-                    DUMMY_PROBABILITY_FOR_VALID_WORDS : NOT_A_PROBABILITY,
-            &historicalInfo, headerPolicy);
+    const HistoricalInfo updatedHistoricalInfo = HistoricalInfo(historicalInfo.getTimestamp(),
+            0 /* level */, originalProbabilityEntry.getHistoricalInfo()->getCount()
+                    + historicalInfo.getCount());
     if (originalProbabilityEntry.isValid()) {
         return ProbabilityEntry(originalProbabilityEntry.getFlags(), &updatedHistoricalInfo);
     } else {
@@ -304,7 +323,7 @@
 
 bool LanguageModelDictContent::updateAllProbabilityEntriesForGCInner(const int bitmapEntryIndex,
         const int prevWordCount, const HeaderPolicy *const headerPolicy,
-        MutableEntryCounters *const outEntryCounters) {
+        const bool needsToHalveCounters, MutableEntryCounters *const outEntryCounters) {
     for (const auto &entry : mTrieMap.getEntriesInSpecifiedLevel(bitmapEntryIndex)) {
         if (prevWordCount > MAX_PREV_WORD_COUNT_FOR_N_GRAM) {
             AKLOGE("Invalid prevWordCount. prevWordCount: %d, MAX_PREV_WORD_COUNT_FOR_N_GRAM: %d.",
@@ -321,33 +340,41 @@
             }
             continue;
         }
-        if (mHasHistoricalInfo && !probabilityEntry.representsBeginningOfSentence()
-                && probabilityEntry.isValid()) {
-            const HistoricalInfo historicalInfo = ForgettingCurveUtils::createHistoricalInfoToSave(
-                    probabilityEntry.getHistoricalInfo(), headerPolicy);
-            if (ForgettingCurveUtils::needsToKeep(&historicalInfo, headerPolicy)) {
-                // Update the entry.
-                const ProbabilityEntry updatedEntry(probabilityEntry.getFlags(), &historicalInfo);
-                if (!mTrieMap.put(entry.key(), updatedEntry.encode(mHasHistoricalInfo),
-                        bitmapEntryIndex)) {
-                    return false;
-                }
-            } else {
+        if (mHasHistoricalInfo && probabilityEntry.isValid()) {
+            const HistoricalInfo *originalHistoricalInfo = probabilityEntry.getHistoricalInfo();
+            if (DynamicLanguageModelProbabilityUtils::shouldRemoveEntryDuringGC(
+                    *originalHistoricalInfo)) {
                 // Remove the entry.
                 if (!mTrieMap.remove(entry.key(), bitmapEntryIndex)) {
                     return false;
                 }
                 continue;
             }
+            if (needsToHalveCounters) {
+                const int updatedCount = originalHistoricalInfo->getCount() / 2;
+                if (updatedCount == 0) {
+                    // Remove the entry.
+                    if (!mTrieMap.remove(entry.key(), bitmapEntryIndex)) {
+                        return false;
+                    }
+                    continue;
+                }
+                const HistoricalInfo historicalInfoToSave(originalHistoricalInfo->getTimestamp(),
+                        originalHistoricalInfo->getLevel(), updatedCount);
+                const ProbabilityEntry updatedEntry(probabilityEntry.getFlags(),
+                        &historicalInfoToSave);
+                if (!mTrieMap.put(entry.key(), updatedEntry.encode(mHasHistoricalInfo),
+                        bitmapEntryIndex)) {
+                    return false;
+                }
+            }
         }
-        if (!probabilityEntry.representsBeginningOfSentence()) {
-            outEntryCounters->incrementNgramCount(prevWordCount + 1);
-        }
+        outEntryCounters->incrementNgramCount(prevWordCount + 1);
         if (!entry.hasNextLevelMap()) {
             continue;
         }
         if (!updateAllProbabilityEntriesForGCInner(entry.getNextLevelBitmapEntryIndex(),
-                prevWordCount + 1, headerPolicy, outEntryCounters)) {
+                prevWordCount + 1, headerPolicy, needsToHalveCounters, outEntryCounters)) {
             return false;
         }
     }
@@ -401,11 +428,11 @@
         }
         const ProbabilityEntry probabilityEntry =
                 ProbabilityEntry::decode(entry.value(), mHasHistoricalInfo);
-        const int probability = (mHasHistoricalInfo) ?
-                ForgettingCurveUtils::decodeProbability(probabilityEntry.getHistoricalInfo(),
-                        headerPolicy) : probabilityEntry.getProbability();
-        outEntryInfo->emplace_back(probability,
-                probabilityEntry.getHistoricalInfo()->getTimestamp(),
+        const int priority = mHasHistoricalInfo
+                ? DynamicLanguageModelProbabilityUtils::getPriorityToPreventFromEviction(
+                        *probabilityEntry.getHistoricalInfo())
+                : probabilityEntry.getProbability();
+        outEntryInfo->emplace_back(priority, probabilityEntry.getHistoricalInfo()->getCount(),
                 entry.key(), targetLevel, prevWordIds->data());
     }
     return true;
@@ -413,11 +440,11 @@
 
 bool LanguageModelDictContent::EntryInfoToTurncate::Comparator::operator()(
         const EntryInfoToTurncate &left, const EntryInfoToTurncate &right) const {
-    if (left.mProbability != right.mProbability) {
-        return left.mProbability < right.mProbability;
+    if (left.mPriority != right.mPriority) {
+        return left.mPriority < right.mPriority;
     }
-    if (left.mTimestamp != right.mTimestamp) {
-        return left.mTimestamp > right.mTimestamp;
+    if (left.mCount != right.mCount) {
+        return left.mCount < right.mCount;
     }
     if (left.mKey != right.mKey) {
         return left.mKey < right.mKey;
@@ -434,10 +461,9 @@
     return false;
 }
 
-LanguageModelDictContent::EntryInfoToTurncate::EntryInfoToTurncate(const int probability,
-        const int timestamp, const int key, const int prevWordCount, const int *const prevWordIds)
-        : mProbability(probability), mTimestamp(timestamp), mKey(key),
-          mPrevWordCount(prevWordCount) {
+LanguageModelDictContent::EntryInfoToTurncate::EntryInfoToTurncate(const int priority,
+        const int count, const int key, const int prevWordCount, const int *const prevWordIds)
+        : mPriority(priority), mCount(count), mKey(key), mPrevWordCount(prevWordCount) {
     memmove(mPrevWordIds, prevWordIds, mPrevWordCount * sizeof(mPrevWordIds[0]));
 }
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h
index 1cccf92..9678c35 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h
@@ -22,6 +22,7 @@
 
 #include "defines.h"
 #include "suggest/core/dictionary/word_attributes.h"
+#include "suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.h"
 #include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h"
 #include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
@@ -131,15 +132,17 @@
         const ProbabilityEntry mProbabilityEntry;
     };
 
-    LanguageModelDictContent(const ReadWriteByteArrayView trieMapBuffer,
+    LanguageModelDictContent(const ReadWriteByteArrayView *const buffers,
             const bool hasHistoricalInfo)
-            : mTrieMap(trieMapBuffer), mHasHistoricalInfo(hasHistoricalInfo) {}
+            : mTrieMap(buffers[TRIE_MAP_BUFFER_INDEX]),
+              mGlobalCounters(buffers[GLOBAL_COUNTERS_BUFFER_INDEX]),
+              mHasHistoricalInfo(hasHistoricalInfo) {}
 
     explicit LanguageModelDictContent(const bool hasHistoricalInfo)
-            : mTrieMap(), mHasHistoricalInfo(hasHistoricalInfo) {}
+            : mTrieMap(), mGlobalCounters(), mHasHistoricalInfo(hasHistoricalInfo) {}
 
     bool isNearSizeLimit() const {
-        return mTrieMap.isNearSizeLimit();
+        return mTrieMap.isNearSizeLimit() || mGlobalCounters.needsToHalveCounters();
     }
 
     bool save(FILE *const file) const;
@@ -148,13 +151,14 @@
             const LanguageModelDictContent *const originalContent);
 
     const WordAttributes getWordAttributes(const WordIdArrayView prevWordIds, const int wordId,
-            const HeaderPolicy *const headerPolicy) const;
+            const bool mustMatchAllPrevWords, const HeaderPolicy *const headerPolicy) const;
 
     ProbabilityEntry getProbabilityEntry(const int wordId) const {
         return getNgramProbabilityEntry(WordIdArrayView(), wordId);
     }
 
     bool setProbabilityEntry(const int wordId, const ProbabilityEntry *const probabilityEntry) {
+        mGlobalCounters.addToTotalCount(probabilityEntry->getHistoricalInfo()->getCount());
         return setNgramProbabilityEntry(WordIdArrayView(), wordId, probabilityEntry);
     }
 
@@ -177,8 +181,15 @@
 
     bool updateAllProbabilityEntriesForGC(const HeaderPolicy *const headerPolicy,
             MutableEntryCounters *const outEntryCounters) {
-        return updateAllProbabilityEntriesForGCInner(mTrieMap.getRootBitmapEntryIndex(),
-                0 /* prevWordCount */, headerPolicy, outEntryCounters);
+        if (!updateAllProbabilityEntriesForGCInner(mTrieMap.getRootBitmapEntryIndex(),
+                0 /* prevWordCount */, headerPolicy, mGlobalCounters.needsToHalveCounters(),
+                outEntryCounters)) {
+            return false;
+        }
+        if (mGlobalCounters.needsToHalveCounters()) {
+            mGlobalCounters.halveCounters();
+        }
+        return true;
     }
 
     // entryCounts should be created by updateAllProbabilityEntries.
@@ -203,11 +214,12 @@
             DISALLOW_ASSIGNMENT_OPERATOR(Comparator);
         };
 
-        EntryInfoToTurncate(const int probability, const int timestamp, const int key,
+        EntryInfoToTurncate(const int priority, const int count, const int key,
                 const int prevWordCount, const int *const prevWordIds);
 
-        int mProbability;
-        int mTimestamp;
+        int mPriority;
+        // TODO: Remove.
+        int mCount;
         int mKey;
         int mPrevWordCount;
         int mPrevWordIds[MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1];
@@ -216,10 +228,11 @@
         DISALLOW_DEFAULT_CONSTRUCTOR(EntryInfoToTurncate);
     };
 
-    // TODO: Remove
-    static const int DUMMY_PROBABILITY_FOR_VALID_WORDS;
+    static const int TRIE_MAP_BUFFER_INDEX;
+    static const int GLOBAL_COUNTERS_BUFFER_INDEX;
 
     TrieMap mTrieMap;
+    LanguageModelDictContentGlobalCounters mGlobalCounters;
     const bool mHasHistoricalInfo;
 
     bool runGCInner(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap,
@@ -227,7 +240,8 @@
     int createAndGetBitmapEntryIndex(const WordIdArrayView prevWordIds);
     int getBitmapEntryIndex(const WordIdArrayView prevWordIds) const;
     bool updateAllProbabilityEntriesForGCInner(const int bitmapEntryIndex, const int prevWordCount,
-            const HeaderPolicy *const headerPolicy, MutableEntryCounters *const outEntryCounters);
+            const HeaderPolicy *const headerPolicy, const bool needsToHalveCounters,
+            MutableEntryCounters *const outEntryCounters);
     bool turncateEntriesInSpecifiedLevel(const HeaderPolicy *const headerPolicy,
             const int maxEntryCount, const int targetLevel, int *const outEntryCount);
     bool getEntryInfo(const HeaderPolicy *const headerPolicy, const int targetLevel,
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.cpp
new file mode 100644
index 0000000..d6d9188
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#include "suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.h"
+
+#include <climits>
+
+#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
+
+namespace latinime {
+
+const int LanguageModelDictContentGlobalCounters::COUNTER_VALUE_NEAR_LIMIT_THRESHOLD =
+        (1 << (Ver4DictConstants::WORD_COUNT_FIELD_SIZE * CHAR_BIT)) - 64;
+const int LanguageModelDictContentGlobalCounters::TOTAL_COUNT_VALUE_NEAR_LIMIT_THRESHOLD = 1 << 30;
+const int LanguageModelDictContentGlobalCounters::COUNTER_SIZE_IN_BYTES = 4;
+const int LanguageModelDictContentGlobalCounters::TOTAL_COUNT_INDEX = 0;
+const int LanguageModelDictContentGlobalCounters::MAX_VALUE_OF_COUNTERS_INDEX = 1;
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.h
new file mode 100644
index 0000000..283c269
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.h
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+#ifndef LATINIME_LANGUAGE_MODEL_DICT_CONTENT_GLOBAL_COUNTERS_H
+#define LATINIME_LANGUAGE_MODEL_DICT_CONTENT_GLOBAL_COUNTERS_H
+
+#include <cstdio>
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+#include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h"
+#include "utils/byte_array_view.h"
+
+namespace latinime {
+
+class LanguageModelDictContentGlobalCounters {
+ public:
+    explicit LanguageModelDictContentGlobalCounters(const ReadWriteByteArrayView buffer)
+            : mBuffer(buffer, 0 /* maxAdditionalBufferSize */),
+              mTotalCount(readValue(mBuffer, TOTAL_COUNT_INDEX)),
+              mMaxValueOfCounters(readValue(mBuffer, MAX_VALUE_OF_COUNTERS_INDEX)) {}
+
+    LanguageModelDictContentGlobalCounters()
+            : mBuffer(0 /* maxAdditionalBufferSize */), mTotalCount(0), mMaxValueOfCounters(0) {}
+
+    bool needsToHalveCounters() const {
+        return mMaxValueOfCounters >= COUNTER_VALUE_NEAR_LIMIT_THRESHOLD
+                || mTotalCount >= TOTAL_COUNT_VALUE_NEAR_LIMIT_THRESHOLD;
+    }
+
+    int getTotalCount() const {
+        return mTotalCount;
+    }
+
+    bool save(FILE *const file) const {
+        BufferWithExtendableBuffer bufferToWrite(
+                BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE);
+        if (!bufferToWrite.writeUint(mTotalCount, COUNTER_SIZE_IN_BYTES,
+                TOTAL_COUNT_INDEX * COUNTER_SIZE_IN_BYTES)) {
+            return false;
+        }
+        if (!bufferToWrite.writeUint(mMaxValueOfCounters, COUNTER_SIZE_IN_BYTES,
+                MAX_VALUE_OF_COUNTERS_INDEX * COUNTER_SIZE_IN_BYTES)) {
+            return false;
+        }
+        return DictFileWritingUtils::writeBufferToFileTail(file, &bufferToWrite);
+    }
+
+    void incrementTotalCount() {
+        mTotalCount += 1;
+    }
+
+    void addToTotalCount(const int count) {
+        mTotalCount += count;
+    }
+
+    void updateMaxValueOfCounters(const int count) {
+        mMaxValueOfCounters = std::max(count, mMaxValueOfCounters);
+    }
+
+    void halveCounters() {
+        mMaxValueOfCounters /= 2;
+        mTotalCount /= 2;
+    }
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(LanguageModelDictContentGlobalCounters);
+
+    const static int COUNTER_VALUE_NEAR_LIMIT_THRESHOLD;
+    const static int TOTAL_COUNT_VALUE_NEAR_LIMIT_THRESHOLD;
+    const static int COUNTER_SIZE_IN_BYTES;
+    const static int TOTAL_COUNT_INDEX;
+    const static int MAX_VALUE_OF_COUNTERS_INDEX;
+
+    BufferWithExtendableBuffer mBuffer;
+    int mTotalCount;
+    int mMaxValueOfCounters;
+
+    static int readValue(const BufferWithExtendableBuffer &buffer, const int index) {
+        const int pos = COUNTER_SIZE_IN_BYTES * index;
+        if (pos + COUNTER_SIZE_IN_BYTES > buffer.getTailPosition()) {
+            return 0;
+        }
+        return buffer.readUint(COUNTER_SIZE_IN_BYTES, pos);
+    }
+};
+} // namespace latinime
+#endif /* LATINIME_LANGUAGE_MODEL_DICT_CONTENT_GLOBAL_COUNTERS_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp
index 45f88e9..4d088dc 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp
@@ -179,7 +179,7 @@
                   BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE),
           mTerminalPositionLookupTable(
                   contentBuffers[Ver4DictConstants::TERMINAL_ADDRESS_LOOKUP_TABLE_BUFFER_INDEX]),
-          mLanguageModelDictContent(contentBuffers[Ver4DictConstants::LANGUAGE_MODEL_BUFFER_INDEX],
+          mLanguageModelDictContent(&contentBuffers[Ver4DictConstants::LANGUAGE_MODEL_BUFFER_INDEX],
                   mHeaderPolicy.hasHistoricalInfoOfWords()),
           mShortcutDictContent(&contentBuffers[Ver4DictConstants::SHORTCUT_BUFFERS_INDEX]),
           mIsUpdatable(mDictBuffer->isUpdatable()) {}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
index 8e6cb97..eb6080a 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
@@ -67,6 +67,6 @@
 
 const size_t Ver4DictConstants::NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT = 1;
 const size_t Ver4DictConstants::NUM_OF_BUFFERS_FOR_SPARSE_TABLE_DICT_CONTENT = 3;
-const size_t Ver4DictConstants::NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT = 1;
+const size_t Ver4DictConstants::NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT = 2;
 
 } // namespace latinime
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 d3de322..96d789f 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
@@ -110,7 +110,7 @@
         return WordAttributes();
     }
     return mBuffers->getLanguageModelDictContent()->getWordAttributes(prevWordIds, wordId,
-            mHeaderPolicy);
+            false /* mustMatchAllPrevWords */, mHeaderPolicy);
 }
 
 int Ver4PatriciaTriePolicy::getProbabilityOfWord(const WordIdArrayView prevWordIds,
@@ -118,18 +118,13 @@
     if (wordId == NOT_A_WORD_ID || prevWordIds.contains(NOT_A_WORD_ID)) {
         return NOT_A_PROBABILITY;
     }
-    const ProbabilityEntry probabilityEntry =
-            mBuffers->getLanguageModelDictContent()->getNgramProbabilityEntry(prevWordIds, wordId);
-    if (!probabilityEntry.isValid() || probabilityEntry.isBlacklisted()
-            || probabilityEntry.isNotAWord()) {
+    const WordAttributes wordAttributes =
+            mBuffers->getLanguageModelDictContent()->getWordAttributes(prevWordIds, wordId,
+                    true /* mustMatchAllPrevWords */, mHeaderPolicy);
+    if (wordAttributes.isBlacklisted() || wordAttributes.isNotAWord()) {
         return NOT_A_PROBABILITY;
     }
-    if (mHeaderPolicy->hasHistoricalInfoOfWords()) {
-        return ForgettingCurveUtils::decodeProbability(probabilityEntry.getHistoricalInfo(),
-                mHeaderPolicy);
-    } else {
-        return probabilityEntry.getProbability();
-    }
+    return wordAttributes.getProbability();
 }
 
 BinaryDictionaryShortcutIterator Ver4PatriciaTriePolicy::getShortcutIterator(
@@ -152,9 +147,7 @@
                 continue;
             }
             const int probability = probabilityEntry.hasHistoricalInfo() ?
-                    ForgettingCurveUtils::decodeProbability(
-                            probabilityEntry.getHistoricalInfo(), mHeaderPolicy) :
-                    probabilityEntry.getProbability();
+                    0 : probabilityEntry.getProbability();
             listener->onVisitEntry(probability, entry.getWordId());
         }
     }
@@ -386,25 +379,35 @@
             AKLOGE("Cannot add unigarm entry in updateEntriesForWordWithNgramContext().");
             return false;
         }
+        if (!isValidWord) {
+            return true;
+        }
         wordId = getWordId(wordCodePoints, false /* tryLowerCaseSearch */);
     }
 
     WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray;
     const WordIdArrayView prevWordIds = ngramContext->getPrevWordIds(this, &prevWordIdArray,
             false /* tryLowerCaseSearch */);
-    if (prevWordIds.firstOrDefault(NOT_A_WORD_ID) == NOT_A_WORD_ID
-            && ngramContext->isNthPrevWordBeginningOfSentence(1 /* n */)) {
-        const UnigramProperty beginningOfSentenceUnigramProperty(
-                true /* representsBeginningOfSentence */,
-                true /* isNotAWord */, false /* isPossiblyOffensive */, NOT_A_PROBABILITY,
-                HistoricalInfo(historicalInfo.getTimestamp(), 0 /* level */, 0 /* count */));
-        if (!addUnigramEntry(ngramContext->getNthPrevWordCodePoints(1 /* n */),
-                &beginningOfSentenceUnigramProperty)) {
-            AKLOGE("Cannot add BoS entry in updateEntriesForWordWithNgramContext().");
+    if (ngramContext->isNthPrevWordBeginningOfSentence(1 /* n */)) {
+        if (prevWordIds.firstOrDefault(NOT_A_WORD_ID) == NOT_A_WORD_ID) {
+            const UnigramProperty beginningOfSentenceUnigramProperty(
+                    true /* representsBeginningOfSentence */,
+                    true /* isNotAWord */, false /* isPossiblyOffensive */, NOT_A_PROBABILITY,
+                    HistoricalInfo(historicalInfo.getTimestamp(), 0 /* level */, 0 /* count */));
+            if (!addUnigramEntry(ngramContext->getNthPrevWordCodePoints(1 /* n */),
+                    &beginningOfSentenceUnigramProperty)) {
+                AKLOGE("Cannot add BoS entry in updateEntriesForWordWithNgramContext().");
+                return false;
+            }
+            // Refresh word ids.
+            ngramContext->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */);
+        }
+        // Update entries for beginning of sentence.
+        if (!mBuffers->getMutableLanguageModelDictContent()->updateAllEntriesOnInputWord(
+                prevWordIds.skip(1 /* n */), prevWordIds[0], true /* isVaild */, historicalInfo,
+                mHeaderPolicy, &mEntryCounters)) {
             return false;
         }
-        // Refresh word ids.
-        ngramContext->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */);
     }
     if (!mBuffers->getMutableLanguageModelDictContent()->updateAllEntriesOnInputWord(prevWordIds,
             wordId, updateAsAValidWord, historicalInfo, mHeaderPolicy, &mEntryCounters)) {
@@ -542,7 +545,7 @@
         }
     }
     const WordAttributes wordAttributes = languageModelDictContent->getWordAttributes(
-            WordIdArrayView(), wordId, mHeaderPolicy);
+            WordIdArrayView(), wordId, true /* mustMatchAllPrevWords */, mHeaderPolicy);
     const ProbabilityEntry probabilityEntry = languageModelDictContent->getProbabilityEntry(wordId);
     const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo();
     const UnigramProperty unigramProperty(probabilityEntry.representsBeginningOfSentence(),
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
index 0cffe56..8b47147 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
@@ -28,9 +28,11 @@
 /* static */ FormatUtils::FORMAT_VERSION FormatUtils::getFormatVersion(const int formatVersion) {
     switch (formatVersion) {
         case VERSION_2:
-            return VERSION_2;
         case VERSION_201:
-            return VERSION_201;
+            AKLOGE("Dictionary versions 2 and 201 are incompatible with this version");
+            return UNKNOWN_VERSION;
+        case VERSION_202:
+            return VERSION_202;
         case VERSION_4_ONLY_FOR_TESTING:
             return VERSION_4_ONLY_FOR_TESTING;
         case VERSION_4:
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
index 9631008..05bd7eb 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
@@ -31,8 +31,12 @@
  public:
     enum FORMAT_VERSION {
         // These MUST have the same values as the relevant constants in FormatSpec.java.
+        // TODO: Remove VERSION_2 and VERSION_201 when we:
+        // * Confirm that old versions of LatinIME download old-format dictionaries
+        // * We no longer need the corresponding constants on the Java side for dicttool
         VERSION_2 = 2,
         VERSION_201 = 201,
+        VERSION_202 = 202,
         VERSION_4_ONLY_FOR_TESTING = 399,
         VERSION_4 = 402,
         VERSION_4_DEV = 403,
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.cpp
new file mode 100644
index 0000000..e8fa069
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.cpp
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+#include "suggest/policyimpl/dictionary/utils/probability_utils.h"
+
+namespace latinime {
+
+const float ProbabilityUtils::PROBABILITY_ENCODING_SCALER = 8.58923700372f;
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h
index 3b339e6..2050af1 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h
@@ -17,6 +17,9 @@
 #ifndef LATINIME_PROBABILITY_UTILS_H
 #define LATINIME_PROBABILITY_UTILS_H
 
+#include <algorithm>
+#include <cmath>
+
 #include "defines.h"
 
 namespace latinime {
@@ -47,8 +50,20 @@
                 + static_cast<int>(static_cast<float>(bigramProbability + 1) * stepSize);
     }
 
+    // Encode probability using the same way as we are doing for main dictionaries.
+    static AK_FORCE_INLINE int encodeRawProbability(const float rawProbability) {
+        const float probability = static_cast<float>(MAX_PROBABILITY)
+                + log2f(rawProbability) * PROBABILITY_ENCODING_SCALER;
+        if (probability < 0.0f) {
+            return 0;
+        }
+        return std::min(static_cast<int>(probability + 0.5f), MAX_PROBABILITY);
+    }
+
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(ProbabilityUtils);
+
+    static const float PROBABILITY_ENCODING_SCALER;
 };
 }
 #endif /* LATINIME_PROBABILITY_UTILS_H */
diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters_test.cpp
new file mode 100644
index 0000000..44b5a8a
--- /dev/null
+++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters_test.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include "suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.h"
+
+#include <gtest/gtest.h>
+
+#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
+
+namespace latinime {
+namespace {
+
+TEST(LanguageModelDictContentGlobalCountersTest, TestUpdateMaxValueOfCounters) {
+    LanguageModelDictContentGlobalCounters globalCounters;
+
+    EXPECT_FALSE(globalCounters.needsToHalveCounters());
+    globalCounters.updateMaxValueOfCounters(10);
+    EXPECT_FALSE(globalCounters.needsToHalveCounters());
+    const int count = (1 << (Ver4DictConstants::WORD_COUNT_FIELD_SIZE * CHAR_BIT)) - 1;
+    globalCounters.updateMaxValueOfCounters(count);
+    EXPECT_TRUE(globalCounters.needsToHalveCounters());
+    globalCounters.halveCounters();
+    EXPECT_FALSE(globalCounters.needsToHalveCounters());
+}
+
+TEST(LanguageModelDictContentGlobalCountersTest, TestIncrementTotalCount) {
+    LanguageModelDictContentGlobalCounters globalCounters;
+
+    EXPECT_EQ(0, globalCounters.getTotalCount());
+    globalCounters.incrementTotalCount();
+    EXPECT_EQ(1, globalCounters.getTotalCount());
+    for (int i = 1; i < 50; ++i) {
+        globalCounters.incrementTotalCount();
+    }
+    EXPECT_EQ(50, globalCounters.getTotalCount());
+    globalCounters.halveCounters();
+    EXPECT_EQ(25, globalCounters.getTotalCount());
+    globalCounters.halveCounters();
+    EXPECT_EQ(12, globalCounters.getTotalCount());
+    for (int i = 0; i < 4; ++i) {
+        globalCounters.halveCounters();
+    }
+    EXPECT_EQ(0, globalCounters.getTotalCount());
+}
+
+}  // namespace
+}  // namespace latinime
diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp
index 4469dc7..86040f1 100644
--- a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp
+++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp
@@ -108,14 +108,14 @@
     languageModelDictContent.setNgramProbabilityEntry(prevWordIds.limit(1), wordId,
             &bigramProbabilityEntry);
     EXPECT_EQ(bigramProbability, languageModelDictContent.getWordAttributes(prevWordIds, wordId,
-            nullptr /* headerPolicy */).getProbability());
+            false /* mustMatchAllPrevWords */, nullptr /* headerPolicy */).getProbability());
     const ProbabilityEntry trigramProbabilityEntry(flag, trigramProbability);
     languageModelDictContent.setNgramProbabilityEntry(prevWordIds.limit(1),
             prevWordIds[1], &probabilityEntry);
     languageModelDictContent.setNgramProbabilityEntry(prevWordIds.limit(2), wordId,
             &trigramProbabilityEntry);
     EXPECT_EQ(trigramProbability, languageModelDictContent.getWordAttributes(prevWordIds, wordId,
-            nullptr /* headerPolicy */).getProbability());
+            false /* mustMatchAllPrevWords */, nullptr /* headerPolicy */).getProbability());
 }
 
 }  // namespace
diff --git a/native/jni/tests/suggest/policyimpl/dictionary/utils/probability_utils_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/utils/probability_utils_test.cpp
new file mode 100644
index 0000000..be1f278
--- /dev/null
+++ b/native/jni/tests/suggest/policyimpl/dictionary/utils/probability_utils_test.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#include "suggest/policyimpl/dictionary/utils/probability_utils.h"
+
+#include <gtest/gtest.h>
+
+#include "defines.h"
+
+namespace latinime {
+namespace {
+
+TEST(ProbabilityUtilsTest, TestEncodeRawProbability) {
+    EXPECT_EQ(MAX_PROBABILITY, ProbabilityUtils::encodeRawProbability(1.0f));
+    EXPECT_EQ(MAX_PROBABILITY - 9, ProbabilityUtils::encodeRawProbability(0.5f));
+    EXPECT_EQ(0, ProbabilityUtils::encodeRawProbability(0.0f));
+}
+
+}  // namespace
+}  // namespace latinime
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index f90b266..b976448 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -59,6 +59,7 @@
         super.setUp();
         mCurrentTime = 0;
         mDictFilesToBeDeleted.clear();
+        setCurrentTimeForTestMode(mCurrentTime);
     }
 
     @Override
@@ -71,8 +72,8 @@
         super.tearDown();
     }
 
-    private static boolean supportsBeginningOfSentence(final int formatVersion) {
-        return formatVersion > FormatSpec.VERSION401;
+    private static boolean supportsCountBasedNgram(final int formatVersion) {
+        return formatVersion >= FormatSpec.VERSION4_DEV;
     }
 
     private static boolean supportsNgram(final int formatVersion) {
@@ -142,19 +143,13 @@
 
     private File createEmptyDictionaryWithAttributeMapAndGetFile(final int formatVersion,
             final HashMap<String, String> attributeMap) {
-        if (formatVersion == FormatSpec.VERSION4
-                || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING
-                || formatVersion == FormatSpec.VERSION4_DEV) {
-            try {
-                final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion,
-                        attributeMap);
-                mDictFilesToBeDeleted.add(dictFile);
-                return dictFile;
-            } catch (final IOException e) {
-                fail(e.toString());
-            }
-        } else {
-            fail("Dictionary format version " + formatVersion + " is not supported.");
+        try {
+            final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion,
+                    attributeMap);
+            mDictFilesToBeDeleted.add(dictFile);
+            return dictFile;
+        } catch (final IOException e) {
+            fail(e.toString());
         }
         return null;
     }
@@ -263,12 +258,10 @@
         onInputWord(binaryDictionary, "a", false /* isValidWord */);
         assertTrue(binaryDictionary.isValidWord("a"));
 
-        onInputWord(binaryDictionary, "b", true /* isValidWord */);
-        assertTrue(binaryDictionary.isValidWord("b"));
-
         onInputWordWithPrevWord(binaryDictionary, "b", false /* isValidWord */, "a");
         assertFalse(isValidBigram(binaryDictionary, "a", "b"));
         onInputWordWithPrevWord(binaryDictionary, "b", false /* isValidWord */, "a");
+        assertTrue(binaryDictionary.isValidWord("b"));
         assertTrue(isValidBigram(binaryDictionary, "a", "b"));
 
         onInputWordWithPrevWord(binaryDictionary, "c", true /* isValidWord */, "a");
@@ -284,16 +277,12 @@
             return;
         }
 
-        onInputWordWithPrevWords(binaryDictionary, "c", false /* isValidWord */, "b", "a");
-        assertFalse(isValidTrigram(binaryDictionary, "a", "b", "c"));
-        assertFalse(isValidBigram(binaryDictionary, "b", "c"));
-        onInputWordWithPrevWords(binaryDictionary, "c", false /* isValidWord */, "b", "a");
+        onInputWordWithPrevWords(binaryDictionary, "c", true /* isValidWord */, "b", "a");
         assertTrue(isValidTrigram(binaryDictionary, "a", "b", "c"));
         assertTrue(isValidBigram(binaryDictionary, "b", "c"));
-
-        onInputWordWithPrevWords(binaryDictionary, "d", true /* isValidWord */, "b", "a");
-        assertTrue(isValidTrigram(binaryDictionary, "a", "b", "d"));
-        assertTrue(isValidBigram(binaryDictionary, "b", "d"));
+        onInputWordWithPrevWords(binaryDictionary, "d", false /* isValidWord */, "c", "b");
+        assertFalse(isValidTrigram(binaryDictionary, "b", "c", "d"));
+        assertFalse(isValidBigram(binaryDictionary, "c", "d"));
 
         onInputWordWithPrevWords(binaryDictionary, "cd", true /* isValidWord */, "b", "a");
         assertTrue(isValidTrigram(binaryDictionary, "a", "b", "cd"));
@@ -312,6 +301,13 @@
         onInputWord(binaryDictionary, "a", true /* isValidWord */);
         assertTrue(binaryDictionary.isValidWord("a"));
         forcePassingShortTime(binaryDictionary);
+        if (supportsCountBasedNgram(formatVersion)) {
+            // Count based ngram language model doesn't support decaying based on the elapsed time.
+            assertTrue(binaryDictionary.isValidWord("a"));
+        } else {
+            assertFalse(binaryDictionary.isValidWord("a"));
+        }
+        forcePassingLongTime(binaryDictionary);
         assertFalse(binaryDictionary.isValidWord("a"));
 
         onInputWord(binaryDictionary, "a", true /* isValidWord */);
@@ -327,6 +323,12 @@
         onInputWordWithPrevWord(binaryDictionary, "b", true /* isValidWord */, "a");
         assertTrue(isValidBigram(binaryDictionary, "a", "b"));
         forcePassingShortTime(binaryDictionary);
+        if (supportsCountBasedNgram(formatVersion)) {
+            assertTrue(isValidBigram(binaryDictionary, "a", "b"));
+        } else {
+            assertFalse(isValidBigram(binaryDictionary, "a", "b"));
+        }
+        forcePassingLongTime(binaryDictionary);
         assertFalse(isValidBigram(binaryDictionary, "a", "b"));
 
         onInputWord(binaryDictionary, "a", true /* isValidWord */);
@@ -349,7 +351,7 @@
         onInputWordWithPrevWord(binaryDictionary, "bc", true /* isValidWord */, "ab");
         onInputWordWithPrevWords(binaryDictionary, "cd", true /* isValidWord */, "bc", "ab");
         assertTrue(isValidTrigram(binaryDictionary, "ab", "bc", "cd"));
-        forcePassingShortTime(binaryDictionary);
+        forcePassingLongTime(binaryDictionary);
         assertFalse(isValidTrigram(binaryDictionary, "ab", "bc", "cd"));
 
         onInputWord(binaryDictionary, "ab", true /* isValidWord */);
@@ -540,7 +542,7 @@
                 assertTrue(bigramCountBeforeGC > bigramCountAfterGC);
             }
         }
-
+        forcePassingShortTime(binaryDictionary);
         assertTrue(Integer.parseInt(binaryDictionary.getPropertyForGettingStats(
                 BinaryDictionary.BIGRAM_COUNT_QUERY)) > 0);
         assertTrue(Integer.parseInt(binaryDictionary.getPropertyForGettingStats(
@@ -666,14 +668,17 @@
         assertEquals(toFormatVersion, binaryDictionary.getFormatVersion());
         assertTrue(binaryDictionary.isValidWord("aaa"));
         assertFalse(binaryDictionary.isValidWord("bbb"));
-        assertTrue(binaryDictionary.getFrequency("aaa") < binaryDictionary.getFrequency("ccc"));
-        onInputWord(binaryDictionary, "bbb", false /* isValidWord */);
-        assertTrue(binaryDictionary.isValidWord("bbb"));
+        if (supportsCountBasedNgram(toFormatVersion)) {
+            assertTrue(binaryDictionary.getFrequency("aaa") < binaryDictionary.getFrequency("ccc"));
+            onInputWord(binaryDictionary, "bbb", false /* isValidWord */);
+            assertTrue(binaryDictionary.isValidWord("bbb"));
+        }
         assertTrue(isValidBigram(binaryDictionary, "aaa", "abc"));
         assertFalse(isValidBigram(binaryDictionary, "aaa", "bbb"));
-        onInputWordWithPrevWord(binaryDictionary, "bbb", false /* isValidWord */, "aaa");
-        assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb"));
-
+        if (supportsCountBasedNgram(toFormatVersion)) {
+            onInputWordWithPrevWord(binaryDictionary, "bbb", false /* isValidWord */, "aaa");
+            assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb"));
+        }
         if (supportsNgram(toFormatVersion)) {
             assertTrue(isValidTrigram(binaryDictionary, "aaa", "abc", "xyz"));
             assertFalse(isValidTrigram(binaryDictionary, "aaa", "abc", "def"));
@@ -686,9 +691,7 @@
 
     public void testBeginningOfSentence() {
         for (final int formatVersion : DICT_FORMAT_VERSIONS) {
-            if (supportsBeginningOfSentence(formatVersion)) {
-                testBeginningOfSentence(formatVersion);
-            }
+            testBeginningOfSentence(formatVersion);
         }
     }
 
@@ -716,10 +719,8 @@
         assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa"));
         assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb"));
         onInputWordWithBeginningOfSentenceContext(binaryDictionary, "aaa", true /* isValidWord */);
-        assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa"));
         onInputWordWithBeginningOfSentenceContext(binaryDictionary, "aaa", true /* isValidWord */);
         onInputWordWithBeginningOfSentenceContext(binaryDictionary, "bbb", true /* isValidWord */);
-        assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb"));
         onInputWordWithBeginningOfSentenceContext(binaryDictionary, "bbb", true /* isValidWord */);
         assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa"));
         assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb"));
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index a35fa13..d239f8d 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -314,14 +314,14 @@
         final String dictVersion = Long.toString(System.currentTimeMillis());
         final String codePointTableAttribute = DictionaryHeader.CODE_POINT_TABLE_KEY;
         final File file = BinaryDictUtils.getDictFile(dictName, dictVersion,
-                BinaryDictUtils.VERSION201_OPTIONS, getContext().getCacheDir());
+                BinaryDictUtils.STATIC_OPTIONS, getContext().getCacheDir());
 
         // Write a test dictionary
         final DictEncoder dictEncoder = new Ver2DictEncoder(file,
                 Ver2DictEncoder.CODE_POINT_TABLE_ON);
         final FormatSpec.FormatOptions formatOptions =
                 new FormatSpec.FormatOptions(
-                        FormatSpec.MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE);
+                        FormatSpec.MINIMUM_SUPPORTED_STATIC_VERSION);
         final FusionDictionary sourcedict = new FusionDictionary(new PtNodeArray(),
                 BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions));
         addUnigrams(words.size(), sourcedict, words, null /* shortcutMap */);
@@ -359,11 +359,11 @@
         final List<String> results = new ArrayList<>();
 
         runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER,
-                BinaryDictUtils.VERSION2_OPTIONS);
+                BinaryDictUtils.STATIC_OPTIONS);
         runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER,
-                BinaryDictUtils.VERSION4_OPTIONS_WITHOUT_TIMESTAMP);
+                BinaryDictUtils.DYNAMIC_OPTIONS_WITHOUT_TIMESTAMP);
         runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER,
-                BinaryDictUtils.VERSION4_OPTIONS_WITH_TIMESTAMP);
+                BinaryDictUtils.DYNAMIC_OPTIONS_WITH_TIMESTAMP);
         for (final String result : results) {
             Log.d(TAG, result);
         }
@@ -373,11 +373,11 @@
         final List<String> results = new ArrayList<>();
 
         runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY,
-                BinaryDictUtils.VERSION2_OPTIONS);
+                BinaryDictUtils.STATIC_OPTIONS);
         runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY,
-                BinaryDictUtils.VERSION4_OPTIONS_WITHOUT_TIMESTAMP);
+                BinaryDictUtils.DYNAMIC_OPTIONS_WITHOUT_TIMESTAMP);
         runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY,
-                BinaryDictUtils.VERSION4_OPTIONS_WITH_TIMESTAMP);
+                BinaryDictUtils.DYNAMIC_OPTIONS_WITH_TIMESTAMP);
 
         for (final String result : results) {
             Log.d(TAG, result);
@@ -501,7 +501,7 @@
         final ArrayList<String> results = new ArrayList<>();
 
         runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_BUFFER,
-                BinaryDictUtils.VERSION2_OPTIONS);
+                BinaryDictUtils.STATIC_OPTIONS);
 
         for (final String result : results) {
             Log.d(TAG, result);
@@ -512,7 +512,7 @@
         final ArrayList<String> results = new ArrayList<>();
 
         runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_ARRAY,
-                BinaryDictUtils.VERSION2_OPTIONS);
+                BinaryDictUtils.STATIC_OPTIONS);
 
         for (final String result : results) {
             Log.d(TAG, result);
@@ -623,9 +623,9 @@
         final ArrayList<String> results = new ArrayList<>();
 
         runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_ARRAY,
-                BinaryDictUtils.VERSION2_OPTIONS);
+                BinaryDictUtils.STATIC_OPTIONS);
         runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_BUFFER,
-                BinaryDictUtils.VERSION2_OPTIONS);
+                BinaryDictUtils.STATIC_OPTIONS);
 
         for (final String result : results) {
             Log.d(TAG, result);
@@ -633,7 +633,7 @@
     }
 
     public void testVer2DictGetWordProperty() {
-        final FormatOptions formatOptions = BinaryDictUtils.VERSION2_OPTIONS;
+        final FormatOptions formatOptions = BinaryDictUtils.STATIC_OPTIONS;
         final ArrayList<String> words = sWords;
         final HashMap<String, List<String>> shortcuts = sShortcuts;
         final String dictName = "testGetWordProperty";
@@ -669,7 +669,7 @@
     }
 
     public void testVer2DictIteration() {
-        final FormatOptions formatOptions = BinaryDictUtils.VERSION2_OPTIONS;
+        final FormatOptions formatOptions = BinaryDictUtils.STATIC_OPTIONS;
         final ArrayList<String> words = sWords;
         final HashMap<String, List<String>> shortcuts = sShortcuts;
         final SparseArray<List<Integer>> bigrams = sEmptyBigrams;
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
index 60e3825..ce905c4 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
@@ -819,12 +819,18 @@
             final ArrayList<Entry<Integer, Integer>> codePointOccurrenceArray)
                     throws IOException, UnsupportedFormatException {
         final int version = formatOptions.mVersion;
-        if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
-                || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
+        if ((version >= FormatSpec.MINIMUM_SUPPORTED_STATIC_VERSION &&
+                version <= FormatSpec.MAXIMUM_SUPPORTED_STATIC_VERSION) || (
+                version >= FormatSpec.MINIMUM_SUPPORTED_DYNAMIC_VERSION &&
+                version <= FormatSpec.MAXIMUM_SUPPORTED_DYNAMIC_VERSION)) {
+            // Dictionary is valid
+        } else {
             throw new UnsupportedFormatException("Requested file format version " + version
-                    + ", but this implementation only supports versions "
-                    + FormatSpec.MINIMUM_SUPPORTED_VERSION + " through "
-                    + FormatSpec.MAXIMUM_SUPPORTED_VERSION);
+                    + ", but this implementation only supports static versions "
+                    + FormatSpec.MINIMUM_SUPPORTED_STATIC_VERSION + " through "
+                    + FormatSpec.MAXIMUM_SUPPORTED_STATIC_VERSION + " and dynamic versions "
+                    + FormatSpec.MINIMUM_SUPPORTED_DYNAMIC_VERSION + " through "
+                    + FormatSpec.MAXIMUM_SUPPORTED_DYNAMIC_VERSION);
         }
 
         ByteArrayOutputStream headerBuffer = new ByteArrayOutputStream(256);
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
index 8eabf74..9c1e4cf 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
@@ -28,13 +28,11 @@
 
     public static final String TEST_DICT_FILE_EXTENSION = ".testDict";
 
-    public static final FormatSpec.FormatOptions VERSION2_OPTIONS =
-            new FormatSpec.FormatOptions(FormatSpec.VERSION2);
-    public static final FormatSpec.FormatOptions VERSION201_OPTIONS =
-            new FormatSpec.FormatOptions(FormatSpec.VERSION201);
-    public static final FormatSpec.FormatOptions VERSION4_OPTIONS_WITHOUT_TIMESTAMP =
+    public static final FormatSpec.FormatOptions STATIC_OPTIONS =
+            new FormatSpec.FormatOptions(FormatSpec.VERSION202);
+    public static final FormatSpec.FormatOptions DYNAMIC_OPTIONS_WITHOUT_TIMESTAMP =
             new FormatSpec.FormatOptions(FormatSpec.VERSION4, false /* hasTimestamp */);
-    public static final FormatSpec.FormatOptions VERSION4_OPTIONS_WITH_TIMESTAMP =
+    public static final FormatSpec.FormatOptions DYNAMIC_OPTIONS_WITH_TIMESTAMP =
             new FormatSpec.FormatOptions(FormatSpec.VERSION4, true /* hasTimestamp */);
 
     public static DictionaryOptions makeDictionaryOptions(final String id, final String version,
@@ -55,7 +53,8 @@
     public static File getDictFile(final String name, final String version,
             final FormatOptions formatOptions, final File directory) {
         if (formatOptions.mVersion == FormatSpec.VERSION2
-                || formatOptions.mVersion == FormatSpec.VERSION201) {
+                || formatOptions.mVersion == FormatSpec.VERSION201
+                || formatOptions.mVersion == FormatSpec.VERSION202) {
             return new File(directory, name + "." + version + TEST_DICT_FILE_EXTENSION);
         } else if (formatOptions.mVersion == FormatSpec.VERSION4) {
             return new File(directory, name + "." + version);
@@ -71,7 +70,7 @@
                 file.mkdir();
             }
             return new Ver4DictEncoder(file);
-        } else if (formatOptions.mVersion == FormatSpec.VERSION2) {
+        } else if (formatOptions.mVersion == FormatSpec.VERSION202) {
             return new Ver2DictEncoder(file, Ver2DictEncoder.CODE_POINT_TABLE_OFF);
         } else {
             throw new RuntimeException("The format option has a wrong version : "
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
index 457e7af..5c261a9 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
@@ -178,7 +178,8 @@
             throw new IOException("Cannot read the dictionary header.");
         }
         if (header.mFormatOptions.mVersion != FormatSpec.VERSION2 &&
-                header.mFormatOptions.mVersion != FormatSpec.VERSION201) {
+                header.mFormatOptions.mVersion != FormatSpec.VERSION201 &&
+                header.mFormatOptions.mVersion != FormatSpec.VERSION202) {
             throw new UnsupportedFormatException("File header has a wrong version : "
                     + header.mFormatOptions.mVersion);
         }
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
index 2c2152b..b52b8c4 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
@@ -124,7 +124,8 @@
     @Override
     public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions)
             throws IOException, UnsupportedFormatException {
-        if (formatOptions.mVersion > FormatSpec.VERSION201) {
+        // We no longer support anything but the latest version of v2.
+        if (formatOptions.mVersion != FormatSpec.VERSION202) {
             throw new UnsupportedFormatException(
                     "The given format options has wrong version number : "
                     + formatOptions.mVersion);
diff --git a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
index a5979c3..dc4e2e4 100644
--- a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
@@ -29,14 +29,45 @@
 @SmallTest
 public class CollectionUtilsTests extends AndroidTestCase {
     /**
+     * Tests that {@link CollectionUtils#arrayAsList(Object[],int,int)} fails as expected
+     * with some invalid inputs.
+     */
+    public void testArrayAsListFailure() {
+        final String[] array = { "0", "1" };
+        // Negative start
+        try {
+            CollectionUtils.arrayAsList(array, -1, 1);
+            fail("Failed to catch start < 0");
+        } catch (final IllegalArgumentException e) {
+            assertEquals("Invalid start: -1 end: 1 with array.length: 2", e.getMessage());
+        }
+        // start > end
+        try {
+            CollectionUtils.arrayAsList(array, 1, -1);
+            fail("Failed to catch start > end");
+        } catch (final IllegalArgumentException e) {
+            assertEquals("Invalid start: 1 end: -1 with array.length: 2", e.getMessage());
+        }
+        // end > array.length
+        try {
+            CollectionUtils.arrayAsList(array, 1, 3);
+            fail("Failed to catch end > array.length");
+        } catch (final IllegalArgumentException e) {
+            assertEquals("Invalid start: 1 end: 3 with array.length: 2", e.getMessage());
+        }
+    }
+
+    /**
      * Tests that {@link CollectionUtils#arrayAsList(Object[],int,int)} gives the expected
      * results for a few valid inputs.
      */
     public void testArrayAsList() {
-        final String[] array = { "0", "1", "2", "3", "4" };
         final ArrayList<String> empty = new ArrayList<>();
+        assertEquals(empty, CollectionUtils.arrayAsList(new String[] { }, 0, 0));
+        final String[] array = { "0", "1", "2", "3", "4" };
         assertEquals(empty, CollectionUtils.arrayAsList(array, 0, 0));
         assertEquals(empty, CollectionUtils.arrayAsList(array, 1, 1));
+        assertEquals(empty, CollectionUtils.arrayAsList(array, array.length, array.length));
         final ArrayList<String> expected123 = new ArrayList<>(Arrays.asList("1", "2", "3"));
         assertEquals(expected123, CollectionUtils.arrayAsList(array, 1, 4));
     }
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java
index 48d2e59..955c572 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java
@@ -98,6 +98,7 @@
         String word = null;
         ProbabilityInfo probabilityInfo = new ProbabilityInfo(0);
         boolean isNotAWord = false;
+        boolean isPossiblyOffensive = false;
         ArrayList<WeightedString> bigrams = new ArrayList<>();
         ArrayList<WeightedString> shortcuts = new ArrayList<>();
         while (null != (line = reader.readLine())) {
@@ -106,7 +107,7 @@
             if (args[0].matches(CombinedFormatUtils.WORD_TAG + "=.*")) {
                 if (null != word) {
                     dict.add(word, probabilityInfo, shortcuts.isEmpty() ? null : shortcuts,
-                            isNotAWord, false /* isPossiblyOffensive */);
+                            isNotAWord, isPossiblyOffensive);
                     for (WeightedString s : bigrams) {
                         dict.setBigram(word, s.mWord, s.mProbabilityInfo);
                     }
@@ -114,27 +115,37 @@
                 if (!shortcuts.isEmpty()) shortcuts = new ArrayList<>();
                 if (!bigrams.isEmpty()) bigrams = new ArrayList<>();
                 isNotAWord = false;
+                isPossiblyOffensive = false;
                 for (String param : args) {
                     final String params[] = param.split("=", 2);
                     if (2 != params.length) throw new RuntimeException("Wrong format : " + line);
-                    if (CombinedFormatUtils.WORD_TAG.equals(params[0])) {
-                        word = params[1];
-                    } else if (CombinedFormatUtils.PROBABILITY_TAG.equals(params[0])) {
-                        probabilityInfo = new ProbabilityInfo(Integer.parseInt(params[1]),
-                                probabilityInfo.mTimestamp, probabilityInfo.mLevel,
-                                probabilityInfo.mCount);
-                    } else if (CombinedFormatUtils.HISTORICAL_INFO_TAG.equals(params[0])) {
-                        final String[] historicalInfoParams =
-                                params[1].split(CombinedFormatUtils.HISTORICAL_INFO_SEPARATOR);
-                        if (historicalInfoParams.length != HISTORICAL_INFO_ELEMENT_COUNT) {
-                            throw new RuntimeException("Wrong format (historical info) : " + line);
-                        }
-                        probabilityInfo = new ProbabilityInfo(probabilityInfo.mProbability,
-                                Integer.parseInt(historicalInfoParams[0]),
-                                Integer.parseInt(historicalInfoParams[1]),
-                                Integer.parseInt(historicalInfoParams[2]));
-                    } else if (CombinedFormatUtils.NOT_A_WORD_TAG.equals(params[0])) {
-                        isNotAWord = "true".equals(params[1]);
+                    switch (params[0]) {
+                        case CombinedFormatUtils.WORD_TAG:
+                            word = params[1];
+                            break;
+                        case CombinedFormatUtils.PROBABILITY_TAG:
+                            probabilityInfo = new ProbabilityInfo(Integer.parseInt(params[1]),
+                                    probabilityInfo.mTimestamp, probabilityInfo.mLevel,
+                                    probabilityInfo.mCount);
+                            break;
+                        case CombinedFormatUtils.HISTORICAL_INFO_TAG:
+                            final String[] historicalInfoParams = params[1].split(
+                                    CombinedFormatUtils.HISTORICAL_INFO_SEPARATOR);
+                            if (historicalInfoParams.length != HISTORICAL_INFO_ELEMENT_COUNT) {
+                                throw new RuntimeException("Wrong format (historical info) : "
+                                        + line);
+                            }
+                            probabilityInfo = new ProbabilityInfo(probabilityInfo.mProbability,
+                                    Integer.parseInt(historicalInfoParams[0]),
+                                    Integer.parseInt(historicalInfoParams[1]),
+                                    Integer.parseInt(historicalInfoParams[2]));
+                            break;
+                        case CombinedFormatUtils.NOT_A_WORD_TAG:
+                            isNotAWord = CombinedFormatUtils.isLiteralTrue(params[1]);
+                            break;
+                        case CombinedFormatUtils.POSSIBLY_OFFENSIVE_TAG:
+                            isPossiblyOffensive = CombinedFormatUtils.isLiteralTrue(params[1]);
+                            break;
                     }
                 }
             } else if (args[0].matches(CombinedFormatUtils.SHORTCUT_TAG + "=.*")) {
@@ -190,7 +201,7 @@
         }
         if (null != word) {
             dict.add(word, probabilityInfo, shortcuts.isEmpty() ? null : shortcuts, isNotAWord,
-                    false /* isPossiblyOffensive */);
+                    isPossiblyOffensive);
             for (WeightedString s : bigrams) {
                 dict.setBigram(word, s.mWord, s.mProbabilityInfo);
             }
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 8f9e4a3..6187853 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
@@ -120,7 +120,7 @@
             String inputCombined = null;
             String outputBinary = null;
             String outputCombined = null;
-            int outputBinaryFormatVersion = FormatSpec.VERSION201; // the default version is 201.
+            int outputBinaryFormatVersion = FormatSpec.VERSION202; // the default version is 202.
             // Don't use code point table by default.
             int codePointTableMode = Ver2DictEncoder.CODE_POINT_TABLE_OFF;