Merge "Making 'dicttool header' output format version"
diff --git a/common/Android.mk b/common/Android.mk
index 085543f..132a223 100644
--- a/common/Android.mk
+++ b/common/Android.mk
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 LOCAL_MODULE := latinime-common
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/common/src/com/android/inputmethod/latin/common/CollectionUtils.java
similarity index 97%
rename from java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
rename to common/src/com/android/inputmethod/latin/common/CollectionUtils.java
index 01f5e10..f7ba693 100644
--- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/CollectionUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin.utils;
+package com.android.inputmethod.latin.common;
 
 import java.util.ArrayList;
 import java.util.Collection;
diff --git a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java b/common/src/com/android/inputmethod/latin/common/CoordinateUtils.java
similarity index 98%
rename from java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java
rename to common/src/com/android/inputmethod/latin/common/CoordinateUtils.java
index 3a97059..0316624 100644
--- a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/CoordinateUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin.utils;
+package com.android.inputmethod.latin.common;
 
 import javax.annotation.Nonnull;
 
diff --git a/java/src/com/android/inputmethod/latin/utils/FileUtils.java b/common/src/com/android/inputmethod/latin/common/FileUtils.java
similarity index 96%
rename from java/src/com/android/inputmethod/latin/utils/FileUtils.java
rename to common/src/com/android/inputmethod/latin/common/FileUtils.java
index f1106a6..6768458 100644
--- a/java/src/com/android/inputmethod/latin/utils/FileUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/FileUtils.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.latin.utils;
+package com.android.inputmethod.latin.common;
 
 import java.io.File;
 import java.io.FilenameFilter;
diff --git a/java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java b/common/src/com/android/inputmethod/latin/common/LocaleUtils.java
similarity index 91%
rename from java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java
rename to common/src/com/android/inputmethod/latin/common/LocaleUtils.java
index 4f0805c..14b3d22 100644
--- a/java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/LocaleUtils.java
@@ -14,11 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.dictionarypack;
-
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.text.TextUtils;
+package com.android.inputmethod.latin.common;
 
 import java.util.HashMap;
 import java.util.Locale;
@@ -105,8 +101,8 @@
      * @return a constant that measures how well the tested locale matches the reference locale.
      */
     public static int getMatchLevel(final String referenceLocale, final String testedLocale) {
-        if (TextUtils.isEmpty(referenceLocale)) {
-            return TextUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH;
+        if (StringUtils.isEmpty(referenceLocale)) {
+            return StringUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH;
         }
         if (null == testedLocale) return LOCALE_NO_MATCH;
         final String[] referenceParams = referenceLocale.split("_", 3);
@@ -160,21 +156,6 @@
         return LOCALE_MATCH <= level;
     }
 
-    /**
-     * Sets the system locale for this process.
-     *
-     * @param res the resources to use. Pass current resources.
-     * @param newLocale the locale to change to.
-     * @return the old locale.
-     */
-    public static Locale setSystemLocale(final Resources res, final Locale newLocale) {
-        final Configuration conf = res.getConfiguration();
-        final Locale saveLocale = conf.locale;
-        conf.locale = newLocale;
-        res.updateConfiguration(conf, res.getDisplayMetrics());
-        return saveLocale;
-    }
-
     private static final HashMap<String, Locale> sLocaleCache = new HashMap<>();
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java b/common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java
similarity index 90%
rename from java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
rename to common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java
index 7603dbb..7ef741c 100644
--- a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
+++ b/common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin.settings;
+package com.android.inputmethod.latin.common;
 
 public class NativeSuggestOptions {
     // Need to update suggest_options.h when you add, remove or reorder options.
@@ -25,8 +25,11 @@
     private static final int WEIGHT_FOR_LOCALE_IN_THOUSANDS = 4;
     private static final int OPTIONS_SIZE = 5;
 
-    private final int[] mOptions = new int[OPTIONS_SIZE
-            + AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE];
+    private final int[] mOptions;
+
+    public NativeSuggestOptions(final int additionalFeaturesSettingsSize) {
+        mOptions = new int[OPTIONS_SIZE + additionalFeaturesSettingsSize];
+    }
 
     public void setIsGesture(final boolean value) {
         setBooleanOption(IS_GESTURE, value);
diff --git a/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
index bd54238..4e8a10b 100644
--- a/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
+++ b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
@@ -47,6 +47,7 @@
         // do nothing.
     }
 
+    @Nonnull
     public static RichInputMethodSubtype createRichInputMethodSubtype(
             @Nonnull final RichInputMethodManager imm,
             @Nonnull final InputMethodSubtype subtype,
diff --git a/java/Android.mk b/java/Android.mk
index a2c5697..b84b347 100644
--- a/java/Android.mk
+++ b/java/Android.mk
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := optional
diff --git a/java/res/values-af/strings-emoji-descriptions.xml b/java/res/values-af/strings-emoji-descriptions.xml
index 26bfc5f..5ed066a 100644
--- a/java/res/values-af/strings-emoji-descriptions.xml
+++ b/java/res/values-af/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Koekie"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Staaf sjokolade"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Lekkergoed"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Suiglekker"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Vla"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Heuningpot"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Broskoek"</string>
diff --git a/java/res/values-ar/strings-emoji-descriptions.xml b/java/res/values-ar/strings-emoji-descriptions.xml
index 875f626..c1decd3 100644
--- a/java/res/values-ar/strings-emoji-descriptions.xml
+++ b/java/res/values-ar/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"كعكة"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"بار شيكولاتة"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"حلوى"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"مصاصة"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"كاسترد"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"جرة عسل"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"كعكة بسمن وسكر"</string>
diff --git a/java/res/values-bn-rBD/strings-emoji-descriptions.xml b/java/res/values-bn-rBD/strings-emoji-descriptions.xml
index 4c66166..3c58621 100644
--- a/java/res/values-bn-rBD/strings-emoji-descriptions.xml
+++ b/java/res/values-bn-rBD/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"কুকি"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"চকোলেট বার"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ক্যান্ডি"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"ললিপপ"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"কাস্টার্ড"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"মধুর পাত্র"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"শর্টকেক"</string>
diff --git a/java/res/values-cs/strings-emoji-descriptions.xml b/java/res/values-cs/strings-emoji-descriptions.xml
index b664202..b7f359c 100644
--- a/java/res/values-cs/strings-emoji-descriptions.xml
+++ b/java/res/values-cs/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Sušenka"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Tabulka čokolády"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Bonbon"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lízátko"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Pudink"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Hrnek medu"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Dort"</string>
diff --git a/java/res/values-el/strings-emoji-descriptions.xml b/java/res/values-el/strings-emoji-descriptions.xml
index aa8966e..a15d0fd 100644
--- a/java/res/values-el/strings-emoji-descriptions.xml
+++ b/java/res/values-el/strings-emoji-descriptions.xml
@@ -269,7 +269,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Κούκι"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Σοκολάτα"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Γλυκά"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Γλειφιτζούρι"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Κρέμα"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Βάζο με μέλι"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Μπισκότο"</string>
diff --git a/java/res/values-fa/strings-emoji-descriptions.xml b/java/res/values-fa/strings-emoji-descriptions.xml
index cc670ee..8adb530 100644
--- a/java/res/values-fa/strings-emoji-descriptions.xml
+++ b/java/res/values-fa/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"کلوچه"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"تخته شکلات"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"آبنبات"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"آبنبات چوبی"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"آب‌نبات چوبی"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"کاستارد"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"ظرف عسل"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"کیک روغنی"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index 2461da5..16a6f87 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -84,7 +84,7 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"برای ذخیره اینجا را لمس کنید"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"دیکشنری موجود است"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"طرح زمینه صفحه‌کلید"</string>
-    <string name="switch_accounts" msgid="3321216593719006162">"جابجایی بین حساب‌ها"</string>
+    <string name="switch_accounts" msgid="3321216593719006162">"جابه‌جایی بین حساب‌ها"</string>
     <string name="no_accounts_selected" msgid="2073821619103904330">"هیچ حسابی انتخاب نشده است"</string>
     <string name="account_selected" msgid="2846876462199625974">"در حال حاضر در حال استفاده از <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
     <string name="account_select_ok" msgid="9141195141763227797">"تأیید"</string>
diff --git a/java/res/values-fi/strings-emoji-descriptions.xml b/java/res/values-fi/strings-emoji-descriptions.xml
index 72af3c2..7d6a9f9 100644
--- a/java/res/values-fi/strings-emoji-descriptions.xml
+++ b/java/res/values-fi/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Pikkuleipä"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Suklaapatukka"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Karamelli"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Tikkari"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Vanukas"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Hunajapurkki"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Kakkuviipale"</string>
diff --git a/java/res/values-fr/strings-emoji-descriptions.xml b/java/res/values-fr/strings-emoji-descriptions.xml
index b7ad706..1f99ee3 100644
--- a/java/res/values-fr/strings-emoji-descriptions.xml
+++ b/java/res/values-fr/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Biscuit"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Barre de chocolat"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Bonbon"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Sucette"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Crème anglaise"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Pot de miel"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Sablé"</string>
diff --git a/java/res/values-gl-rES/strings-emoji-descriptions.xml b/java/res/values-gl-rES/strings-emoji-descriptions.xml
index 31eb89b..cdb67fa 100644
--- a/java/res/values-gl-rES/strings-emoji-descriptions.xml
+++ b/java/res/values-gl-rES/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Galleta"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Barra de chocolate"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Caramelo"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Chupa-chupa"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Crema"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Tarro de mel"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Pastel"</string>
diff --git a/java/res/values-hi/strings-emoji-descriptions.xml b/java/res/values-hi/strings-emoji-descriptions.xml
index df5fa1e..1f18e6a 100644
--- a/java/res/values-hi/strings-emoji-descriptions.xml
+++ b/java/res/values-hi/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"कुकी"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"चॉकलेट बार"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"कैंडी"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"लॉलीपॉप"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"दही"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"शहद का बर्तन"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"शॉर्टकेक"</string>
diff --git a/java/res/values-hu/strings-emoji-descriptions.xml b/java/res/values-hu/strings-emoji-descriptions.xml
index 4f3d01c..b72f298 100644
--- a/java/res/values-hu/strings-emoji-descriptions.xml
+++ b/java/res/values-hu/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Sütemény"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Csokoládé"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Cukorka"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Nyalóka"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Sodó"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Mézesbödön"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Tortaszelet"</string>
diff --git a/java/res/values-hy-rAM/strings-emoji-descriptions.xml b/java/res/values-hy-rAM/strings-emoji-descriptions.xml
index f41f2fc..dcc718e 100644
--- a/java/res/values-hy-rAM/strings-emoji-descriptions.xml
+++ b/java/res/values-hy-rAM/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Թխվածքաբլիթ"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Շոկոլադի սալիկ"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Կոնֆետ"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Սառնաշաքար կոնֆետ"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Շաքարաքլոր"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Եփովի կրեմ"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Մեղրանոթ"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Տորթի կտոր"</string>
diff --git a/java/res/values-iw/strings-emoji-descriptions.xml b/java/res/values-iw/strings-emoji-descriptions.xml
index ab31403..fc4435a 100644
--- a/java/res/values-iw/strings-emoji-descriptions.xml
+++ b/java/res/values-iw/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"עוגייה"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"חפיסת שוקולד"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"סוכרייה"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"סוכרייה על מקל"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"רפרפת"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"סיר דבש"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"עוגת פירות"</string>
diff --git a/java/res/values-km-rKH/strings-emoji-descriptions.xml b/java/res/values-km-rKH/strings-emoji-descriptions.xml
index 757df50..e9b8780 100644
--- a/java/res/values-km-rKH/strings-emoji-descriptions.xml
+++ b/java/res/values-km-rKH/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"ខូគី"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"​​សូកូឡា"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ស្ករគ្រាប់"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"ស្ករ​គ្រាប់​មាន​ដង​​កាន់"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"សង់ខ្យា"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"ថូ"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"នំ​ខេក"</string>
diff --git a/java/res/values-kn-rIN/strings-emoji-descriptions.xml b/java/res/values-kn-rIN/strings-emoji-descriptions.xml
index 4e6d5ce..a013c27 100644
--- a/java/res/values-kn-rIN/strings-emoji-descriptions.xml
+++ b/java/res/values-kn-rIN/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"ಕುಕಿ"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"ಚಾಕೊಲೇಟ್ ಬಾರ್"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ಕ್ಯಾಂಡಿ"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"ಲಾಲಿಪಪ್"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"ಕಸ್ಟರ್ಡ್"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"ಜೇನಿನ ಮಡಕೆ"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"ಶಾರ್ಟ್‌ಕೇಕ್"</string>
diff --git a/java/res/values-ml-rIN/strings-emoji-descriptions.xml b/java/res/values-ml-rIN/strings-emoji-descriptions.xml
index ab65097..a846f31 100644
--- a/java/res/values-ml-rIN/strings-emoji-descriptions.xml
+++ b/java/res/values-ml-rIN/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"കുക്കി"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"ചോക്കലേറ്റ് ബാർ"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"മിഠായി"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"ലോലിപോപ്പ്"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"കസ്‌റ്റാർഡ്"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"തേൻ കുടം"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"ഷോർട്ട്‌കേക്ക്"</string>
diff --git a/java/res/values-nb/strings-emoji-descriptions.xml b/java/res/values-nb/strings-emoji-descriptions.xml
index fa655f1..6811188 100644
--- a/java/res/values-nb/strings-emoji-descriptions.xml
+++ b/java/res/values-nb/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Kjeks"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Sjokoladeplate"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Godteri"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Kjærlighet på pinne"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Pudding"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Honningkrukke"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Kakestykke"</string>
diff --git a/java/res/values-nl/strings-emoji-descriptions.xml b/java/res/values-nl/strings-emoji-descriptions.xml
index a02c21f..3b29890 100644
--- a/java/res/values-nl/strings-emoji-descriptions.xml
+++ b/java/res/values-nl/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Cookie"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Chocoladereep"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Snoep"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lolly"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Vla"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Honingpot"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Cake"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 2e0cd3b..2cc86e1 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -102,8 +102,7 @@
     <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_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacto)"</string>
     <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-emoji-descriptions.xml b/java/res/values-ro/strings-emoji-descriptions.xml
index f44a0b9..2ac84e5 100644
--- a/java/res/values-ro/strings-emoji-descriptions.xml
+++ b/java/res/values-ro/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Biscuit"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Ciocolată"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Bomboane"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Acadea"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Budincă"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Oală de miere"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Prăjitură"</string>
diff --git a/java/res/values-sw/strings-emoji-descriptions.xml b/java/res/values-sw/strings-emoji-descriptions.xml
index 5716387..329bf39 100644
--- a/java/res/values-sw/strings-emoji-descriptions.xml
+++ b/java/res/values-sw/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Biskuti"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Mchi wa chokoleti"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Peremende"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Peremende ya kijiti"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Faluda au Kastadi"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Emoji ya chungu cha asali"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Keki tamu yenye vitandamlo inayofanana na biskuti"</string>
diff --git a/java/res/values-th/strings-emoji-descriptions.xml b/java/res/values-th/strings-emoji-descriptions.xml
index e5ef9b8..86ab2c0 100644
--- a/java/res/values-th/strings-emoji-descriptions.xml
+++ b/java/res/values-th/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"คุกกี้"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"ช็อกโกแลตแท่ง"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ลูกกวาด"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"อมยิ้ม"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"คัสตาร์ด"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"โถน้ำผึ้ง"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"ชอร์ตเค้ก"</string>
diff --git a/java/res/values-ur-rPK/strings-emoji-descriptions.xml b/java/res/values-ur-rPK/strings-emoji-descriptions.xml
index d5bcf2f..e6bbdcf 100644
--- a/java/res/values-ur-rPK/strings-emoji-descriptions.xml
+++ b/java/res/values-ur-rPK/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"کوکی"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"چاکلیٹ بار"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"قندی"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"لالی پاپ"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"کسٹرڈ"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"شہد کا برتن"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"کیک کا ٹکڑا"</string>
diff --git a/java/res/values-vi/strings-emoji-descriptions.xml b/java/res/values-vi/strings-emoji-descriptions.xml
index 8b44dcf..492c726 100644
--- a/java/res/values-vi/strings-emoji-descriptions.xml
+++ b/java/res/values-vi/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
     <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Bánh quy"</string>
     <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Thanh sôcôla"</string>
     <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Kẹo"</string>
-    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Kẹo que"</string>
+    <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
     <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Món sữa trứng"</string>
     <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Mắt ong"</string>
     <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Bánh bơ giòn"</string>
diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
index 7fc1e9d..2de71ce 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
@@ -31,9 +31,9 @@
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 import com.android.inputmethod.latin.settings.Settings;
 import com.android.inputmethod.latin.settings.SettingsValues;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
 
 import java.util.List;
 
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index d3e24e3..be07443 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -27,8 +27,8 @@
 import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.utils.LocaleUtils;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
index 37fa76b..659fe5c 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
@@ -31,6 +31,7 @@
 import android.util.Log;
 
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.utils.DebugLogUtils;
 
 import java.io.File;
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
index e9b634e..e6acb8f 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
@@ -25,6 +25,7 @@
 import android.widget.Toast;
 
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.common.LocaleUtils;
 
 import java.util.Locale;
 import java.util.Random;
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
index c2dc879..14e0050 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.dictionarypack;
 
+import com.android.inputmethod.latin.common.LocaleUtils;
+
 import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java b/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java
index f1633ff..50b3c72 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java
@@ -26,6 +26,7 @@
 
 import com.android.inputmethod.annotations.ExternallyReferenced;
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.common.LocaleUtils;
 
 import java.util.Locale;
 
diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
index d59b7a5..bd61521 100644
--- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
+++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
@@ -39,6 +39,7 @@
 import com.android.inputmethod.compat.DownloadManagerCompatUtils;
 import com.android.inputmethod.compat.NotificationCompatUtils;
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.utils.ApplicationUtils;
 import com.android.inputmethod.latin.utils.DebugLogUtils;
 
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 3c90a04..619b801 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -22,7 +22,7 @@
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
 import com.android.inputmethod.keyboard.internal.KeyboardParams;
 import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 
 import java.util.ArrayList;
 import java.util.Collections;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 1637ed9..51f37fd 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -51,6 +51,9 @@
 import java.lang.ref.SoftReference;
 import java.util.HashMap;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 /**
  * This class represents a set of keyboard layouts. Each of them represents a different keyboard
  * specific to a keyboard state, such as alphabet, symbols, and so on.  Layouts in the same
@@ -82,6 +85,8 @@
     private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache =
             new HashMap<>();
     private static final KeysCache sKeysCache = new KeysCache();
+    private final static HashMap<InputMethodSubtype, Integer> sScriptIdsForSubtypes =
+            new HashMap<>();
 
     @SuppressWarnings("serial")
     public static final class KeyboardLayoutSetException extends RuntimeException {
@@ -140,6 +145,16 @@
         sKeysCache.clear();
     }
 
+    public static int getScriptId(final Resources resources, final InputMethodSubtype subtype) {
+        final Integer value = sScriptIdsForSubtypes.get(subtype);
+        if (null == value) {
+            final int scriptId = Builder.readScriptId(resources, subtype);
+            sScriptIdsForSubtypes.put(subtype, scriptId);
+            return scriptId;
+        }
+        return value;
+    }
+
     KeyboardLayoutSet(final Context context, final Params params) {
         mContext = context;
         mParams = params;
@@ -244,7 +259,7 @@
 
         private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo();
 
-        public Builder(final Context context, final EditorInfo ei) {
+        public Builder(final Context context, @Nullable final EditorInfo ei) {
             mContext = context;
             mPackageName = context.getPackageName();
             mResources = context.getResources();
@@ -265,7 +280,7 @@
             return this;
         }
 
-        public Builder setSubtype(final RichInputMethodSubtype subtype) {
+        public Builder setSubtype(@Nonnull final RichInputMethodSubtype subtype) {
             final boolean asciiCapable = InputMethodSubtypeCompatUtils.isAsciiCapable(subtype);
             // TODO: Consolidate with {@link InputAttributes}.
             @SuppressWarnings("deprecation")
@@ -303,31 +318,13 @@
             return this;
         }
 
-        public Builder setScriptId(final int scriptId) {
-            mParams.mScriptId = scriptId;
-            return this;
-        }
-
         public Builder setSplitLayoutEnabledByUser(final boolean enabled) {
             mParams.mIsSplitLayoutEnabledByUser = enabled;
             return this;
         }
 
-        private final static HashMap<InputMethodSubtype, Integer> sScriptIdsForSubtypes =
-                new HashMap<>();
-        public static int getScriptId(final Resources resources, final InputMethodSubtype subtype) {
-            final Integer value = sScriptIdsForSubtypes.get(subtype);
-            if (null == value) {
-                final int scriptId = readScriptId(resources, subtype);
-                sScriptIdsForSubtypes.put(subtype, scriptId);
-                return scriptId;
-            }
-            return value;
-        }
-
         // Super redux version of reading the script ID for some subtype from Xml.
-        private static int readScriptId(final Resources resources,
-                final InputMethodSubtype subtype) {
+        static int readScriptId(final Resources resources, final InputMethodSubtype subtype) {
             final String layoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX
                     + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
             final int xmlId = getXmlId(resources, layoutSetName);
@@ -414,7 +411,7 @@
                     if (TAG_ELEMENT.equals(tag)) {
                         parseKeyboardLayoutSetElement(parser);
                     } else if (TAG_FEATURE.equals(tag)) {
-                        parseKeyboardLayoutSetFeature(parser);
+                        mParams.mScriptId = readScriptIdFromTagFeature(mResources, parser);
                     } else {
                         throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD_SET);
                     }
@@ -459,12 +456,6 @@
             }
         }
 
-        private void parseKeyboardLayoutSetFeature(final XmlPullParser parser)
-                throws XmlPullParserException, IOException {
-            final int scriptId = readScriptIdFromTagFeature(mResources, parser);
-            setScriptId(scriptId);
-        }
-
         private static int getKeyboardMode(final EditorInfo editorInfo) {
             final int inputType = editorInfo.inputType;
             final int variation = inputType & InputType.TYPE_MASK_VARIATION;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index c2862f5..5e3a5f1 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -114,7 +114,7 @@
         final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
         final int keyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues);
         builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
-        builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
+        builder.setSubtype(RichInputMethodManager.getInstance().getCurrentSubtype());
         builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey);
         builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey());
         builder.setSplitLayoutEnabledByUser(ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED
@@ -123,7 +123,8 @@
         try {
             mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState);
             // TODO: revisit this for multi-lingual input
-            mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocales()[0],
+            mKeyboardTextsSet.setLocale(
+                    RichInputMethodManager.getInstance().getCurrentSubtypeLocales()[0],
                     mThemeContext);
         } catch (KeyboardLayoutSetException e) {
             Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
@@ -163,7 +164,7 @@
                 currentSettingsValues.mKeyPreviewDismissEndXScale,
                 currentSettingsValues.mKeyPreviewDismissEndYScale,
                 currentSettingsValues.mKeyPreviewDismissDuration);
-        keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
+        keyboardView.updateShortcutKey(RichInputMethodManager.getInstance().isShortcutImeReady());
         final boolean subtypeChanged = (oldKeyboard == null)
                 || !keyboard.mId.mSubtype.equals(oldKeyboard.mId.mSubtype);
         final int languageOnSpacebarFormatType = mSubtypeSwitcher.getLanguageOnSpacebarFormatType(
@@ -414,9 +415,10 @@
     }
 
     public void onNetworkStateChanged() {
-        if (mKeyboardView != null) {
-            mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
+        if (mKeyboardView == null) {
+            return;
         }
+        mKeyboardView.updateShortcutKey(RichInputMethodManager.getInstance().isShortcutImeReady());
     }
 
     public int getKeyboardShiftMode() {
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 1bad7cb..cba7ff2 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -56,9 +56,9 @@
 import com.android.inputmethod.latin.RichInputMethodSubtype;
 import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 import com.android.inputmethod.latin.common.StringUtils;
 import com.android.inputmethod.latin.settings.DebugSettings;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
 import com.android.inputmethod.latin.utils.TypefaceUtils;
 
 import java.util.Locale;
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index 0152253..3acc11b 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -31,7 +31,7 @@
 import com.android.inputmethod.keyboard.internal.KeyDrawParams;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 
 /**
  * A view that renders a virtual {@link MoreKeysKeyboard}. It handles rendering of keys and
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 41eb87f..7902ce8 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -35,10 +35,10 @@
 import com.android.inputmethod.keyboard.internal.TypingTimeRecorder;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 import com.android.inputmethod.latin.common.InputPointers;
 import com.android.inputmethod.latin.define.DebugFlags;
 import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
 import com.android.inputmethod.latin.utils.ResourceUtils;
 
 import java.util.ArrayList;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java
index a5d47ad..9c0d743 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java
@@ -24,7 +24,7 @@
 import android.util.AttributeSet;
 import android.widget.RelativeLayout;
 
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 
 import java.util.ArrayList;
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java
index 330ec52..5443c2a 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java
@@ -27,7 +27,7 @@
 import com.android.inputmethod.keyboard.PointerTracker;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 
 import javax.annotation.Nonnull;
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
index d376487..448f1b4 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
@@ -23,7 +23,7 @@
 import android.view.ViewGroup;
 
 import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 import com.android.inputmethod.latin.utils.ViewLayoutUtils;
 
 import java.util.ArrayDeque;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java b/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
index 21eaed9..2a70ef5 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
@@ -25,6 +25,8 @@
 import java.util.List;
 import java.util.Locale;
 
+import javax.annotation.Nonnull;
+
 /**
  * This class determines that the language name on the spacebar should be displayed in what format.
  */
@@ -37,7 +39,7 @@
     private List<InputMethodSubtype> mEnabledSubtypes = Collections.emptyList();
     private boolean mIsSystemLanguageSameAsInputLanguage;
 
-    public int getLanguageOnSpacebarFormatType(final RichInputMethodSubtype subtype) {
+    public int getLanguageOnSpacebarFormatType(@Nonnull final RichInputMethodSubtype subtype) {
         if (subtype.isNoLanguage()) {
             return FORMAT_TYPE_FULL_LOCALE;
         }
@@ -65,11 +67,30 @@
                 : FORMAT_TYPE_LANGUAGE_ONLY;
     }
 
-    public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) {
+    public void onUpdateEnabledSubtypes(@Nonnull final List<InputMethodSubtype> enabledSubtypes) {
         mEnabledSubtypes = enabledSubtypes;
     }
 
-    public void updateIsSystemLanguageSameAsInputLanguage(final boolean isSame) {
-        mIsSystemLanguageSameAsInputLanguage = isSame;
+    public void onSubtypeChanged(@Nonnull final RichInputMethodSubtype subtype,
+           final boolean implicitlyEnabledSubtype, @Nonnull final Locale systemLocale) {
+        final Locale[] newLocales = subtype.getLocales();
+        if (newLocales.length > 1) {
+            // In multi-locales mode, the system language is never the same as the input language
+            // because there is no single input language.
+            mIsSystemLanguageSameAsInputLanguage = false;
+            return;
+        }
+        final Locale newLocale = newLocales[0];
+        if (systemLocale.equals(newLocale)) {
+            mIsSystemLanguageSameAsInputLanguage = true;
+            return;
+        }
+        if (!systemLocale.getLanguage().equals(newLocale.getLanguage())) {
+            mIsSystemLanguageSameAsInputLanguage = false;
+            return;
+        }
+        // If the subtype is enabled explicitly, the language name should be displayed even when
+        // the keyboard language and the system language are equal.
+        mIsSystemLanguageSameAsInputLanguage = implicitlyEnabledSubtype;
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
index a0bb406..b1a3887 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
@@ -21,10 +21,10 @@
 
 import com.android.inputmethod.compat.CharacterCompat;
 import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.latin.common.CollectionUtils;
 import com.android.inputmethod.latin.common.Constants;
 import com.android.inputmethod.latin.common.StringUtils;
 import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.utils.CollectionUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
index 3a9aa81..8a375c6 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
@@ -22,7 +22,7 @@
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.KeyDetector;
 import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 
 public final class NonDistinctMultitouchHelper {
     private static final String TAG = NonDistinctMultitouchHelper.class.getSimpleName();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java
index ef4c74d..73a6f95 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java
@@ -23,7 +23,7 @@
 
 import com.android.inputmethod.keyboard.PointerTracker;
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 
 /**
  * Draw rubber band preview graphics during sliding key input.
diff --git a/java/src/com/android/inputmethod/latin/AssetFileAddress.java b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
index fd6c24d..923f43c 100644
--- a/java/src/com/android/inputmethod/latin/AssetFileAddress.java
+++ b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.latin;
 
-import com.android.inputmethod.latin.utils.FileUtils;
+import com.android.inputmethod.latin.common.FileUtils;
 
 import java.io.File;
 
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index b5d0b44..46cd3b8 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -24,6 +24,7 @@
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.common.ComposedData;
 import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.FileUtils;
 import com.android.inputmethod.latin.common.InputPointers;
 import com.android.inputmethod.latin.common.StringUtils;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
@@ -33,7 +34,6 @@
 import com.android.inputmethod.latin.makedict.WordProperty;
 import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
 import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
 import com.android.inputmethod.latin.utils.WordInputEventForPersonalization;
 
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 1570bda..46c8d55 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -21,11 +21,11 @@
 import android.content.res.AssetFileDescriptor;
 import android.util.Log;
 
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
 import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
-import com.android.inputmethod.latin.utils.LocaleUtils;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
index 95390aa..aefefd3 100644
--- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java
+++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
@@ -17,7 +17,8 @@
 package com.android.inputmethod.latin;
 
 import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.settings.NativeSuggestOptions;
+import com.android.inputmethod.latin.common.NativeSuggestOptions;
+import com.android.inputmethod.latin.settings.AdditionalFeaturesSettingUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
 
 import java.util.Locale;
@@ -43,7 +44,8 @@
     public final int[] mOutputAutoCommitFirstWordConfidence = new int[1];
     public final float[] mInputOutputWeightOfLangModelVsSpatialModel = new float[1];
 
-    public final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions();
+    public final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions(
+            AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE);
 
     private static native long setDicTraverseSessionNative(String locale, long dictSize);
     private static native void initDicTraverseSessionNative(long nativeDicTraverseSession,
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index b47eaa9..d9d22e0 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -23,6 +23,7 @@
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.common.ComposedData;
 import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.FileUtils;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
 import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
@@ -32,7 +33,6 @@
 import com.android.inputmethod.latin.utils.CombinedFormatUtils;
 import com.android.inputmethod.latin.utils.DistracterFilter;
 import com.android.inputmethod.latin.utils.ExecutorUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
 import com.android.inputmethod.latin.utils.WordInputEventForPersonalization;
 
 import java.io.File;
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index eb0037e..719656b 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -77,6 +77,7 @@
 import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 import com.android.inputmethod.latin.common.InputPointers;
 import com.android.inputmethod.latin.define.DebugFlags;
 import com.android.inputmethod.latin.define.ProductionFlags;
@@ -93,7 +94,6 @@
 import com.android.inputmethod.latin.touchinputconsumer.GestureConsumer;
 import com.android.inputmethod.latin.utils.ApplicationUtils;
 import com.android.inputmethod.latin.utils.CapsModeUtils;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
 import com.android.inputmethod.latin.utils.CursorAnchorInfoUtils;
 import com.android.inputmethod.latin.utils.DialogUtils;
 import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
@@ -603,7 +603,7 @@
     // Has to be package-visible for unit tests
     @UsedForTesting
     void loadSettings() {
-        final Locale[] locales = mSubtypeSwitcher.getCurrentSubtypeLocales();
+        final Locale[] locales = mRichImm.getCurrentSubtypeLocales();
         final EditorInfo editorInfo = getCurrentInputEditorInfo();
         final InputAttributes inputAttributes = new InputAttributes(
                 editorInfo, isFullscreenMode(), getPackageName());
@@ -657,7 +657,7 @@
     }
 
     void resetDictionaryFacilitatorIfNecessary() {
-        final Locale[] subtypeSwitcherLocales = mSubtypeSwitcher.getCurrentSubtypeLocales();
+        final Locale[] subtypeSwitcherLocales = mRichImm.getCurrentSubtypeLocales();
         if (mDictionaryFacilitator.isForLocales(subtypeSwitcherLocales)) {
             return;
         }
@@ -909,7 +909,7 @@
         // Update to a gesture consumer with the current editor and IME state.
         mGestureConsumer = GestureConsumer.newInstance(editorInfo,
                 mInputLogic.getPrivateCommandPerformer(),
-                Arrays.asList(mSubtypeSwitcher.getCurrentSubtypeLocales()),
+                Arrays.asList(mRichImm.getCurrentSubtypeLocales()),
                 switcher.getKeyboard());
 
         // Forward this event to the accessibility utilities, if enabled.
@@ -947,7 +947,7 @@
             // span, so we should reset our state unconditionally, even if restarting is true.
             // We also tell the input logic about the combining rules for the current subtype, so
             // it can adjust its combiners if needed.
-            mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype(),
+            mInputLogic.startInput(mRichImm.getCombiningRulesExtraValueOfCurrentSubtype(),
                     currentSettingsValues);
 
             resetDictionaryFacilitatorIfNecessary();
@@ -1424,7 +1424,7 @@
     // completely replace #onCodeInput.
     public void onEvent(@Nonnull final Event event) {
         if (Constants.CODE_SHORTCUT == event.mKeyCode) {
-            mSubtypeSwitcher.switchToShortcutIME(this);
+            mRichImm.switchToShortcutIME(this);
         }
         final InputTransaction completeInputTransaction =
                 mInputLogic.onCodeInput(mSettings.getCurrent(), event,
@@ -1468,7 +1468,7 @@
     public void onStartBatchInput() {
         mInputLogic.onStartBatchInput(mSettings.getCurrent(), mKeyboardSwitcher, mHandler);
         mGestureConsumer.onGestureStarted(
-                Arrays.asList(mSubtypeSwitcher.getCurrentSubtypeLocales()),
+                Arrays.asList(mRichImm.getCurrentSubtypeLocales()),
                 mKeyboardSwitcher.getKeyboard());
     }
 
@@ -1590,7 +1590,7 @@
                 // We should clear the contextual strip if there is no suggestion from dictionaries.
                 || noSuggestionsFromDictionaries) {
             mSuggestionStripView.setSuggestions(suggestedWords,
-                    mSubtypeSwitcher.getCurrentSubtype().isRtlSubtype());
+                    mRichImm.getCurrentSubtype().isRtlSubtype());
         }
     }
 
@@ -1811,7 +1811,7 @@
         public void onReceive(final Context context, final Intent intent) {
             final String action = intent.getAction();
             if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
-                mSubtypeSwitcher.onNetworkStateChanged(intent);
+                mRichImm.onNetworkStateChanged(intent);
             } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
                 AudioAndHapticFeedbackManager.getInstance().onRingerModeChanged();
             }
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
index a53837a..a1ac55a 100644
--- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
@@ -17,9 +17,15 @@
 package com.android.inputmethod.latin;
 
 import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE;
+import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.SharedPreferences;
+import android.inputmethodservice.InputMethodService;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.AsyncTask;
 import android.os.Build;
 import android.os.IBinder;
 import android.preference.PreferenceManager;
@@ -28,9 +34,9 @@
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
-import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
-import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.latin.settings.AdditionalFeaturesSettingUtils;
 import com.android.inputmethod.latin.settings.Settings;
 import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
@@ -41,6 +47,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Set;
 
 import javax.annotation.Nonnull;
@@ -51,6 +58,7 @@
 // non final for easy mocking.
 public class RichInputMethodManager {
     private static final String TAG = RichInputMethodManager.class.getSimpleName();
+    private static final boolean DEBUG = false;
 
     private RichInputMethodManager() {
         // This utility class is not publicly instantiable.
@@ -61,6 +69,10 @@
     private Context mContext;
     private InputMethodManagerCompatWrapper mImmWrapper;
     private InputMethodInfoCache mInputMethodInfoCache;
+    private RichInputMethodSubtype mCurrentRichInputMethodSubtype;
+    private InputMethodInfo mShortcutInputMethodInfo;
+    private InputMethodSubtype mShortcutSubtype;
+    private boolean mIsNetworkConnected;
     final HashMap<InputMethodInfo, List<InputMethodSubtype>>
             mSubtypeListCacheWithImplicitlySelectedSubtypes = new HashMap<>();
     final HashMap<InputMethodInfo, List<InputMethodSubtype>>
@@ -100,6 +112,11 @@
         SubtypeLocaleUtils.init(context);
         final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes(context);
         setAdditionalInputMethodSubtypes(additionalSubtypes);
+
+        final ConnectivityManager connectivityManager =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        final NetworkInfo info = connectivityManager.getActiveNetworkInfo();
+        mIsNetworkConnected = (info != null && info.isConnected());
     }
 
     public InputMethodSubtype[] getAdditionalSubtypes(final Context context) {
@@ -309,10 +326,47 @@
     }
 
     @Nonnull
+    public RichInputMethodSubtype onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) {
+        final RichInputMethodSubtype richSubtype = createCurrentRichInputMethodSubtype(newSubtype);
+        if (DEBUG) {
+            Log.w(TAG, "onSubtypeChanged: " + richSubtype.getNameForLogging());
+        }
+        mCurrentRichInputMethodSubtype = richSubtype;
+        return richSubtype;
+    }
+
+    private static RichInputMethodSubtype sForcedSubtypeForTesting = null;
+
+    @UsedForTesting
+    static void forceSubtype(final InputMethodSubtype subtype) {
+        sForcedSubtypeForTesting = new RichInputMethodSubtype(subtype);
+    }
+
+    public Locale[] getCurrentSubtypeLocales() {
+        if (null != sForcedSubtypeForTesting) {
+            return sForcedSubtypeForTesting.getLocales();
+        }
+        return getCurrentSubtype().getLocales();
+    }
+
+    public RichInputMethodSubtype getCurrentSubtype() {
+        if (null != sForcedSubtypeForTesting) {
+            return sForcedSubtypeForTesting;
+        }
+        return mCurrentRichInputMethodSubtype;
+    }
+
+
+    public String getCombiningRulesExtraValueOfCurrentSubtype() {
+        return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype().getRawSubtype());
+    }
+
+    @Nonnull
     public InputMethodSubtype getCurrentRawSubtype() {
         return mImmWrapper.mImm.getCurrentInputMethodSubtype();
     }
 
+    @Nonnull
     public RichInputMethodSubtype createCurrentRichInputMethodSubtype(
             @Nonnull final InputMethodSubtype rawSubtype) {
         return AdditionalFeaturesSettingUtils.createRichInputMethodSubtype(this, rawSubtype,
@@ -462,4 +516,96 @@
         }
         return true;
     }
+
+    // TODO: Make this private
+    void updateShortcutIME() {
+        if (DEBUG) {
+            Log.d(TAG, "Update shortcut IME from : "
+                    + (mShortcutInputMethodInfo == null
+                            ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
+                    + (mShortcutSubtype == null ? "<null>" : (
+                            mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
+        }
+        // TODO: Update an icon for shortcut IME
+        final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
+                getInputMethodManager().getShortcutInputMethodsAndSubtypes();
+        mShortcutInputMethodInfo = null;
+        mShortcutSubtype = null;
+        for (final InputMethodInfo imi : shortcuts.keySet()) {
+            final List<InputMethodSubtype> subtypes = shortcuts.get(imi);
+            // TODO: Returns the first found IMI for now. Should handle all shortcuts as
+            // appropriate.
+            mShortcutInputMethodInfo = imi;
+            // TODO: Pick up the first found subtype for now. Should handle all subtypes
+            // as appropriate.
+            mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null;
+            break;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Update shortcut IME to : "
+                    + (mShortcutInputMethodInfo == null
+                            ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
+                    + (mShortcutSubtype == null ? "<null>" : (
+                            mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
+        }
+    }
+
+    public void switchToShortcutIME(final InputMethodService context) {
+        if (mShortcutInputMethodInfo == null) {
+            return;
+        }
+
+        final String imiId = mShortcutInputMethodInfo.getId();
+        switchToTargetIME(imiId, mShortcutSubtype, context);
+    }
+
+    private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype,
+            final InputMethodService context) {
+        final IBinder token = context.getWindow().getWindow().getAttributes().token;
+        if (token == null) {
+            return;
+        }
+        final InputMethodManager imm = getInputMethodManager();
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                imm.setInputMethodAndSubtype(token, imiId, subtype);
+                return null;
+            }
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+    }
+
+    public boolean isShortcutImeEnabled() {
+        updateShortcutIME();
+        if (mShortcutInputMethodInfo == null) {
+            return false;
+        }
+        if (mShortcutSubtype == null) {
+            return true;
+        }
+        return checkIfSubtypeBelongsToImeAndEnabled(
+                mShortcutInputMethodInfo, mShortcutSubtype);
+    }
+
+    public boolean isShortcutImeReady() {
+        updateShortcutIME();
+        if (mShortcutInputMethodInfo == null) {
+            return false;
+        }
+        if (mShortcutSubtype == null) {
+            return true;
+        }
+        if (mShortcutSubtype.containsExtraValueKey(REQ_NETWORK_CONNECTIVITY)) {
+            return mIsNetworkConnected;
+        }
+        return true;
+    }
+
+    public void onNetworkStateChanged(final Intent intent) {
+        final boolean noConnection = intent.getBooleanExtra(
+                ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+        mIsNetworkConnected = !noConnection;
+
+        KeyboardSwitcher.getInstance().onNetworkStateChanged();
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
index 1a6c6ba..03f6d60 100644
--- a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
@@ -23,7 +23,7 @@
 
 import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
 import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
 import java.util.Arrays;
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 306ccf0..23e348b 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -16,37 +16,18 @@
 
 package com.android.inputmethod.latin;
 
-import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY;
-
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Resources;
-import android.inputmethodservice.InputMethodService;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.AsyncTask;
-import android.os.IBinder;
-import android.util.Log;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
 
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper;
-import com.android.inputmethod.latin.define.DebugFlags;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
 import java.util.List;
-import java.util.Locale;
-import java.util.Map;
 
 import javax.annotation.Nonnull;
 
 public final class SubtypeSwitcher {
-    private static boolean DBG = DebugFlags.DEBUG_ENABLED;
-    private static final String TAG = SubtypeSwitcher.class.getSimpleName();
-
     private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
 
     private /* final */ RichInputMethodManager mRichImm;
@@ -54,10 +35,6 @@
 
     private final LanguageOnSpacebarHelper mLanguageOnSpacebarHelper =
             new LanguageOnSpacebarHelper();
-    private InputMethodInfo mShortcutInputMethodInfo;
-    private InputMethodSubtype mShortcutSubtype;
-    private RichInputMethodSubtype mCurrentRichInputMethodSubtype;
-    private boolean mIsNetworkConnected;
 
     public static SubtypeSwitcher getInstance() {
         return sInstance;
@@ -79,11 +56,6 @@
         }
         mResources = context.getResources();
         mRichImm = RichInputMethodManager.getInstance();
-        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(
-                Context.CONNECTIVITY_SERVICE);
-
-        final NetworkInfo info = connectivityManager.getActiveNetworkInfo();
-        mIsNetworkConnected = (info != null && info.isConnected());
 
         onSubtypeChanged(mRichImm.getCurrentRawSubtype());
         updateParametersOnStartInputView();
@@ -96,161 +68,21 @@
     public void updateParametersOnStartInputView() {
         final List<InputMethodSubtype> enabledSubtypesOfThisIme =
                 mRichImm.getMyEnabledInputMethodSubtypeList(true);
-        mLanguageOnSpacebarHelper.updateEnabledSubtypes(enabledSubtypesOfThisIme);
-        updateShortcutIME();
-    }
-
-    private void updateShortcutIME() {
-        if (DBG) {
-            Log.d(TAG, "Update shortcut IME from : "
-                    + (mShortcutInputMethodInfo == null
-                            ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
-                    + (mShortcutSubtype == null ? "<null>" : (
-                            mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
-        }
-        // TODO: Update an icon for shortcut IME
-        final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
-                mRichImm.getInputMethodManager().getShortcutInputMethodsAndSubtypes();
-        mShortcutInputMethodInfo = null;
-        mShortcutSubtype = null;
-        for (final InputMethodInfo imi : shortcuts.keySet()) {
-            final List<InputMethodSubtype> subtypes = shortcuts.get(imi);
-            // TODO: Returns the first found IMI for now. Should handle all shortcuts as
-            // appropriate.
-            mShortcutInputMethodInfo = imi;
-            // TODO: Pick up the first found subtype for now. Should handle all subtypes
-            // as appropriate.
-            mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null;
-            break;
-        }
-        if (DBG) {
-            Log.d(TAG, "Update shortcut IME to : "
-                    + (mShortcutInputMethodInfo == null
-                            ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
-                    + (mShortcutSubtype == null ? "<null>" : (
-                            mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
-        }
+        mLanguageOnSpacebarHelper.onUpdateEnabledSubtypes(enabledSubtypesOfThisIme);
+        mRichImm.updateShortcutIME();
     }
 
     // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
     public void onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) {
-        final RichInputMethodSubtype richSubtype =
-                mRichImm.createCurrentRichInputMethodSubtype(newSubtype);
-        if (DBG) {
-            Log.w(TAG, "onSubtypeChanged: " + richSubtype.getNameForLogging());
-        }
-        mCurrentRichInputMethodSubtype = richSubtype;
-        final Locale[] newLocales = richSubtype.getLocales();
-        if (newLocales.length > 1) {
-            // In multi-locales mode, the system language is never the same as the input language
-            // because there is no single input language.
-            mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false);
-        } else {
-            final Locale newLocale = newLocales[0];
-            final Locale systemLocale = mResources.getConfiguration().locale;
-            final boolean sameLocale = systemLocale.equals(newLocale);
-            final boolean sameLanguage = systemLocale.getLanguage().equals(newLocale.getLanguage());
-            final boolean implicitlyEnabled = mRichImm
-                    .checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype);
-            mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(
-                    sameLocale || (sameLanguage && implicitlyEnabled));
-        }
-        updateShortcutIME();
+        final RichInputMethodSubtype richSubtype = mRichImm.onSubtypeChanged(newSubtype);
+        final boolean implicitlyEnabledSubtype = mRichImm
+                .checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype);
+        mLanguageOnSpacebarHelper.onSubtypeChanged(
+                richSubtype, implicitlyEnabledSubtype, mResources.getConfiguration().locale);
+        mRichImm.updateShortcutIME();
     }
 
-    ////////////////////////////
-    // Shortcut IME functions //
-    ////////////////////////////
-
-    public void switchToShortcutIME(final InputMethodService context) {
-        if (mShortcutInputMethodInfo == null) {
-            return;
-        }
-
-        final String imiId = mShortcutInputMethodInfo.getId();
-        switchToTargetIME(imiId, mShortcutSubtype, context);
-    }
-
-    private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype,
-            final InputMethodService context) {
-        final IBinder token = context.getWindow().getWindow().getAttributes().token;
-        if (token == null) {
-            return;
-        }
-        final InputMethodManager imm = mRichImm.getInputMethodManager();
-        new AsyncTask<Void, Void, Void>() {
-            @Override
-            protected Void doInBackground(Void... params) {
-                imm.setInputMethodAndSubtype(token, imiId, subtype);
-                return null;
-            }
-        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-    }
-
-    public boolean isShortcutImeEnabled() {
-        updateShortcutIME();
-        if (mShortcutInputMethodInfo == null) {
-            return false;
-        }
-        if (mShortcutSubtype == null) {
-            return true;
-        }
-        return mRichImm.checkIfSubtypeBelongsToImeAndEnabled(
-                mShortcutInputMethodInfo, mShortcutSubtype);
-    }
-
-    public boolean isShortcutImeReady() {
-        updateShortcutIME();
-        if (mShortcutInputMethodInfo == null) {
-            return false;
-        }
-        if (mShortcutSubtype == null) {
-            return true;
-        }
-        if (mShortcutSubtype.containsExtraValueKey(REQ_NETWORK_CONNECTIVITY)) {
-            return mIsNetworkConnected;
-        }
-        return true;
-    }
-
-    public void onNetworkStateChanged(final Intent intent) {
-        final boolean noConnection = intent.getBooleanExtra(
-                ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
-        mIsNetworkConnected = !noConnection;
-
-        KeyboardSwitcher.getInstance().onNetworkStateChanged();
-    }
-
-    //////////////////////////////////
-    // Subtype Switching functions //
-    //////////////////////////////////
-
     public int getLanguageOnSpacebarFormatType(final RichInputMethodSubtype subtype) {
         return mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(subtype);
     }
-
-    private static RichInputMethodSubtype sForcedSubtypeForTesting = null;
-
-    @UsedForTesting
-    static void forceSubtype(final InputMethodSubtype subtype) {
-        sForcedSubtypeForTesting = new RichInputMethodSubtype(subtype);
-    }
-
-    public Locale[] getCurrentSubtypeLocales() {
-        if (null != sForcedSubtypeForTesting) {
-            return sForcedSubtypeForTesting.getLocales();
-        }
-        return getCurrentSubtype().getLocales();
-    }
-
-    public RichInputMethodSubtype getCurrentSubtype() {
-        if (null != sForcedSubtypeForTesting) {
-            return sForcedSubtypeForTesting;
-        }
-        return mCurrentRichInputMethodSubtype;
-    }
-
-    public String getCombiningRulesExtraValueOfCurrentSubtype() {
-        return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype().getRawSubtype());
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 9b4619d..ee8d3f8 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -230,7 +230,7 @@
             inputStyle = inputStyleIfNotPrediction;
         }
         callback.onGetSuggestedWords(new SuggestedWords(suggestionsList,
-                suggestionResults.mRawSuggestions,
+                suggestionResults.mRawSuggestions, typedWord,
                 // TODO: this first argument is lying. If this is a whitelisted word which is an
                 // actual word, it says typedWordValid = false, which looks wrong. We should either
                 // rename the attribute or change the value.
@@ -286,8 +286,12 @@
         // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false).
         // Note that because this method is never used to get predictions, there is no need to
         // modify inputType such in getSuggestedWordsForNonBatchInput.
+        final String pseudoTypedWord = suggestionsContainer.isEmpty() ? null
+                : suggestionsContainer.get(0).mWord;
+
         callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer,
                 suggestionResults.mRawSuggestions,
+                pseudoTypedWord,
                 true /* typedWordValid */,
                 false /* willAutoCorrect */,
                 false /* isObsoleteSuggestions */,
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index c51e20f..bddeac4 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -73,21 +73,11 @@
             final boolean willAutoCorrect,
             final boolean isObsoleteSuggestions,
             final int inputStyle) {
-        this(suggestedWordInfoList, rawSuggestions, typedWordValid, willAutoCorrect,
-                isObsoleteSuggestions, inputStyle, NOT_A_SEQUENCE_NUMBER);
-    }
-
-    public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
-            final ArrayList<SuggestedWordInfo> rawSuggestions,
-            final boolean typedWordValid,
-            final boolean willAutoCorrect,
-            final boolean isObsoleteSuggestions,
-            final int inputStyle,
-            final int sequenceNumber) {
         this(suggestedWordInfoList, rawSuggestions,
                 (suggestedWordInfoList.isEmpty() || isPrediction(inputStyle)) ? null
                         : suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord,
-                typedWordValid, willAutoCorrect, isObsoleteSuggestions, inputStyle, sequenceNumber);
+                typedWordValid, willAutoCorrect,
+                isObsoleteSuggestions, inputStyle, NOT_A_SEQUENCE_NUMBER);
     }
 
     public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index fa55319..78860d8 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -21,10 +21,10 @@
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.common.ComposedData;
 import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 import com.android.inputmethod.latin.common.InputPointers;
 import com.android.inputmethod.latin.common.StringUtils;
 import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
 
 import java.util.ArrayList;
 import java.util.Collections;
diff --git a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
index d4be0e3..78bfd2b 100644
--- a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
+++ b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
@@ -26,10 +26,10 @@
 import com.android.inputmethod.latin.BinaryDictionaryFileDumper;
 import com.android.inputmethod.latin.BinaryDictionaryGetter;
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
 import com.android.inputmethod.latin.utils.DialogUtils;
 import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
-import com.android.inputmethod.latin.utils.LocaleUtils;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
index 331f85e..ac2fc07 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
@@ -19,7 +19,7 @@
 import android.content.Context;
 import android.util.Log;
 
-import com.android.inputmethod.latin.utils.FileUtils;
+import com.android.inputmethod.latin.common.FileUtils;
 
 import java.io.File;
 import java.io.FilenameFilter;
diff --git a/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java
index 49db2bd..c0ceb88 100644
--- a/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java
@@ -24,7 +24,7 @@
 
 import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.RichInputMethodManager;
 
 /**
  * "Preferences" settings sub screen.
@@ -49,7 +49,7 @@
         // When we are called from the Settings application but we are not already running, some
         // singleton and utility classes may not have been initialized.  We have to call
         // initialization method of these classes here. See {@link LatinIME#onCreate()}.
-        SubtypeSwitcher.init(context);
+        RichInputMethodManager.init(context);
 
         final boolean showVoiceKeyOption = res.getBoolean(
                 R.bool.config_enable_show_voice_key_option);
@@ -71,7 +71,7 @@
         super.onResume();
         final Preference voiceInputKeyOption = findPreference(Settings.PREF_VOICE_INPUT_KEY);
         if (voiceInputKeyOption != null) {
-            final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance()
+            final boolean isShortcutImeEnabled = RichInputMethodManager.getInstance()
                     .isShortcutImeEnabled();
             voiceInputKeyOption.setEnabled(isShortcutImeEnabled);
             voiceInputKeyOption.setSummary(
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 509b41f..26415e7 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -28,7 +28,6 @@
 import com.android.inputmethod.latin.InputAttributes;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.RichInputMethodManager;
-import com.android.inputmethod.latin.SubtypeSwitcher;
 import com.android.inputmethod.latin.utils.AsyncResultHolder;
 import com.android.inputmethod.latin.utils.ResourceUtils;
 import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
@@ -140,7 +139,7 @@
                 DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, true);
         mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res)
                 && mInputAttributes.mShouldShowVoiceInputKey
-                && SubtypeSwitcher.getInstance().isShortcutImeEnabled();
+                && RichInputMethodManager.getInstance().isShortcutImeEnabled();
         final String autoCorrectionThresholdRawValue = prefs.getString(
                 Settings.PREF_AUTO_CORRECTION_THRESHOLD,
                 res.getString(R.string.auto_correction_threshold_mode_index_modest));
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 3ad8fb9..c90e8a3 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -34,10 +34,10 @@
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.WordComposer;
 import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.CoordinateUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.common.StringUtils;
 import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
-import com.android.inputmethod.latin.utils.LocaleUtils;
 import com.android.inputmethod.latin.utils.ScriptUtils;
 import com.android.inputmethod.latin.utils.SuggestionResults;
 
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
index eda8194..22fc35a 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
@@ -28,7 +28,7 @@
 
 import com.android.inputmethod.compat.UserDictionaryCompatUtils;
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
 
 import java.util.ArrayList;
 import java.util.Locale;
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
index 90e4faa..b9ed353 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
@@ -31,7 +31,7 @@
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
 
 import java.util.List;
 import java.util.Locale;
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java
index e58727e..c0a946e 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java
@@ -17,7 +17,7 @@
 package com.android.inputmethod.latin.userdictionary;
 
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
 
 import android.content.Context;
 import android.text.TextUtils;
diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
index 24025b2..81c3e3c 100644
--- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
@@ -28,6 +28,7 @@
 import com.android.inputmethod.latin.BinaryDictionaryGetter;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
diff --git a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
deleted file mode 100644
index 4bab497..0000000
--- a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.text.TextUtils;
-
-import java.util.HashMap;
-import java.util.Locale;
-
-import javax.annotation.Nullable;
-
-/**
- * A class to help with handling Locales in string form.
- *
- * This file has the same meaning and features (and shares all of its code) with
- * the one in the dictionary pack. They need to be kept synchronized; for any
- * update/bugfix to this file, consider also updating/fixing the version in the
- * dictionary pack.
- */
-public final class LocaleUtils {
-    private LocaleUtils() {
-        // Intentional empty constructor for utility class.
-    }
-
-    // Locale match level constants.
-    // A higher level of match is guaranteed to have a higher numerical value.
-    // Some room is left within constants to add match cases that may arise necessary
-    // in the future, for example differentiating between the case where the countries
-    // are both present and different, and the case where one of the locales does not
-    // specify the countries. This difference is not needed now.
-
-    // Nothing matches.
-    public static final int LOCALE_NO_MATCH = 0;
-    // The languages matches, but the country are different. Or, the reference locale requires a
-    // country and the tested locale does not have one.
-    public static final int LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER = 3;
-    // The languages and country match, but the variants are different. Or, the reference locale
-    // requires a variant and the tested locale does not have one.
-    public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER = 6;
-    // The required locale is null or empty so it will accept anything, and the tested locale
-    // is non-null and non-empty.
-    public static final int LOCALE_ANY_MATCH = 10;
-    // The language matches, and the tested locale specifies a country but the reference locale
-    // does not require one.
-    public static final int LOCALE_LANGUAGE_MATCH = 15;
-    // The language and the country match, and the tested locale specifies a variant but the
-    // reference locale does not require one.
-    public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH = 20;
-    // The compared locales are fully identical. This is the best match level.
-    public static final int LOCALE_FULL_MATCH = 30;
-
-    // The level at which a match is "normally" considered a locale match with standard algorithms.
-    // Don't use this directly, use #isMatch to test.
-    private static final int LOCALE_MATCH = LOCALE_ANY_MATCH;
-
-    // Make this match the maximum match level. If this evolves to have more than 2 digits
-    // when written in base 10, also adjust the getMatchLevelSortedString method.
-    private static final int MATCH_LEVEL_MAX = 30;
-
-    /**
-     * Return how well a tested locale matches a reference locale.
-     *
-     * This will check the tested locale against the reference locale and return a measure of how
-     * a well it matches the reference. The general idea is that the tested locale has to match
-     * every specified part of the required locale. A full match occur when they are equal, a
-     * partial match when the tested locale agrees with the reference locale but is more specific,
-     * and a difference when the tested locale does not comply with all requirements from the
-     * reference locale.
-     * In more detail, if the reference locale specifies at least a language and the testedLocale
-     * does not specify one, or specifies a different one, LOCALE_NO_MATCH is returned. If the
-     * reference locale is empty or null, it will match anything - in the form of LOCALE_FULL_MATCH
-     * if the tested locale is empty or null, and LOCALE_ANY_MATCH otherwise. If the reference and
-     * tested locale agree on the language, but not on the country,
-     * LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER is returned if the reference locale specifies a country,
-     * and LOCALE_LANGUAGE_MATCH otherwise.
-     * If they agree on both the language and the country, but not on the variant,
-     * LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER is returned if the reference locale
-     * specifies a variant, and LOCALE_LANGUAGE_AND_COUNTRY_MATCH otherwise. If everything matches,
-     * LOCALE_FULL_MATCH is returned.
-     * Examples:
-     * en <=> en_US  => LOCALE_LANGUAGE_MATCH
-     * en_US <=> en => LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER
-     * en_US_POSIX <=> en_US_Android  =>  LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER
-     * en_US <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH
-     * sp_US <=> en_US  =>  LOCALE_NO_MATCH
-     * de <=> de  => LOCALE_FULL_MATCH
-     * en_US <=> en_US => LOCALE_FULL_MATCH
-     * "" <=> en_US => LOCALE_ANY_MATCH
-     *
-     * @param referenceLocale the reference locale to test against.
-     * @param testedLocale the locale to test.
-     * @return a constant that measures how well the tested locale matches the reference locale.
-     */
-    public static int getMatchLevel(String referenceLocale, String testedLocale) {
-        if (TextUtils.isEmpty(referenceLocale)) {
-            return TextUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH;
-        }
-        if (null == testedLocale) return LOCALE_NO_MATCH;
-        String[] referenceParams = referenceLocale.split("_", 3);
-        String[] testedParams = testedLocale.split("_", 3);
-        // By spec of String#split, [0] cannot be null and length cannot be 0.
-        if (!referenceParams[0].equals(testedParams[0])) return LOCALE_NO_MATCH;
-        switch (referenceParams.length) {
-        case 1:
-            return 1 == testedParams.length ? LOCALE_FULL_MATCH : LOCALE_LANGUAGE_MATCH;
-        case 2:
-            if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
-            if (!referenceParams[1].equals(testedParams[1]))
-                return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
-            if (3 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH;
-            return LOCALE_FULL_MATCH;
-        case 3:
-            if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
-            if (!referenceParams[1].equals(testedParams[1]))
-                return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
-            if (2 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER;
-            if (!referenceParams[2].equals(testedParams[2]))
-                return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER;
-            return LOCALE_FULL_MATCH;
-        }
-        // It should be impossible to come here
-        return LOCALE_NO_MATCH;
-    }
-
-    /**
-     * Return a string that represents this match level, with better matches first.
-     *
-     * The strings are sorted in lexicographic order: a better match will always be less than
-     * a worse match when compared together.
-     */
-    public static String getMatchLevelSortedString(int matchLevel) {
-        // This works because the match levels are 0~99 (actually 0~30)
-        // Ideally this should use a number of digits equals to the 1og10 of the greater matchLevel
-        return String.format(Locale.ROOT, "%02d", MATCH_LEVEL_MAX - matchLevel);
-    }
-
-    /**
-     * Find out whether a match level should be considered a match.
-     *
-     * This method takes a match level as returned by the #getMatchLevel method, and returns whether
-     * it should be considered a match in the usual sense with standard Locale functions.
-     *
-     * @param level the match level, as returned by getMatchLevel.
-     * @return whether this is a match or not.
-     */
-    public static boolean isMatch(int level) {
-        return LOCALE_MATCH <= level;
-    }
-
-    private static final HashMap<String, Locale> sLocaleCache = new HashMap<>();
-
-    /**
-     * Creates a locale from a string specification.
-     */
-    @Nullable
-    public static Locale constructLocaleFromString(@Nullable final String localeStr) {
-        if (localeStr == null) {
-            // TODO: Should this be Locale.ROOT?
-            return null;
-        }
-        synchronized (sLocaleCache) {
-            Locale retval = sLocaleCache.get(localeStr);
-            if (retval != null) {
-                return retval;
-            }
-            final String[] localeParams = localeStr.split("_", 3);
-            if (localeParams.length == 1) {
-                retval = new Locale(localeParams[0]);
-            } else if (localeParams.length == 2) {
-                retval = new Locale(localeParams[0], localeParams[1]);
-            } else if (localeParams.length == 3) {
-                retval = new Locale(localeParams[0], localeParams[1], localeParams[2]);
-            }
-            if (retval != null) {
-                sLocaleCache.put(localeStr, retval);
-            }
-            // TODO: Should return Locale.ROOT instead of null?
-            return retval;
-        }
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
index e4a8ee2..b36168b 100644
--- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
@@ -28,6 +28,7 @@
 
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.RichInputMethodSubtype;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.common.StringUtils;
 
 import java.util.Arrays;
diff --git a/native/dicttoolkit/NativeFileList.mk b/native/dicttoolkit/NativeFileList.mk
index b6be9c5..925ec45 100644
--- a/native/dicttoolkit/NativeFileList.mk
+++ b/native/dicttoolkit/NativeFileList.mk
@@ -15,7 +15,15 @@
 LATIN_IME_DICT_TOOLKIT_MAIN_SRC_FILES := \
     dict_toolkit_main.cpp
 
-LATIN_IME_DICT_TOOLKIT_SRC_FILES :=
+LATIN_IME_DICT_TOOLKIT_SRC_FILES := \
+    $(addprefix command_executors/, \
+        diff_executor.cpp \
+        header_executor.cpp \
+        help_executor.cpp \
+        info_executor.cpp \
+        makedict_executor.cpp) \
+    utils/command_utils.cpp
 
 LATIN_IME_DICT_TOOLKIT_TEST_FILES := \
-    dict_toolkit_defines_test.cpp
+    dict_toolkit_defines_test.cpp \
+    utils/command_utils_test.cpp
diff --git a/native/dicttoolkit/UnitTests.mk b/native/dicttoolkit/UnitTests.mk
index d568db4..96e2873 100644
--- a/native/dicttoolkit/UnitTests.mk
+++ b/native/dicttoolkit/UnitTests.mk
@@ -46,6 +46,7 @@
 include $(CLEAR_VARS)
 
 # TODO: Remove -std=c++11 once it is set by default on host build.
+LOCAL_ADDRESS_SANITIZER := true
 LOCAL_CFLAGS += -std=c++11 -Wno-unused-parameter -Wno-unused-function
 LOCAL_CLANG := true
 LOCAL_CXX_STL := libc++
diff --git a/native/dicttoolkit/dict_toolkit_main.cpp b/native/dicttoolkit/dict_toolkit_main.cpp
index d71b50e..53cc5e9 100644
--- a/native/dicttoolkit/dict_toolkit_main.cpp
+++ b/native/dicttoolkit/dict_toolkit_main.cpp
@@ -16,8 +16,24 @@
 
 #include <cstdio>
 
+#include "dict_toolkit_defines.h"
+#include "utils/command_utils.h"
+
+void usage(int argc, char **argv) {
+    fprintf(stderr, "Usage: %s <command> [arguments]\n", argc > 0 ? argv[0] : "dicttoolkit");
+}
+
 int main(int argc, char **argv) {
-    // TODO: Implement.
-    printf("%s\n", argv[0]);
-    return 0;
+    if (argc < MIN_ARG_COUNT) {
+        usage(argc, argv);
+        return 1;
+    }
+    using namespace latinime::dicttoolkit;
+    const CommandType commandType = CommandUtils::getCommandType(argv[1]);
+    if (commandType == CommandType::Unknown) {
+        CommandUtils::printCommandUnknownMessage(argv[0], argv[1]);
+        return 1;
+    }
+    const auto executor = CommandUtils::getCommandExecutor(commandType);
+    return executor(argc - 1, argv + 1);
 }
diff --git a/native/dicttoolkit/src/command_executors/diff_executor.cpp b/native/dicttoolkit/src/command_executors/diff_executor.cpp
new file mode 100644
index 0000000..077a400
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/diff_executor.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "command_executors/diff_executor.h"
+
+#include <cstdio>
+
+namespace latinime {
+namespace dicttoolkit {
+
+const char *const DiffExecutor::COMMAND_NAME = "diff";
+
+/* static */ int DiffExecutor::run(const int argc, char **argv) {
+    fprintf(stderr, "Command '%s' has not been implemented yet.\n", COMMAND_NAME);
+    return 0;
+}
+
+/* static */ void DiffExecutor::printUsage() {
+    printf("*** %s\n", COMMAND_NAME);
+    printf("Usage: %s\n", COMMAND_NAME);
+    printf("Shows differences between two dictionaries.\n\n");
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/command_executors/diff_executor.h b/native/dicttoolkit/src/command_executors/diff_executor.h
new file mode 100644
index 0000000..fc8dc0d
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/diff_executor.h
@@ -0,0 +1,38 @@
+/*
+ * 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_DICT_TOOLKIT_DIFF_EXECUTOR_H
+#define LATINIME_DICT_TOOLKIT_DIFF_EXECUTOR_H
+
+#include "dict_toolkit_defines.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class DiffExecutor final {
+ public:
+    static const char *const COMMAND_NAME;
+
+    static int run(const int argc, char **argv);
+    static void printUsage();
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DiffExecutor);
+};
+
+} // namespace dicttoolkit
+} // namepsace latinime
+#endif // LATINIME_DICT_TOOLKIT_DIFF_EXECUTOR_H
diff --git a/native/dicttoolkit/src/command_executors/header_executor.cpp b/native/dicttoolkit/src/command_executors/header_executor.cpp
new file mode 100644
index 0000000..068a62c
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/header_executor.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "command_executors/header_executor.h"
+
+#include <cstdio>
+
+namespace latinime {
+namespace dicttoolkit {
+
+const char *const HeaderExecutor::COMMAND_NAME = "header";
+
+/* static */ int HeaderExecutor::run(const int argc, char **argv) {
+    fprintf(stderr, "Command '%s' has not been implemented yet.\n", COMMAND_NAME);
+    return 0;
+}
+
+/* static */ void HeaderExecutor::printUsage() {
+    printf("*** %s\n", COMMAND_NAME);
+    printf("Usage: %s\n", COMMAND_NAME);
+    printf("Prints the header contents of a dictionary file.\n\n");
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/command_executors/header_executor.h b/native/dicttoolkit/src/command_executors/header_executor.h
new file mode 100644
index 0000000..4cdeb1a
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/header_executor.h
@@ -0,0 +1,38 @@
+/*
+ * 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_DICT_TOOLKIT_HEADER_EXECUTOR_H
+#define LATINIME_DICT_TOOLKIT_HEADER_EXECUTOR_H
+
+#include "dict_toolkit_defines.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class HeaderExecutor final {
+ public:
+    static const char *const COMMAND_NAME;
+
+    static int run(const int argc, char **argv);
+    static void printUsage();
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderExecutor);
+};
+
+} // namespace dicttoolkit
+} // namepsace latinime
+#endif // LATINIME_DICT_TOOLKIT_HEADER_EXECUTOR_H
diff --git a/native/dicttoolkit/src/command_executors/help_executor.cpp b/native/dicttoolkit/src/command_executors/help_executor.cpp
new file mode 100644
index 0000000..bd29a5b
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/help_executor.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 "command_executors/help_executor.h"
+
+#include <cstdio>
+#include <functional>
+#include <vector>
+
+#include "command_executors/diff_executor.h"
+#include "command_executors/header_executor.h"
+#include "command_executors/info_executor.h"
+#include "command_executors/makedict_executor.h"
+#include "utils/command_utils.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+const char *const HelpExecutor::COMMAND_NAME = "help";
+
+/* static */ int HelpExecutor::run(const int argc, char **argv) {
+    printf("Available commands:\n\n");
+    const std::vector<std::function<void(void)>> printUsageMethods = {DiffExecutor::printUsage,
+            HeaderExecutor::printUsage, InfoExecutor::printUsage, MakedictExecutor::printUsage,
+            printUsage};
+    for (const auto &printUsageMethod : printUsageMethods) {
+        printUsageMethod();
+    }
+    return 0;
+}
+
+/* static */ void HelpExecutor::printUsage() {
+    printf("*** %s\n", COMMAND_NAME);
+    printf("Usage: %s\n", COMMAND_NAME);
+    printf("Show this help list.\n\n");
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/command_executors/help_executor.h b/native/dicttoolkit/src/command_executors/help_executor.h
new file mode 100644
index 0000000..280610e
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/help_executor.h
@@ -0,0 +1,38 @@
+/*
+ * 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_DICT_TOOLKIT_HELP_EXECUTOR_H
+#define LATINIME_DICT_TOOLKIT_HELP_EXECUTOR_H
+
+#include "dict_toolkit_defines.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class HelpExecutor final {
+ public:
+    static const char *const COMMAND_NAME;
+
+    static int run(const int argc, char **argv);
+    static void printUsage();
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(HelpExecutor);
+};
+
+} // namespace dicttoolkit
+} // namepsace latinime
+#endif // LATINIME_DICT_TOOLKIT_HELP_EXECUTOR_H
diff --git a/native/dicttoolkit/src/command_executors/info_executor.cpp b/native/dicttoolkit/src/command_executors/info_executor.cpp
new file mode 100644
index 0000000..c4d84ca
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/info_executor.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "command_executors/info_executor.h"
+
+#include <cstdio>
+
+namespace latinime {
+namespace dicttoolkit {
+
+const char *const InfoExecutor::COMMAND_NAME = "info";
+
+/* static */ int InfoExecutor::run(const int argc, char **argv) {
+    fprintf(stderr, "Command '%s' has not been implemented yet.\n", COMMAND_NAME);
+    return 0;
+}
+
+/* static */ void InfoExecutor::printUsage() {
+    printf("*** %s\n", COMMAND_NAME);
+    printf("Usage: %s\n", COMMAND_NAME);
+    printf("Prints various information about a dictionary file.\n\n");
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/command_executors/info_executor.h b/native/dicttoolkit/src/command_executors/info_executor.h
new file mode 100644
index 0000000..4ffa74f
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/info_executor.h
@@ -0,0 +1,38 @@
+/*
+ * 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_DICT_TOOLKIT_INFO_EXECUTOR_H
+#define LATINIME_DICT_TOOLKIT_INFO_EXECUTOR_H
+
+#include "dict_toolkit_defines.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class InfoExecutor final {
+ public:
+    static const char *const COMMAND_NAME;
+
+    static int run(const int argc, char **argv);
+    static void printUsage();
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(InfoExecutor);
+};
+
+} // namepsace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_INFO_EXECUTOR_H
diff --git a/native/dicttoolkit/src/command_executors/makedict_executor.cpp b/native/dicttoolkit/src/command_executors/makedict_executor.cpp
new file mode 100644
index 0000000..ea62e3c
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/makedict_executor.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "command_executors/makedict_executor.h"
+
+#include <cstdio>
+
+namespace latinime {
+namespace dicttoolkit {
+
+const char *const MakedictExecutor::COMMAND_NAME = "makedict";
+
+/* static */ int MakedictExecutor::run(const int argc, char **argv) {
+    fprintf(stderr, "Command '%s' has not been implemented yet.\n", COMMAND_NAME);
+    return 0;
+}
+
+/* static */ void MakedictExecutor::printUsage() {
+    printf("*** %s\n", COMMAND_NAME);
+    printf("Usage: %s\n", COMMAND_NAME);
+    printf("Converts a source dictionary file to one or several outputs.\n"
+            "Source can be a binary dictionary file or a combined format file.\n"
+            "Binary version 2 (Jelly Bean), 4, and combined format outputs are supported.\n\n");
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/command_executors/makedict_executor.h b/native/dicttoolkit/src/command_executors/makedict_executor.h
new file mode 100644
index 0000000..ae1309f
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/makedict_executor.h
@@ -0,0 +1,38 @@
+/*
+ * 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_DICT_TOOLKIT_MAKEDICT_EXECUTOR_H
+#define LATINIME_DICT_TOOLKIT_MAKEDICT_EXECUTOR_H
+
+#include "dict_toolkit_defines.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class MakedictExecutor final {
+ public:
+    static const char *const COMMAND_NAME;
+
+    static int run(const int argc, char **argv);
+    static void printUsage();
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(MakedictExecutor);
+};
+
+} // namespace dicttoolkit
+} // namepsace latinime
+#endif // LATINIME_DICT_TOOLKIT_MAKEDICT_EXECUTOR_H
diff --git a/native/dicttoolkit/src/dict_toolkit_defines.h b/native/dicttoolkit/src/dict_toolkit_defines.h
index 2a2104e..dbaae0c 100644
--- a/native/dicttoolkit/src/dict_toolkit_defines.h
+++ b/native/dicttoolkit/src/dict_toolkit_defines.h
@@ -19,4 +19,6 @@
 
 #include "defines.h"
 
+#define MIN_ARG_COUNT 2
+
 #endif // LATINIME_DICT_TOOLKIT_DEFINES_H
diff --git a/native/dicttoolkit/src/utils/command_utils.cpp b/native/dicttoolkit/src/utils/command_utils.cpp
new file mode 100644
index 0000000..3419642
--- /dev/null
+++ b/native/dicttoolkit/src/utils/command_utils.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 "utils/command_utils.h"
+
+#include <cstdio>
+
+#include "command_executors/diff_executor.h"
+#include "command_executors/header_executor.h"
+#include "command_executors/help_executor.h"
+#include "command_executors/info_executor.h"
+#include "command_executors/makedict_executor.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+/* static */ CommandType CommandUtils::getCommandType(const std::string &commandName) {
+    if (commandName == InfoExecutor::COMMAND_NAME) {
+        return CommandType::Info;
+    } else if (commandName == DiffExecutor::COMMAND_NAME) {
+        return CommandType::Diff;
+    } else if (commandName == MakedictExecutor::COMMAND_NAME) {
+        return CommandType::Makedict;
+    } else if (commandName == HeaderExecutor::COMMAND_NAME) {
+        return CommandType::Header;
+    } else if (commandName == HelpExecutor::COMMAND_NAME) {
+        return CommandType::Help;
+    } else {
+        return CommandType::Unknown;
+    }
+}
+
+/* static */ void CommandUtils::printCommandUnknownMessage(const std::string &programName,
+        const std::string &commandName) {
+    fprintf(stderr, "Command '%s' is unknown. Try '%s %s' for more information.\n",
+            commandName.c_str(), programName.c_str(), HelpExecutor::COMMAND_NAME);
+}
+
+/* static */ std::function<int(int, char **)> CommandUtils::getCommandExecutor(
+        const CommandType commandType) {
+    switch (commandType) {
+        case CommandType::Info:
+            return InfoExecutor::run;
+        case CommandType::Diff:
+            return DiffExecutor::run;
+        case CommandType::Makedict:
+            return MakedictExecutor::run;
+        case CommandType::Header:
+            return HeaderExecutor::run;
+        case CommandType::Help:
+            return HelpExecutor::run;
+        default:
+            return [] (int, char **) -> int {
+                printf("Command executor not found.");
+                return 1;
+            };
+    }
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/utils/command_utils.h b/native/dicttoolkit/src/utils/command_utils.h
new file mode 100644
index 0000000..4a181f1
--- /dev/null
+++ b/native/dicttoolkit/src/utils/command_utils.h
@@ -0,0 +1,50 @@
+/*
+ * 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_DICT_TOOLKIT_COMMAND_UTILS_H
+#define LATINIME_DICT_TOOLKIT_COMMAND_UTILS_H
+
+#include <functional>
+#include <memory>
+#include <string>
+
+#include "dict_toolkit_defines.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+enum class CommandType : int {
+    Info,
+    Diff,
+    Makedict,
+    Header,
+    Help,
+    Unknown
+};
+
+class CommandUtils {
+public:
+    static CommandType getCommandType(const std::string &commandName);
+    static void printCommandUnknownMessage(const std::string &programName,
+            const std::string &commandName);
+    static std::function<int(int, char **)> getCommandExecutor(const CommandType commandType);
+
+private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(CommandUtils);
+};
+} // namespace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_COMMAND_UTILS_H
diff --git a/native/dicttoolkit/tests/utils/command_utils_test.cpp b/native/dicttoolkit/tests/utils/command_utils_test.cpp
new file mode 100644
index 0000000..9d79c9d
--- /dev/null
+++ b/native/dicttoolkit/tests/utils/command_utils_test.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 "utils/command_utils.h"
+
+#include <gtest/gtest.h>
+
+namespace latinime {
+namespace dicttoolkit {
+namespace {
+
+TEST(CommandUtilsTests, TestGetCommandType) {
+    EXPECT_EQ(CommandUtils::getCommandType(""), CommandType::Unknown);
+    EXPECT_EQ(CommandUtils::getCommandType("abc"), CommandType::Unknown);
+    EXPECT_EQ(CommandUtils::getCommandType("info"), CommandType::Info);
+    EXPECT_EQ(CommandUtils::getCommandType("diff"), CommandType::Diff);
+    EXPECT_EQ(CommandUtils::getCommandType("makedict"), CommandType::Makedict);
+    EXPECT_EQ(CommandUtils::getCommandType("header"), CommandType::Header);
+    EXPECT_EQ(CommandUtils::getCommandType("help"), CommandType::Help);
+}
+
+} // namespace
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 118f600..9c065e0 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -35,6 +35,7 @@
 #include "utils/int_array_view.h"
 #include "utils/jni_data_utils.h"
 #include "utils/log_utils.h"
+#include "utils/profiler.h"
 #include "utils/time_keeper.h"
 
 namespace latinime {
@@ -43,8 +44,8 @@
 
 static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring sourceDir,
         jlong dictOffset, jlong dictSize, jboolean isUpdatable) {
-    PROF_OPEN;
-    PROF_START(66);
+    PROF_INIT;
+    PROF_TIMER_START(66);
     const jsize sourceDirUtf8Length = env->GetStringUTFLength(sourceDir);
     if (sourceDirUtf8Length <= 0) {
         AKLOGE("DICT: Can't get sourceDir string");
@@ -63,8 +64,7 @@
 
     Dictionary *const dictionary =
             new Dictionary(env, std::move(dictionaryStructureWithBufferPolicy));
-    PROF_END(66);
-    PROF_CLOSE;
+    PROF_TIMER_END(66);
     return reinterpret_cast<jlong>(dictionary);
 }
 
@@ -586,7 +586,7 @@
         }
         if (!dictionaryStructureWithBufferPolicy->addUnigramEntry(
                 CodePointArrayView(wordCodePoints, wordCodePointCount),
-                wordProperty.getUnigramProperty())) {
+                &wordProperty.getUnigramProperty())) {
             LogUtils::logToJava(env, "Cannot add unigram to the new dict.");
             return false;
         }
@@ -605,7 +605,7 @@
                 return false;
             }
         }
-        for (const NgramProperty &ngramProperty : *wordProperty.getNgramProperties()) {
+        for (const NgramProperty &ngramProperty : wordProperty.getNgramProperties()) {
             if (!dictionaryStructureWithBufferPolicy->addNgramEntry(&ngramProperty)) {
                 LogUtils::logToJava(env, "Cannot add ngram to the new dict.");
                 return false;
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 8851185..0e67b4d 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -23,10 +23,10 @@
 #define AK_FORCE_INLINE inline
 #endif // __GNUC__
 
-#if defined(FLAG_DO_PROFILE) || defined(FLAG_DBG)
+#if defined(FLAG_DBG)
 #undef AK_FORCE_INLINE
 #define AK_FORCE_INLINE inline
-#endif // defined(FLAG_DO_PROFILE) || defined(FLAG_DBG)
+#endif // defined(FLAG_DBG)
 
 // Must be equal to Constants.Dictionary.MAX_WORD_LENGTH in Java
 #define MAX_WORD_LENGTH 48
@@ -172,69 +172,6 @@
 #define INTS_TO_CHARS(input, length, output)
 #endif // defined(FLAG_DO_PROFILE) || defined(FLAG_DBG)
 
-#ifdef FLAG_DO_PROFILE
-// Profiler
-#include <time.h>
-
-#define PROF_BUF_SIZE 100
-static float profile_buf[PROF_BUF_SIZE];
-static float profile_old[PROF_BUF_SIZE];
-static unsigned int profile_counter[PROF_BUF_SIZE];
-
-#define PROF_RESET               prof_reset()
-#define PROF_COUNT(prof_buf_id)  ++profile_counter[prof_buf_id]
-#define PROF_OPEN                do { PROF_RESET; PROF_START(PROF_BUF_SIZE - 1); } while (0)
-#define PROF_START(prof_buf_id)  do { \
-        PROF_COUNT(prof_buf_id); profile_old[prof_buf_id] = (clock()); } while (0)
-#define PROF_CLOSE               do { PROF_END(PROF_BUF_SIZE - 1); PROF_OUTALL; } while (0)
-#define PROF_END(prof_buf_id)    profile_buf[prof_buf_id] += ((clock()) - profile_old[prof_buf_id])
-#define PROF_CLOCKOUT(prof_buf_id) \
-        AKLOGI("%s : clock is %f", __FUNCTION__, (clock() - profile_old[prof_buf_id]))
-#define PROF_OUTALL              do { AKLOGI("--- %s ---", __FUNCTION__); prof_out(); } while (0)
-
-static inline void prof_reset(void) {
-    for (int i = 0; i < PROF_BUF_SIZE; ++i) {
-        profile_buf[i] = 0;
-        profile_old[i] = 0;
-        profile_counter[i] = 0;
-    }
-}
-
-static inline void prof_out(void) {
-    if (profile_counter[PROF_BUF_SIZE - 1] != 1) {
-        AKLOGI("Error: You must call PROF_OPEN before PROF_CLOSE.");
-    }
-    AKLOGI("Total time is %6.3f ms.",
-            profile_buf[PROF_BUF_SIZE - 1] * 1000.0f / static_cast<float>(CLOCKS_PER_SEC));
-    float all = 0.0f;
-    for (int i = 0; i < PROF_BUF_SIZE - 1; ++i) {
-        all += profile_buf[i];
-    }
-    if (all < 1.0f) all = 1.0f;
-    for (int i = 0; i < PROF_BUF_SIZE - 1; ++i) {
-        if (profile_buf[i] > 0.0f) {
-            AKLOGI("(%d): Used %4.2f%%, %8.4f ms. Called %d times.",
-                    i, (profile_buf[i] * 100.0f / all),
-                    profile_buf[i] * 1000.0f / static_cast<float>(CLOCKS_PER_SEC),
-                    profile_counter[i]);
-        }
-    }
-}
-
-#else // FLAG_DO_PROFILE
-#define PROF_BUF_SIZE 0
-#define PROF_RESET
-#define PROF_COUNT(prof_buf_id)
-#define PROF_OPEN
-#define PROF_START(prof_buf_id)
-#define PROF_CLOSE
-#define PROF_END(prof_buf_id)
-#define PROF_CLOCK_OUT(prof_buf_id)
-#define PROF_CLOCKOUT(prof_buf_id)
-#define PROF_OUTALL
-
-#endif // FLAG_DO_PROFILE
-
 #ifdef FLAG_DBG
 #define DEBUG_DICT true
 #define DEBUG_DICT_FULL false
diff --git a/native/jni/src/suggest/core/dictionary/property/word_property.h b/native/jni/src/suggest/core/dictionary/property/word_property.h
index b5314fa..d4db3f0 100644
--- a/native/jni/src/suggest/core/dictionary/property/word_property.h
+++ b/native/jni/src/suggest/core/dictionary/property/word_property.h
@@ -23,6 +23,7 @@
 #include "jni.h"
 #include "suggest/core/dictionary/property/ngram_property.h"
 #include "suggest/core/dictionary/property/unigram_property.h"
+#include "utils/int_array_view.h"
 
 namespace latinime {
 
@@ -33,10 +34,10 @@
     WordProperty()
             : mCodePoints(), mUnigramProperty(), mNgrams() {}
 
-    WordProperty(const std::vector<int> &&codePoints, const UnigramProperty *const unigramProperty,
-            const std::vector<NgramProperty> *const ngrams)
-            : mCodePoints(std::move(codePoints)), mUnigramProperty(*unigramProperty),
-              mNgrams(*ngrams) {}
+    WordProperty(const std::vector<int> &&codePoints, const UnigramProperty &unigramProperty,
+            const std::vector<NgramProperty> &ngrams)
+            : mCodePoints(std::move(codePoints)), mUnigramProperty(unigramProperty),
+              mNgrams(ngrams) {}
 
     void outputProperties(JNIEnv *const env, jintArray outCodePoints, jbooleanArray outFlags,
             jintArray outProbabilityInfo, jobject outNgramPrevWordsArray,
@@ -44,12 +45,16 @@
             jobject outNgramProbabilities, jobject outShortcutTargets,
             jobject outShortcutProbabilities) const;
 
-    const UnigramProperty *getUnigramProperty() const {
-        return &mUnigramProperty;
+    const CodePointArrayView getCodePoints() const {
+        return CodePointArrayView(mCodePoints);
     }
 
-    const std::vector<NgramProperty> *getNgramProperties() const {
-        return &mNgrams;
+    const UnigramProperty &getUnigramProperty() const {
+        return mUnigramProperty;
+    }
+
+    const std::vector<NgramProperty> &getNgramProperties() const {
+        return mNgrams;
     }
 
  private:
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index c372d66..e5e9b46 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -29,6 +29,7 @@
 #include "suggest/core/result/suggestions_output_utils.h"
 #include "suggest/core/session/dic_traverse_session.h"
 #include "suggest/core/suggest_options.h"
+#include "utils/profiler.h"
 
 namespace latinime {
 
@@ -48,8 +49,8 @@
         int *inputXs, int *inputYs, int *times, int *pointerIds, int *inputCodePoints,
         int inputSize, const float weightOfLangModelVsSpatialModel,
         SuggestionResults *const outSuggestionResults) const {
-    PROF_OPEN;
-    PROF_START(0);
+    PROF_INIT;
+    PROF_TIMER_START(0);
     const float maxSpatialDistance = TRAVERSAL->getMaxSpatialDistance();
     DicTraverseSession *tSession = static_cast<DicTraverseSession *>(traverseSession);
     tSession->setupForGetSuggestions(pInfo, inputCodePoints, inputSize, inputXs, inputYs, times,
@@ -57,8 +58,8 @@
     // TODO: Add the way to evaluate cache
 
     initializeSearch(tSession);
-    PROF_END(0);
-    PROF_START(1);
+    PROF_TIMER_END(0);
+    PROF_TIMER_START(1);
 
     // keep expanding search dicNodes until all have terminated.
     while (tSession->getDicTraverseCache()->activeSize() > 0) {
@@ -66,12 +67,11 @@
         tSession->getDicTraverseCache()->advanceActiveDicNodes();
         tSession->getDicTraverseCache()->advanceInputIndex(inputSize);
     }
-    PROF_END(1);
-    PROF_START(2);
+    PROF_TIMER_END(1);
+    PROF_TIMER_START(2);
     SuggestionsOutputUtils::outputSuggestions(
             SCORING, tSession, weightOfLangModelVsSpatialModel, outSuggestionResults);
-    PROF_END(2);
-    PROF_CLOSE;
+    PROF_TIMER_END(2);
 }
 
 /**
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 9455222..ca7d93b 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
@@ -614,7 +614,7 @@
     const UnigramProperty unigramProperty(ptNodeParams.representsBeginningOfSentence(),
             ptNodeParams.isNotAWord(), ptNodeParams.isPossiblyOffensive(),
             ptNodeParams.getProbability(), *historicalInfo, std::move(shortcuts));
-    return WordProperty(wordCodePoints.toVector(), &unigramProperty, &ngrams);
+    return WordProperty(wordCodePoints.toVector(), unigramProperty, ngrams);
 }
 
 int Ver4PatriciaTriePolicy::getNextWordAndNextToken(const int token, int *const outCodePoints,
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 5987361..1a51aca 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
@@ -478,7 +478,7 @@
     const UnigramProperty unigramProperty(ptNodeParams.representsBeginningOfSentence(),
             ptNodeParams.isNotAWord(), ptNodeParams.isPossiblyOffensive(),
             ptNodeParams.getProbability(), HistoricalInfo(), std::move(shortcuts));
-    return WordProperty(wordCodePoints.toVector(), &unigramProperty, &ngrams);
+    return WordProperty(wordCodePoints.toVector(), unigramProperty, ngrams);
 }
 
 int PatriciaTriePolicy::getNextWordAndNextToken(const int token, int *const outCodePoints,
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 1992d4a..7449cd0 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
@@ -560,7 +560,7 @@
             wordAttributes.isNotAWord(), wordAttributes.isBlacklisted(),
             wordAttributes.isPossiblyOffensive(), wordAttributes.getProbability(),
             *historicalInfo, std::move(shortcuts));
-    return WordProperty(wordCodePoints.toVector(), &unigramProperty, &ngrams);
+    return WordProperty(wordCodePoints.toVector(), unigramProperty, ngrams);
 }
 
 int Ver4PatriciaTriePolicy::getNextWordAndNextToken(const int token, int *const outCodePoints,
diff --git a/native/jni/src/utils/profiler.h b/native/jni/src/utils/profiler.h
new file mode 100644
index 0000000..5f107fe
--- /dev/null
+++ b/native/jni/src/utils/profiler.h
@@ -0,0 +1,86 @@
+/*
+ * 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_PROFILER_H
+#define LATINIME_PROFILER_H
+
+#ifdef FLAG_DO_PROFILE
+
+#include "defines.h"
+
+#include <ctime>
+#include <unordered_map>
+
+namespace latinime {
+
+class Profiler final {
+ public:
+    Profiler(const clockid_t clockId)
+            : mClockId(clockId), mStartTime(getTimeInMicroSec()), mStartTimes(), mTimes(),
+              mCounters() {}
+
+    ~Profiler() {
+        const float totalTime =
+                static_cast<float>(getTimeInMicroSec() - mStartTime) / 1000.f;
+        AKLOGI("Total time is %6.3f ms.", totalTime);
+        for (const auto &time : mTimes) {
+            AKLOGI("(%d): Used %4.2f%%, %8.4f ms. Called %d times.", time.first,
+                    time.second / totalTime * 100.0f, time.second, mCounters[time.first]);
+        }
+    }
+
+    void startTimer(const int id) {
+        mStartTimes[id] = getTimeInMicroSec();
+    }
+
+    void endTimer(const int id) {
+        mTimes[id] += static_cast<float>(getTimeInMicroSec() - mStartTimes[id]) / 1000.0f;
+        mCounters[id]++;
+    }
+
+    operator bool() const { return false; }
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(Profiler);
+
+    const clockid_t mClockId;
+    int64_t mStartTime;
+    std::unordered_map<int, int64_t> mStartTimes;
+    std::unordered_map<int, float> mTimes;
+    std::unordered_map<int, int> mCounters;
+
+    int64_t getTimeInMicroSec() {
+        timespec time;
+        clock_gettime(mClockId, &time);
+        return static_cast<int64_t>(time.tv_sec) * 1000000
+                + static_cast<int64_t>(time.tv_nsec) / 1000;
+    }
+};
+} // namespace latinime
+
+#define PROF_INIT Profiler __LATINIME__PROFILER__(CLOCK_THREAD_CPUTIME_ID)
+#define PROF_TIMER_START(timer_id) __LATINIME__PROFILER__.startTimer(timer_id)
+#define PROF_TIMER_END(timer_id) __LATINIME__PROFILER__.endTimer(timer_id)
+
+#else // FLAG_DO_PROFILE
+
+#define PROF_INIT
+#define PROF_TIMER_START(timer_id)
+#define PROF_TIMER_END(timer_id)
+
+#endif // FLAG_DO_PROFILE
+
+#endif /* LATINIME_PROFILER_H */
diff --git a/tests/Android.mk b/tests/Android.mk
index 7cdabf2..0b54491 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
 # We only want this apk build for tests.
diff --git a/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java b/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java
index 94caf51..1ea68e4 100644
--- a/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java
@@ -30,7 +30,7 @@
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyVisual;
 import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.utils.RunInLocale;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java b/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java
index e619801..6bb255b 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java
@@ -30,10 +30,11 @@
 import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
-import java.util.Arrays;
-import java.util.List;
+import java.util.ArrayList;
 import java.util.Locale;
 
+import javax.annotation.Nonnull;
+
 @SmallTest
 public class LanguageOnSpacebarHelperTests extends AndroidTestCase {
     private final LanguageOnSpacebarHelper mLanguageOnSpacebarHelper =
@@ -48,6 +49,7 @@
     RichInputMethodSubtype FR_CH_SWISS;
     RichInputMethodSubtype FR_CH_QWERTY;
     RichInputMethodSubtype FR_CH_QWERTZ;
+    RichInputMethodSubtype IW_HEBREW;
     RichInputMethodSubtype ZZ_QWERTY;
 
     @Override
@@ -56,116 +58,160 @@
         final Context context = getContext();
         RichInputMethodManager.init(context);
         mRichImm = RichInputMethodManager.getInstance();
-        SubtypeLocaleUtils.init(context);
 
-        EN_US_QWERTY = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
-                Locale.US.toString(), "qwerty"));
-        EN_GB_QWERTY = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
-                Locale.UK.toString(), "qwerty"));
-        FR_AZERTY = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
-                Locale.FRENCH.toString(), "azerty"));
-        FR_CA_QWERTY = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
-                Locale.CANADA_FRENCH.toString(), "qwerty"));
-        FR_CH_SWISS = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
-                "fr_CH", "swiss"));
+        EN_US_QWERTY = findSubtypeOf(Locale.US.toString(), "qwerty");
+        EN_GB_QWERTY = findSubtypeOf(Locale.UK.toString(), "qwerty");
+        FR_AZERTY = findSubtypeOf(Locale.FRENCH.toString(), "azerty");
+        FR_CA_QWERTY = findSubtypeOf(Locale.CANADA_FRENCH.toString(), "qwerty");
+        FR_CH_SWISS = findSubtypeOf("fr_CH", "swiss");
         FR_CH_QWERTZ = new RichInputMethodSubtype(
                 AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype("fr_CH", "qwertz"));
         FR_CH_QWERTY = new RichInputMethodSubtype(
                 AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype("fr_CH", "qwerty"));
-        ZZ_QWERTY = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
-                SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"));
+        IW_HEBREW = findSubtypeOf("iw", "hebrew");
+        ZZ_QWERTY = findSubtypeOf(SubtypeLocaleUtils.NO_LANGUAGE, "qwerty");
     }
 
-    private static List<InputMethodSubtype> asList(final InputMethodSubtype ... subtypes) {
-        return Arrays.asList(subtypes);
+    @Nonnull
+    private RichInputMethodSubtype findSubtypeOf(final String localeString,
+            final String keyboardLayoutSetName) {
+        final InputMethodSubtype subtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                localeString, keyboardLayoutSetName);
+        if (subtype == null) {
+            throw new RuntimeException("Can't find subtype of " + localeString + " with "
+                    + keyboardLayoutSetName);
+        }
+        return new RichInputMethodSubtype(subtype);
     }
 
-    public void testOneSubtype() {
-        mLanguageOnSpacebarHelper.updateEnabledSubtypes(asList(EN_US_QWERTY.getRawSubtype()));
-        mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
-        assertEquals("one same English (US)", FORMAT_TYPE_NONE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
-        assertEquals("one same NoLanguage", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
-
-        mLanguageOnSpacebarHelper.updateEnabledSubtypes(asList(FR_AZERTY.getRawSubtype()));
-        mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
-        assertEquals("one diff English (US)", FORMAT_TYPE_LANGUAGE_ONLY,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
-        assertEquals("one diff NoLanguage", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+    private void enableSubtypes(final RichInputMethodSubtype ... subtypes) {
+        final ArrayList<InputMethodSubtype> enabledSubtypes = new ArrayList<>();
+        for (final RichInputMethodSubtype subtype : subtypes) {
+            enabledSubtypes.add(subtype.getRawSubtype());
+        }
+        mLanguageOnSpacebarHelper.onUpdateEnabledSubtypes(enabledSubtypes);
     }
 
-    public void testTwoSubtypes() {
-        mLanguageOnSpacebarHelper.updateEnabledSubtypes(asList(EN_US_QWERTY.getRawSubtype(),
-                FR_AZERTY.getRawSubtype()));
-        mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
-        assertEquals("two same English (US)", FORMAT_TYPE_LANGUAGE_ONLY,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
-        assertEquals("two same French)", FORMAT_TYPE_LANGUAGE_ONLY,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_AZERTY));
-        assertEquals("two same NoLanguage", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
-
-        mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
-        assertEquals("two diff English (US)", FORMAT_TYPE_LANGUAGE_ONLY,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
-        assertEquals("two diff French", FORMAT_TYPE_LANGUAGE_ONLY,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_AZERTY));
-        assertEquals("two diff NoLanguage", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+    private void assertFormatType(final RichInputMethodSubtype subtype,
+            final boolean implicitlyEnabledSubtype, final Locale systemLocale,
+            final int expectedFormat) {
+        mLanguageOnSpacebarHelper.onSubtypeChanged(subtype, implicitlyEnabledSubtype, systemLocale);
+        assertEquals(subtype.getLocales()[0] + " implicitly=" + implicitlyEnabledSubtype
+                + " in " + systemLocale, expectedFormat,
+                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(subtype));
     }
 
-    public void testSameLanuageSubtypes() {
-        mLanguageOnSpacebarHelper.updateEnabledSubtypes(
-                asList(EN_US_QWERTY.getRawSubtype(), EN_GB_QWERTY.getRawSubtype(),
-                        FR_AZERTY.getRawSubtype(), ZZ_QWERTY.getRawSubtype()));
+    public void testOneSubtypeImplicitlyEnabled() {
+        enableSubtypes(EN_US_QWERTY);
+        assertFormatType(EN_US_QWERTY, true, Locale.US,            FORMAT_TYPE_NONE);
 
-        mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
-        assertEquals("two same English (US)", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
-        assertEquals("two same English (UK)", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_GB_QWERTY));
-        assertEquals("two same NoLanguage", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+        enableSubtypes(EN_GB_QWERTY);
+        assertFormatType(EN_GB_QWERTY, true, Locale.UK,            FORMAT_TYPE_NONE);
 
-        mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
-        assertEquals("two diff English (US)", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
-        assertEquals("two diff English (UK)", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_GB_QWERTY));
-        assertEquals("two diff NoLanguage", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+        enableSubtypes(FR_AZERTY);
+        assertFormatType(FR_AZERTY,    true, Locale.FRANCE,        FORMAT_TYPE_NONE);
+
+        enableSubtypes(FR_CA_QWERTY);
+        assertFormatType(FR_CA_QWERTY, true, Locale.CANADA_FRENCH, FORMAT_TYPE_NONE);
     }
 
-    public void testMultiSameLanuageSubtypes() {
-        mLanguageOnSpacebarHelper.updateEnabledSubtypes(
-                asList(FR_AZERTY.getRawSubtype(), FR_CA_QWERTY.getRawSubtype(),
-                        FR_CH_SWISS.getRawSubtype(), FR_CH_QWERTY.getRawSubtype(),
-                        FR_CH_QWERTZ.getRawSubtype()));
+    public void testOneSubtypeExplicitlyEnabled() {
+        enableSubtypes(EN_US_QWERTY);
+        assertFormatType(EN_US_QWERTY, false, Locale.UK,     FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(EN_US_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
 
-        mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
-        assertEquals("multi same French", FORMAT_TYPE_LANGUAGE_ONLY,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_AZERTY));
-        assertEquals("multi same French (CA)", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CA_QWERTY));
-        assertEquals("multi same French (CH)", FORMAT_TYPE_LANGUAGE_ONLY,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_SWISS));
-        assertEquals("multi same French (CH) (QWERTY)", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_QWERTY));
-        assertEquals("multi same French (CH) (QWERTZ)", FORMAT_TYPE_LANGUAGE_ONLY,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_QWERTZ));
+        enableSubtypes(EN_GB_QWERTY);
+        assertFormatType(EN_GB_QWERTY, false, Locale.US,     FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(EN_GB_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
 
-        mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
-        assertEquals("multi diff French", FORMAT_TYPE_LANGUAGE_ONLY,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_AZERTY));
-        assertEquals("multi diff French (CA)", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CA_QWERTY));
-        assertEquals("multi diff French (CH)", FORMAT_TYPE_LANGUAGE_ONLY,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_SWISS));
-        assertEquals("multi diff French (CH) (QWERTY)", FORMAT_TYPE_FULL_LOCALE,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_QWERTY));
-        assertEquals("multi diff French (CH) (QWERTZ)", FORMAT_TYPE_LANGUAGE_ONLY,
-                mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_QWERTZ));
+        enableSubtypes(FR_AZERTY);
+        assertFormatType(FR_AZERTY,    false, Locale.US,            FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_AZERTY,    false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+
+        enableSubtypes(FR_CA_QWERTY);
+        assertFormatType(FR_CA_QWERTY, false, Locale.US,            FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CA_QWERTY, false, Locale.FRANCE,        FORMAT_TYPE_LANGUAGE_ONLY);
+    }
+
+    public void testOneSubtypeImplicitlyEnabledWithNoLanguageSubtype() {
+        final Locale Locale_IW = new Locale("iw");
+        enableSubtypes(IW_HEBREW, ZZ_QWERTY);
+        // TODO: Should this be FORMAT_TYPE_NONE?
+        assertFormatType(IW_HEBREW,    true, Locale_IW, FORMAT_TYPE_LANGUAGE_ONLY);
+        // TODO: Should this be FORMAT_TYPE_NONE?
+        assertFormatType(ZZ_QWERTY,    true, Locale_IW, FORMAT_TYPE_FULL_LOCALE);
+    }
+
+    public void testTwoSubtypesExplicitlyEnabled() {
+        enableSubtypes(EN_US_QWERTY, FR_AZERTY);
+        assertFormatType(EN_US_QWERTY, false, Locale.US,     FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_AZERTY,    false, Locale.US,     FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(EN_US_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_AZERTY,    false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(EN_US_QWERTY, false, Locale.JAPAN,  FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_AZERTY,    false, Locale.JAPAN,  FORMAT_TYPE_LANGUAGE_ONLY);
+
+        enableSubtypes(EN_US_QWERTY, ZZ_QWERTY);
+        assertFormatType(EN_US_QWERTY, false, Locale.US,     FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(ZZ_QWERTY,    false, Locale.US,     FORMAT_TYPE_FULL_LOCALE);
+        assertFormatType(EN_US_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(ZZ_QWERTY,    false, Locale.FRANCE, FORMAT_TYPE_FULL_LOCALE);
+
+    }
+
+    public void testMultiSubtypeWithSameLanuageAndSameLayout() {
+        // Explicitly enable en_US, en_GB, fr_FR, and no language keyboards.
+        enableSubtypes(EN_US_QWERTY, EN_GB_QWERTY, FR_CA_QWERTY, ZZ_QWERTY);
+
+        assertFormatType(EN_US_QWERTY, false, Locale.US,    FORMAT_TYPE_FULL_LOCALE);
+        assertFormatType(EN_GB_QWERTY, false, Locale.US,    FORMAT_TYPE_FULL_LOCALE);
+        assertFormatType(FR_CA_QWERTY, false, Locale.US,    FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(ZZ_QWERTY,    false, Locale.US,    FORMAT_TYPE_FULL_LOCALE);
+
+        assertFormatType(EN_US_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE);
+        assertFormatType(EN_GB_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE);
+        assertFormatType(FR_CA_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(ZZ_QWERTY,    false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE);
+    }
+
+    public void testMultiSubtypesWithSameLanguageButHaveDifferentLayout() {
+        enableSubtypes(FR_AZERTY, FR_CA_QWERTY, FR_CH_SWISS, FR_CH_QWERTZ);
+
+        assertFormatType(FR_AZERTY,    false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CA_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CH_SWISS,  false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CH_QWERTZ, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+
+        assertFormatType(FR_AZERTY,    false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CA_QWERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CH_SWISS,  false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CH_QWERTZ, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+
+        assertFormatType(FR_AZERTY,    false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CA_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CH_SWISS,  false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CH_QWERTZ, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+    }
+
+    public void testMultiSubtypesWithSameLanguageAndMayHaveSameLayout() {
+        enableSubtypes(FR_AZERTY, FR_CA_QWERTY, FR_CH_SWISS, FR_CH_QWERTY, FR_CH_QWERTZ);
+
+        assertFormatType(FR_AZERTY,    false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CA_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_FULL_LOCALE);
+        assertFormatType(FR_CH_SWISS,  false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CH_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_FULL_LOCALE);
+        assertFormatType(FR_CH_QWERTZ, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+
+        assertFormatType(FR_AZERTY,    false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CA_QWERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_FULL_LOCALE);
+        assertFormatType(FR_CH_SWISS,  false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CH_QWERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_FULL_LOCALE);
+        assertFormatType(FR_CH_QWERTZ, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+
+        assertFormatType(FR_AZERTY,    false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CA_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE);
+        assertFormatType(FR_CH_SWISS,  false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+        assertFormatType(FR_CH_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE);
+        assertFormatType(FR_CH_QWERTZ, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index 039330c..cff489d 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -22,6 +22,8 @@
 
 import com.android.inputmethod.latin.NgramContext.WordInfo;
 import com.android.inputmethod.latin.common.CodePointUtils;
+import com.android.inputmethod.latin.common.FileUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
 import com.android.inputmethod.latin.makedict.DictDecoder;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
@@ -30,8 +32,6 @@
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
-import com.android.inputmethod.latin.utils.LocaleUtils;
 import com.android.inputmethod.latin.utils.WordInputEventForPersonalization;
 
 import java.io.File;
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
index fcaa8cd..60d2de1 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
@@ -24,12 +24,12 @@
 import com.android.inputmethod.latin.NgramContext.WordInfo;
 import com.android.inputmethod.latin.common.CodePointUtils;
 import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.FileUtils;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
 import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.WeightedString;
 import com.android.inputmethod.latin.makedict.WordProperty;
 import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index 926a2d3..00e0b52 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -44,10 +44,10 @@
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.common.Constants;
 import com.android.inputmethod.latin.common.InputPointers;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.common.StringUtils;
 import com.android.inputmethod.latin.settings.DebugSettings;
 import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.utils.LocaleUtils;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
 import java.util.Locale;
@@ -387,7 +387,7 @@
                 false /* isAuxiliary */,
                 false /* overridesImplicitlyEnabledSubtype */,
                 0 /* id */);
-        SubtypeSwitcher.forceSubtype(subtype);
+        RichInputMethodManager.forceSubtype(subtype);
         mLatinIME.onCurrentInputMethodSubtypeChanged(subtype);
         runMessages();
         mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard();
diff --git a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java b/tests/src/com/android/inputmethod/latin/RichInputMethodSubtypeTests.java
similarity index 97%
rename from tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java
rename to tests/src/com/android/inputmethod/latin/RichInputMethodSubtypeTests.java
index 83afd78..aed7d6a 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/RichInputMethodSubtypeTests.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin.utils;
+package com.android.inputmethod.latin;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -26,12 +26,15 @@
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.RichInputMethodManager;
 import com.android.inputmethod.latin.RichInputMethodSubtype;
+import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
+import com.android.inputmethod.latin.utils.RunInLocale;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
 import java.util.ArrayList;
 import java.util.Locale;
 
 @SmallTest
-public class SpacebarLanguageUtilsTests extends AndroidTestCase {
+public class RichInputMethodSubtypeTests extends AndroidTestCase {
     // All input method subtypes of LatinIME.
     private final ArrayList<RichInputMethodSubtype> mSubtypesList = new ArrayList<>();
 
diff --git a/tests/src/com/android/inputmethod/latin/WordComposerTests.java b/tests/src/com/android/inputmethod/latin/WordComposerTests.java
index 20256e6..8ae475f 100644
--- a/tests/src/com/android/inputmethod/latin/WordComposerTests.java
+++ b/tests/src/com/android/inputmethod/latin/WordComposerTests.java
@@ -20,8 +20,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.CoordinateUtils;
 import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
 
 /**
  * Unit tests for WordComposer.
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
index 7e54ce9..63ea89c 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
@@ -18,7 +18,7 @@
 
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.BinaryDictionary;
-import com.android.inputmethod.latin.utils.FileUtils;
+import com.android.inputmethod.latin.common.FileUtils;
 
 import java.io.File;
 import java.io.FileNotFoundException;
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index 1554219..1e4bd76 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -20,10 +20,10 @@
 import com.android.inputmethod.latin.BinaryDictionary;
 import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.NgramContext;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.LocaleUtils;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index 778f6e8..813a712 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -23,9 +23,9 @@
 import com.android.inputmethod.latin.ExpandableBinaryDictionary;
 import com.android.inputmethod.latin.NgramContext;
 import com.android.inputmethod.latin.NgramContext.WordInfo;
+import com.android.inputmethod.latin.common.FileUtils;
 import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
 import com.android.inputmethod.latin.utils.DistracterFilter;
-import com.android.inputmethod.latin.utils.FileUtils;
 
 import java.io.File;
 import java.io.FilenameFilter;
diff --git a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java
index 131865a..f50b8e0 100644
--- a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java
@@ -20,6 +20,8 @@
 import android.test.suitebuilder.annotation.LargeTest;
 
 import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.common.FileUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
 import com.android.inputmethod.latin.makedict.FormatSpec;
 
diff --git a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
index 4646a82..9680d85 100644
--- a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
@@ -21,8 +21,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
-import com.android.inputmethod.latin.utils.LocaleUtils;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
index dc4e2e4..47fd5fe 100644
--- a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
@@ -19,6 +19,8 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.inputmethod.latin.common.CollectionUtils;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk
index 4265925..1a9f029 100644
--- a/tools/dicttool/Android.mk
+++ b/tools/dicttool/Android.mk
@@ -47,14 +47,10 @@
         latin/Dictionary.java \
         latin/NgramContext.java \
         latin/SuggestedWords.java \
-        latin/settings/NativeSuggestOptions.java \
         latin/settings/SettingsValuesForSuggestion.java \
         latin/utils/BinaryDictionaryUtils.java \
         latin/utils/CombinedFormatUtils.java \
-        latin/utils/CoordinateUtils.java \
-        latin/utils/FileUtils.java \
-        latin/utils/JniUtils.java \
-        latin/utils/LocaleUtils.java
+        latin/utils/JniUtils.java
 
 LATINIME_OVERRIDABLE_SRC_FILES_FOR_DICTTOOL := \
         latin/define/DebugFlags.java
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java
index b6383d7..e2dd519 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java
@@ -16,10 +16,10 @@
 
 package com.android.inputmethod.latin.dicttool;
 
+import com.android.inputmethod.latin.common.FileUtils;
 import com.android.inputmethod.latin.makedict.BinaryDictDecoderEncoderTests;
 import com.android.inputmethod.latin.makedict.BinaryDictEncoderFlattenTreeTests;
 import com.android.inputmethod.latin.makedict.FusionDictionaryTest;
-import com.android.inputmethod.latin.utils.FileUtils;
 
 import java.io.File;
 import java.io.IOException;