Merge "clean up researchLogging of autocorrection"
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index 9f62e97..393bc18 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -30,7 +30,7 @@
             <meta-data android:name="android.view.textservice.scs" android:resource="@xml/spellchecker" />
         </service>
 
-        <activity android:name="Settings" android:label="@string/english_ime_settings">
+        <activity android:name="SettingsActivity" android:label="@string/english_ime_settings">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
             </intent-filter>
@@ -43,7 +43,7 @@
             </intent-filter>
         </activity>
 
-        <activity android:name="DebugSettings" android:label="@string/english_ime_debug_settings">
+        <activity android:name="DebugSettingsActivity" android:label="@string/english_ime_debug_settings">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
             </intent-filter>
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index bfe4232..892c72a 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -134,24 +134,24 @@
         <item>5</item>
     </string-array>
 
-    <!-- Subtype locale name exceptions -->
+    <!-- Subtype locale display name exceptions -->
     <string-array name="subtype_locale_exception_keys">
         <item>en_US</item>
         <item>en_GB</item>
-        <item>*_QY</item>
-        <item>QY</item>
     </string-array>
     <string-array name="subtype_locale_exception_values">
         <item>English (US)</item>
         <item>English (UK)</item>
-        <item>@string/subtype_generic_qwerty</item>
-        <item>QWERTY</item>
     </string-array>
 
     <!-- Generic subtype label -->
     <string name="subtype_generic">%s</string>
     <!-- Description for generic QWERTY keyboard subtype -->
     <string name="subtype_generic_qwerty">%s (QWERTY)</string>
+    <!-- Description for generic QWERTZ keyboard subtype -->
+    <string name="subtype_generic_qwertz">%s (QWERTZ)</string>
+    <!-- Description for generic AZERTY keyboard subtype -->
+    <string name="subtype_generic_azerty">%s (AZERTY)</string>
 
     <!-- dictionary pack package name /settings activity (for shared prefs and settings) -->
     <string name="dictionary_pack_package_name">com.google.android.inputmethod.latin.dictionarypack</string>
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index ba45343..981f246 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -28,7 +28,6 @@
     cs: Czech/qwertz
     da: Danish/nordic
     de: German/qwertz
-    de_QY: German (QWERTY)/qwerty
     el: Greek/greek
     en_US: English United States/qwerty
     en_GB: English Great Britain/qwerty
@@ -38,7 +37,6 @@
     fi: Finnish/nordic
     fr: French/azerty
     fr_CA: French Canada/qwerty
-    fr_CH: French Switzerland/qwertz
     hi: Hindi/hindi
     hr: Croatian/qwertz
     hu: Hungarian/qwertz
@@ -64,13 +62,13 @@
     tr: Turkish/qwerty
     uk: Ukrainian/east_slavic
     vi: Vietnamese/qwerty
-    zz_QY: QWERTY/qwerty
+    zz: QWERTY/qwerty
     -->
 <!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. -->
 <!-- If IME doesn't have an applicable subtype, the first subtype will be used as a default
      subtype.-->
 <input-method xmlns:android="http://schemas.android.com/apk/res/android"
-        android:settingsActivity="com.android.inputmethod.latin.Settings"
+        android:settingsActivity="com.android.inputmethod.latin.SettingsActivity"
         android:isDefault="@bool/im_is_default">
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_en_US"
@@ -121,12 +119,6 @@
             android:imeSubtypeExtraValue="KeyboardLayoutSet=qwertz,AsciiCapable"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_generic_qwerty"
-            android:imeSubtypeLocale="de"
-            android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty:de_QY,AsciiCapable"
-    />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_generic"
             android:imeSubtypeLocale="el"
             android:imeSubtypeMode="keyboard"
@@ -170,12 +162,6 @@
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_generic"
-            android:imeSubtypeLocale="fr_CH"
-            android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwertz,AsciiCapable"
-    />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="hi"
             android:imeSubtypeMode="keyboard"
             android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi"
@@ -327,7 +313,7 @@
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_no_language_qwerty"
-            android:imeSubtypeLocale="zz_QY"
+            android:imeSubtypeLocale="zz"
             android:imeSubtypeMode="keyboard"
             android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable"
     />
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
index ffed820..4bc82d2 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -17,7 +17,6 @@
 package com.android.inputmethod.compat;
 
 import android.content.Context;
-import android.inputmethodservice.InputMethodService;
 import android.os.IBinder;
 import android.util.Log;
 import android.view.inputmethod.InputMethodInfo;
@@ -50,9 +49,12 @@
         return sInstance;
     }
 
-    public static void init(InputMethodService service) {
-        sInstance.mImm = (InputMethodManager) service.getSystemService(
-                Context.INPUT_METHOD_SERVICE);
+    public static void init(Context context) {
+        sInstance.mImm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
+    }
+
+    public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
+        mImm.setAdditionalInputMethodSubtypes(imiId, subtypes);
     }
 
     public InputMethodSubtype getCurrentInputMethodSubtype() {
@@ -86,6 +88,11 @@
                 onlyCurrentIme);
     }
 
+    public List<InputMethodInfo> getInputMethodList() {
+        if (mImm == null) return null;
+        return mImm.getInputMethodList();
+    }
+
     public List<InputMethodInfo> getEnabledInputMethodList() {
         if (mImm == null) return null;
         return mImm.getEnabledInputMethodList();
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index 43c19d7..d97989d 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -104,8 +104,9 @@
     }
 
     public static CharSequence getTextWithSuggestionSpan(Context context,
-            CharSequence pickedWord, SuggestedWords suggestedWords) {
-        if (TextUtils.isEmpty(pickedWord) || CONSTRUCTOR_SuggestionSpan == null
+            CharSequence pickedWord, SuggestedWords suggestedWords, boolean dictionaryAvailable) {
+        if (!dictionaryAvailable || TextUtils.isEmpty(pickedWord)
+                || CONSTRUCTOR_SuggestionSpan == null
                 || suggestedWords == null || suggestedWords.size() == 0
                 || OBJ_SUGGESTIONS_MAX_SIZE == null) {
             return pickedWord;
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 58225e9..d54acf7 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -793,7 +793,7 @@
                     }
                 };
                 // Null means the current system locale.
-                final Locale locale = language.equals(SubtypeLocale.NO_LANGUAGE)
+                final Locale locale = SubtypeLocale.isNoLanguage(params.mId.mSubtype)
                         ? null : params.mId.mLocale;
                 job.runInLocale(mResources, locale);
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index e350818..b7eb8f6 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -19,9 +19,12 @@
 import android.text.InputType;
 import android.text.TextUtils;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.compat.EditorInfoCompatUtils;
 import com.android.inputmethod.latin.InputTypeUtils;
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.SubtypeLocale;
 
 import java.util.Arrays;
 import java.util.Locale;
@@ -53,6 +56,7 @@
 
     private static final int IME_ACTION_CUSTOM_LABEL = EditorInfo.IME_MASK_ACTION + 1;
 
+    public final InputMethodSubtype mSubtype;
     public final Locale mLocale;
     public final int mOrientation;
     public final int mWidth;
@@ -67,10 +71,11 @@
 
     private final int mHashCode;
 
-    public KeyboardId(int elementId, Locale locale, int orientation, int width, int mode,
-            EditorInfo editorInfo, boolean clobberSettingsKey, boolean shortcutKeyEnabled,
+    public KeyboardId(int elementId, InputMethodSubtype subtype, int orientation, int width,
+            int mode, EditorInfo editorInfo, boolean clobberSettingsKey, boolean shortcutKeyEnabled,
             boolean hasShortcutKey, boolean languageSwitchKeyEnabled) {
-        mLocale = locale;
+        mSubtype = subtype;
+        mLocale = SubtypeLocale.getSubtypeLocale(subtype);
         mOrientation = orientation;
         mWidth = width;
         mMode = mode;
@@ -102,7 +107,7 @@
                 id.mCustomActionLabel,
                 id.navigateNext(),
                 id.navigatePrevious(),
-                id.mLocale
+                id.mSubtype
         });
     }
 
@@ -123,7 +128,7 @@
                 && TextUtils.equals(other.mCustomActionLabel, mCustomActionLabel)
                 && other.navigateNext() == navigateNext()
                 && other.navigatePrevious() == navigatePrevious()
-                && other.mLocale.equals(mLocale);
+                && other.mSubtype.equals(mSubtype);
     }
 
     public boolean isAlphabetKeyboard() {
@@ -176,9 +181,10 @@
 
     @Override
     public String toString() {
-        return String.format("[%s %s %s%d %s %s %s%s%s%s%s%s%s%s]",
+        return String.format("[%s %s:%s %s%d %s %s %s%s%s%s%s%s%s%s]",
                 elementIdToName(mElementId),
                 mLocale,
+                mSubtype.getExtraValueOf(LatinIME.SUBTYPE_EXTRA_VALUE_KEYBOARD_LAYOUT_SET),
                 (mOrientation == 1 ? "port" : "land"), mWidth,
                 modeName(mMode),
                 imeAction(),
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 803a294..35209e0 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -32,7 +32,6 @@
 import com.android.inputmethod.latin.InputTypeUtils;
 import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.LocaleUtils;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.StringUtils;
 import com.android.inputmethod.latin.SubtypeLocale;
@@ -45,7 +44,6 @@
 import java.io.IOException;
 import java.lang.ref.SoftReference;
 import java.util.HashMap;
-import java.util.Locale;
 
 /**
  * This class represents a set of keyboard layouts. Each of them represents a different keyboard
@@ -61,8 +59,6 @@
     private static final String TAG_KEYBOARD_SET = "KeyboardLayoutSet";
     private static final String TAG_ELEMENT = "Element";
 
-    private static final String DEFAULT_KEYBOARD_LAYOUT_SET = "qwerty";
-    private static final char KEYBOARD_LAYOUT_SET_LOCALE_DELIMITER = ':';
     private static final String KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX = "keyboard_layout_set_";
 
     private final Context mContext;
@@ -112,7 +108,7 @@
         boolean mVoiceKeyOnMain;
         boolean mNoSettingsKey;
         boolean mLanguageSwitchKeyEnabled;
-        Locale mLocale;
+        InputMethodSubtype mSubtype;
         int mOrientation;
         int mWidth;
         // KeyboardLayoutSet element id to element's parameters map.
@@ -164,14 +160,13 @@
         }
         final KeyboardId id = getKeyboardId(keyboardLayoutSetElementId);
         try {
-            return getKeyboard(mContext, elementParams, id);
+            return getKeyboard(elementParams, id);
         } catch (RuntimeException e) {
             throw new KeyboardLayoutSetException(e, id);
         }
     }
 
-    private Keyboard getKeyboard(Context context, ElementParams elementParams,
-            final KeyboardId id) {
+    private Keyboard getKeyboard(ElementParams elementParams, final KeyboardId id) {
         final SoftReference<Keyboard> ref = sKeyboardCache.get(id);
         Keyboard keyboard = (ref == null) ? null : ref.get();
         if (keyboard == null) {
@@ -207,38 +202,14 @@
         final Params params = mParams;
         final boolean isSymbols = (keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS
                 || keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED);
-        final boolean noLanguage = params.mLocale.getLanguage().equals(SubtypeLocale.NO_LANGUAGE);
+        final boolean noLanguage = SubtypeLocale.isNoLanguage(params.mSubtype);
         final boolean voiceKeyEnabled = params.mVoiceKeyEnabled && !noLanguage;
         final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != params.mVoiceKeyOnMain);
-        return new KeyboardId(keyboardLayoutSetElementId, params.mLocale, params.mOrientation,
+        return new KeyboardId(keyboardLayoutSetElementId, params.mSubtype, params.mOrientation,
                 params.mWidth, params.mMode, params.mEditorInfo, params.mNoSettingsKey,
                 voiceKeyEnabled, hasShortcutKey, params.mLanguageSwitchKeyEnabled);
     }
 
-    private static String getKeyboardLayoutSetName(InputMethodSubtype subtype) {
-        final String keyboardLayoutSet = subtype.getExtraValueOf(
-                LatinIME.SUBTYPE_EXTRA_VALUE_KEYBOARD_LAYOUT_SET);
-        // TODO: Remove this null check when InputMethodManager.getCurrentInputMethodSubtype is
-        // fixed.
-        if (keyboardLayoutSet == null) return DEFAULT_KEYBOARD_LAYOUT_SET;
-        final int pos = keyboardLayoutSet.indexOf(KEYBOARD_LAYOUT_SET_LOCALE_DELIMITER);
-        return (pos > 0) ? keyboardLayoutSet.substring(0, pos) : keyboardLayoutSet;
-    }
-
-    public static String getKeyboardLayoutSetLocaleString(InputMethodSubtype subtype) {
-        final String keyboardLayoutSet = subtype.getExtraValueOf(
-                LatinIME.SUBTYPE_EXTRA_VALUE_KEYBOARD_LAYOUT_SET);
-        // TODO: Remove this null check when InputMethodManager.getCurrentInputMethodSubtype is
-        // fixed.
-        if (keyboardLayoutSet == null) return subtype.getLocale();
-        final int pos = keyboardLayoutSet.indexOf(KEYBOARD_LAYOUT_SET_LOCALE_DELIMITER);
-        return (pos > 0) ? keyboardLayoutSet.substring(pos + 1) : subtype.getLocale();
-    }
-
-    public static Locale getKeyboardLayoutSetLocale(InputMethodSubtype subtype) {
-        return LocaleUtils.constructLocaleFromString(getKeyboardLayoutSetLocaleString(subtype));
-    }
-
     public static class Builder {
         private final Context mContext;
         private final String mPackageName;
@@ -279,9 +250,9 @@
             final InputMethodSubtype keyboardSubtype = (forceAscii && !asciiCapable)
                     ? SubtypeSwitcher.getInstance().getNoLanguageSubtype()
                     : subtype;
-            mParams.mLocale = getKeyboardLayoutSetLocale(keyboardSubtype);
+            mParams.mSubtype = keyboardSubtype;
             mParams.mKeyboardLayoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX
-                    + getKeyboardLayoutSetName(keyboardSubtype);
+                    + SubtypeLocale.getKeyboardLayoutSetName(keyboardSubtype);
             return this;
         }
 
@@ -306,7 +277,7 @@
         public KeyboardLayoutSet build() {
             if (mParams.mOrientation == Configuration.ORIENTATION_UNDEFINED)
                 throw new RuntimeException("Screen geometry is not specified");
-            if (mParams.mLocale == null)
+            if (mParams.mSubtype == null)
                 throw new RuntimeException("KeyboardLayoutSet subtype is not specified");
             final String packageName = mResources.getResourcePackageName(
                     R.xml.keyboard_layout_set_qwerty);
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index 7fadb3b..341d9bc 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -35,6 +35,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodSubtype;
 import android.widget.PopupWindow;
 
 import com.android.inputmethod.accessibility.AccessibilityUtils;
@@ -52,7 +53,6 @@
 import com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
 import com.android.inputmethod.latin.define.ProductionFlag;
 
-import java.util.Locale;
 import java.util.WeakHashMap;
 
 /**
@@ -80,7 +80,6 @@
     private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
     private static final int ALPHA_OPAQUE = 255;
     private boolean mNeedsToDisplayLanguage;
-    private Locale mSpacebarLocale;
     private int mLanguageOnSpacebarAnimAlpha = ALPHA_OPAQUE;
     private final float mSpacebarTextRatio;
     private float mSpacebarTextSize;
@@ -468,7 +467,6 @@
         mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon(keyboard.mIconsSet) : null;
         final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
         mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
-        mSpacebarLocale = keyboard.mId.mLocale;
     }
 
     /**
@@ -904,12 +902,12 @@
     }
 
     // Layout locale language name on spacebar.
-    private String layoutLanguageOnSpacebar(Paint paint, Locale locale, int width,
+    private String layoutLanguageOnSpacebar(Paint paint, InputMethodSubtype subtype, int width,
             float origTextSize) {
         paint.setTextAlign(Align.CENTER);
         paint.setTypeface(Typeface.DEFAULT);
         // Estimate appropriate language name text size to fit in maxTextWidth.
-        String language = SubtypeLocale.getFullDisplayName(locale);
+        String language = SubtypeLocale.getFullDisplayName(subtype);
         int textWidth = getTextWidth(paint, language, origTextSize);
         // Assuming text width and text size are proportional to each other.
         float textSize = origTextSize * Math.min(width / textWidth, 1.0f);
@@ -921,7 +919,7 @@
 
         final boolean useShortName;
         if (useMiddleName) {
-            language = SubtypeLocale.getMiddleDisplayName(locale);
+            language = SubtypeLocale.getMiddleDisplayName(subtype);
             textWidth = getTextWidth(paint, language, origTextSize);
             textSize = origTextSize * Math.min(width / textWidth, 1.0f);
             useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME)
@@ -931,7 +929,7 @@
         }
 
         if (useShortName) {
-            language = SubtypeLocale.getShortDisplayName(locale);
+            language = SubtypeLocale.getShortDisplayName(subtype);
             textWidth = getTextWidth(paint, language, origTextSize);
             textSize = origTextSize * Math.min(width / textWidth, 1.0f);
         }
@@ -944,10 +942,10 @@
         final int width = key.mWidth;
         final int height = key.mHeight;
 
-        // If input subtypes are explicitly selected.
+        // If input language are explicitly selected.
         if (mNeedsToDisplayLanguage) {
-            final String language = layoutLanguageOnSpacebar(paint, mSpacebarLocale, width,
-                    mSpacebarTextSize);
+            final String language = layoutLanguageOnSpacebar(
+                    paint, getKeyboard().mId.mSubtype, width, mSpacebarTextSize);
             // Draw language text with shadow
             // In case there is no space icon, we will place the language text at the center of
             // spacebar.
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 24ab547..37d9b6a 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -248,6 +248,9 @@
             mListener.onPressKey(key.mCode);
             final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
             mKeyboardLayoutHasBeenChanged = false;
+            if (!key.altCodeWhileTyping() && !key.isModifier()) {
+                mTimerProxy.startTypingStateTimer();
+            }
             return keyboardLayoutHasBeenChanged;
         }
         return false;
@@ -279,9 +282,6 @@
             } else if (code != Keyboard.CODE_UNSPECIFIED) {
                 mListener.onCodeInput(code, x, y);
             }
-            if (!key.altCodeWhileTyping() && !key.isModifier()) {
-                mTimerProxy.startTypingStateTimer();
-            }
         }
     }
 
@@ -734,6 +734,9 @@
     public void onRepeatKey(Key key) {
         if (key != null) {
             detectAndSendKey(key, key.mX, key.mY);
+            if (!key.altCodeWhileTyping() && !key.isModifier()) {
+                mTimerProxy.startTypingStateTimer();
+            }
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
new file mode 100644
index 0000000..deb2478
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.view.inputmethod.InputMethodSubtype;
+
+import java.util.HashMap;
+import java.util.Locale;
+
+public class AdditionalSubtype {
+    public static final String QWERTY = "qwerty";
+    public static final String QWERTZ = "qwertz";
+    public static final String AZERTY = "azerty";
+
+    private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
+    private static final String SUBTYPE_EXTRA_VALUE_IS_ADDITIONAL_SUBTYPE = "isAdditionalSubtype";
+
+    // Keyboard layout to subtype name resource id map.
+    private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap =
+            new HashMap<String, Integer>();
+
+    static {
+        sKeyboardLayoutToNameIdsMap.put(QWERTY, R.string.subtype_generic_qwerty);
+        sKeyboardLayoutToNameIdsMap.put(QWERTZ, R.string.subtype_generic_qwertz);
+        sKeyboardLayoutToNameIdsMap.put(AZERTY, R.string.subtype_generic_azerty);
+    }
+
+    public static boolean isAdditionalSubtype(InputMethodSubtype subtype) {
+        return subtype.containsExtraValueKey(SUBTYPE_EXTRA_VALUE_IS_ADDITIONAL_SUBTYPE);
+    }
+
+    public static InputMethodSubtype createAddtionalSubtype(
+            Locale locale, String keyboardLayoutSet) {
+        final String extraValue = String.format(
+                "%s=%s,%s", LatinIME.SUBTYPE_EXTRA_VALUE_KEYBOARD_LAYOUT_SET, keyboardLayoutSet,
+                SUBTYPE_EXTRA_VALUE_IS_ADDITIONAL_SUBTYPE);
+        Integer nameId = sKeyboardLayoutToNameIdsMap.get(keyboardLayoutSet);
+        if (nameId == null) nameId = R.string.subtype_generic;
+        return new InputMethodSubtype(nameId, R.drawable.ic_subtype_keyboard,
+                locale.toString(), SUBTYPE_MODE_KEYBOARD, extraValue, false, false);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java
index 23d63b4..af76498 100644
--- a/java/src/com/android/inputmethod/latin/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/DebugSettings.java
@@ -16,18 +16,19 @@
 
 package com.android.inputmethod.latin;
 
+import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Bundle;
 import android.os.Process;
 import android.preference.CheckBoxPreference;
-import android.preference.PreferenceActivity;
+import android.preference.PreferenceFragment;
 import android.util.Log;
 
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 
-public class DebugSettings extends PreferenceActivity
+public class DebugSettings extends PreferenceFragment
         implements SharedPreferences.OnSharedPreferenceChangeListener {
 
     private static final String TAG = DebugSettings.class.getSimpleName();
@@ -38,7 +39,7 @@
     private CheckBoxPreference mDebugMode;
 
     @Override
-    protected void onCreate(Bundle icicle) {
+    public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
         addPreferencesFromResource(R.xml.prefs_for_debug);
         SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
@@ -50,7 +51,7 @@
     }
 
     @Override
-    protected void onStop() {
+    public void onStop() {
         super.onStop();
         if (mServiceNeedsRestart) Process.killProcess(Process.myPid());
     }
@@ -76,7 +77,9 @@
         boolean isDebugMode = mDebugMode.isChecked();
         String version = "";
         try {
-            PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0);
+            final Context context = getActivity();
+            final String packageName = context.getPackageName();
+            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
             version = "Version " + info.versionName;
         } catch (NameNotFoundException e) {
             Log.e(TAG, "Could not find version info.");
diff --git a/java/src/com/android/inputmethod/latin/DebugSettingsActivity.java b/java/src/com/android/inputmethod/latin/DebugSettingsActivity.java
new file mode 100644
index 0000000..cde2060
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/DebugSettingsActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+public class DebugSettingsActivity extends PreferenceActivity {
+    @Override
+    public Intent getIntent() {
+        final Intent modIntent = new Intent(super.getIntent());
+        modIntent.putExtra(EXTRA_SHOW_FRAGMENT, DebugSettings.class.getName());
+        return modIntent;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setTitle(R.string.english_ime_debug_settings);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index b587568..3369292 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -439,6 +439,10 @@
 
         loadSettings();
 
+        mImm.setAdditionalInputMethodSubtypes(
+                SubtypeUtils.getInputMethodId(getPackageName()),
+                mSettingsValues.getPrefefinedAdditionalSubtypes());
+
         // TODO: remove the following when it's not needed by updateCorrectionMode() any more
         mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
         updateCorrectionMode();
@@ -1899,7 +1903,8 @@
             if (mSettingsValues.mEnableSuggestionSpanInsertion) {
                 final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
                 ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
-                        this, bestWord, suggestedWords), 1);
+                        this, bestWord, suggestedWords, mSubtypeSwitcher.isDictionaryAvailable()),
+                        1);
             } else {
                 ic.commitText(bestWord, 1);
             }
@@ -2225,15 +2230,15 @@
         }
     }
 
-    protected void launchSettings() {
-        launchSettingsClass(Settings.class);
+    private void launchSettings() {
+        launchSettingsClass(SettingsActivity.class);
     }
 
     public void launchDebugSettings() {
-        launchSettingsClass(DebugSettings.class);
+        launchSettingsClass(DebugSettingsActivity.class);
     }
 
-    protected void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
+    private void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
         handleClose();
         Intent intent = new Intent();
         intent.setClass(LatinIME.this, settingsClass);
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 6bc0498..43e7e27 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -16,9 +16,7 @@
 
 package com.android.inputmethod.latin;
 
-import android.app.Activity;
 import android.app.AlertDialog;
-import android.app.Fragment;
 import android.app.backup.BackupManager;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -33,15 +31,16 @@
 import android.preference.Preference.OnPreferenceClickListener;
 import android.preference.PreferenceGroup;
 import android.preference.PreferenceScreen;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 import android.widget.TextView;
 
 import com.android.inputmethod.latin.define.ProductionFlag;
-import com.android.inputmethodcommon.InputMethodSettingsActivity;
+import com.android.inputmethodcommon.InputMethodSettingsFragment;
 
-public class Settings extends InputMethodSettingsActivity
+public class Settings extends InputMethodSettingsFragment
         implements SharedPreferences.OnSharedPreferenceChangeListener {
     public static final boolean ENABLE_EXPERIMENTAL_SETTINGS = false;
 
@@ -103,26 +102,16 @@
         }
     }
 
-    public Activity getActivityInternal() {
-        Object thisObject = (Object) this;
-        if (thisObject instanceof Activity) {
-            return (Activity) thisObject;
-        } else if (thisObject instanceof Fragment) {
-            return ((Fragment) thisObject).getActivity();
-        } else {
-            return null;
-        }
-    }
-
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
         setInputMethodSettingsCategoryTitle(R.string.language_selection_title);
         setSubtypeEnablerTitle(R.string.select_language);
-        final Resources res = getResources();
-        final Context context = getActivityInternal();
-
         addPreferencesFromResource(R.xml.prefs);
+
+        final Resources res = getResources();
+        final Context context = getActivity();
+
         mVoicePreference = (ListPreference) findPreference(PREF_VOICE_MODE);
         mShowCorrectionSuggestionsPreference =
                 (ListPreference) findPreference(PREF_SHOW_SUGGESTIONS_SETTING);
@@ -276,7 +265,7 @@
 
     @Override
     public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
-        (new BackupManager(getActivityInternal())).dataChanged();
+        (new BackupManager(getActivity())).dataChanged();
         if (key.equals(PREF_POPUP_ON)) {
             final ListPreference popupDismissDelay =
                 (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
@@ -318,7 +307,7 @@
     private void refreshEnablingsOfKeypressSoundAndVibrationSettings(
             SharedPreferences sp, Resources res) {
         if (mKeypressVibrationDurationSettingsPref != null) {
-            final boolean hasVibrator = VibratorUtils.getInstance(this).hasVibrator();
+            final boolean hasVibrator = VibratorUtils.getInstance(getActivity()).hasVibrator();
             final boolean vibrateOn = hasVibrator && sp.getBoolean(Settings.PREF_VIBRATE_ON,
                     res.getBoolean(R.bool.config_default_vibration_enabled));
             mKeypressVibrationDurationSettingsPref.setEnabled(vibrateOn);
@@ -342,7 +331,7 @@
 
     private void showKeypressVibrationDurationSettingsDialog() {
         final SharedPreferences sp = getPreferenceManager().getSharedPreferences();
-        final Activity context = getActivityInternal();
+        final Context context = getActivity();
         final Resources res = context.getResources();
         final AlertDialog.Builder builder = new AlertDialog.Builder(context);
         builder.setTitle(R.string.prefs_keypress_vibration_duration_settings);
@@ -361,7 +350,7 @@
                 dialog.dismiss();
             }
         });
-        final View v = context.getLayoutInflater().inflate(
+        final View v = LayoutInflater.from(context).inflate(
                 R.layout.vibration_settings_dialog, null);
         final int currentMs = SettingsValues.getCurrentVibrationDuration(
                 getPreferenceManager().getSharedPreferences(), getResources());
@@ -398,9 +387,9 @@
     }
 
     private void showKeypressSoundVolumeSettingDialog() {
-        final AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+        final Context context = getActivity();
+        final AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
         final SharedPreferences sp = getPreferenceManager().getSharedPreferences();
-        final Activity context = getActivityInternal();
         final Resources res = context.getResources();
         final AlertDialog.Builder builder = new AlertDialog.Builder(context);
         builder.setTitle(R.string.prefs_keypress_sound_volume_settings);
@@ -420,7 +409,7 @@
                 dialog.dismiss();
             }
         });
-        final View v = context.getLayoutInflater().inflate(
+        final View v = LayoutInflater.from(context).inflate(
                 R.layout.sound_effect_volume_dialog, null);
         final int currentVolumeInt =
                 (int)(SettingsValues.getCurrentKeypressSoundVolume(sp, res) * 100);
diff --git a/java/src/com/android/inputmethod/latin/SettingsActivity.java b/java/src/com/android/inputmethod/latin/SettingsActivity.java
new file mode 100644
index 0000000..b85a936
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/SettingsActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+public class SettingsActivity extends PreferenceActivity {
+    @Override
+    public Intent getIntent() {
+        final Intent modIntent = new Intent(super.getIntent());
+        modIntent.putExtra(EXTRA_SHOW_FRAGMENT, Settings.class.getName());
+        return modIntent;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setTitle(R.string.english_ime_settings);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 526acf1..8ff644f 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -21,12 +21,14 @@
 import android.content.res.Resources;
 import android.util.Log;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.keyboard.internal.KeySpecParser;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Locale;
 
 /**
  * When you call the constructor of this class, you may want to change the current system locale by
@@ -69,6 +71,7 @@
     private final int mVibrationDurationSettingsRawValue;
     @SuppressWarnings("unused") // TODO: Use this
     private final float mKeypressSoundVolumeRawValue;
+    private final InputMethodSubtype[] mPredefinedAdditionalSubtypes;
 
     // Deduced settings
     public final int mKeypressVibrationDuration;
@@ -145,6 +148,16 @@
                 mAutoCorrectionThresholdRawValue);
         mVoiceKeyEnabled = mVoiceMode != null && !mVoiceMode.equals(voiceModeOff);
         mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain);
+
+        // Predefined additional subtypes
+        final InputMethodSubtype DE_QWERTY = AdditionalSubtype.createAddtionalSubtype(
+                Locale.GERMAN, AdditionalSubtype.QWERTY);
+        final InputMethodSubtype FR_QWERTZ = AdditionalSubtype.createAddtionalSubtype(
+                Locale.FRENCH, AdditionalSubtype.QWERTZ);
+        mPredefinedAdditionalSubtypes = new InputMethodSubtype[] {
+                DE_QWERTY,
+                FR_QWERTZ,
+        };
     }
 
     // Helper functions to create member values.
@@ -304,6 +317,11 @@
         return res.getBoolean(R.bool.config_use_fullscreen_mode);
     }
 
+    // TODO: Should be able to add/remove/edit.
+    public InputMethodSubtype[] getPrefefinedAdditionalSubtypes() {
+        return mPredefinedAdditionalSubtypes;
+    }
+
     // Accessed from the settings interface, hence public
     public static float getCurrentKeypressSoundVolume(final SharedPreferences sp,
                 final Resources res) {
diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
index 2bc22a6..37da5e8 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
@@ -18,20 +18,19 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.view.inputmethod.InputMethodSubtype;
 
-
+import java.util.HashMap;
 import java.util.Locale;
 
 public class SubtypeLocale {
     // Special language code to represent "no language".
-    public static final String NO_LANGUAGE = "zz";
-    // Special country code to represent "QWERTY".
-    /* package for test */ static final String QWERTY = "QY";
+    private static final String NO_LANGUAGE = "zz";
+    public static final Locale LOCALE_NO_LANGUAGE = new Locale(NO_LANGUAGE);
 
-    public static final Locale LOCALE_NO_LANGUAGE_QWERTY = new Locale(NO_LANGUAGE, QWERTY);
-
-    private static String[] sExceptionKeys;
-    private static String[] sExceptionValues;
+    // Exceptional locales to display name map.
+    private static final HashMap<String, String> sExceptionalDisplayNamesMap =
+            new HashMap<String, String>();
 
     private SubtypeLocale() {
         // Intentional empty constructor for utility class.
@@ -39,70 +38,82 @@
 
     public static void init(Context context) {
         final Resources res = context.getResources();
-        sExceptionKeys = res.getStringArray(R.array.subtype_locale_exception_keys);
-        sExceptionValues = res.getStringArray(R.array.subtype_locale_exception_values);
-    }
-
-    private static String lookupExceptionalLocale(String key) {
-        for (int index = 0; index < sExceptionKeys.length; index++) {
-            if (sExceptionKeys[index].equals(key)) {
-                return sExceptionValues[index];
-            }
-        }
-        return null;
-    }
-
-    // Get Locale's full display name in its locale.
-    // For example:
-    // "fr_CH" is converted to "Français (Suisse)".
-    // "de_QY" is converted to "Deutsche (QWERTY)". (Any locale that has country code "QY")
-    // "zz_QY" is converted to "QWERTY". (The language code "zz" means "No language", thus just
-    // ends up with the keyboard layout name.)
-    public static String getFullDisplayName(Locale locale) {
-        final String key;
-        if (locale.getLanguage().equals(NO_LANGUAGE)) {
-            key = locale.getCountry();
-        } else if (locale.getCountry().equals(QWERTY)) {
-            key = "*_" + QWERTY;
-        } else {
-            key = locale.toString();
-        }
-        final String value = lookupExceptionalLocale(key);
-        if (value == null) {
-            return StringUtils.toTitleCase(locale.getDisplayName(locale), locale);
-        }
-        if (value.indexOf("%s") >= 0) {
-            final String languageName = StringUtils.toTitleCase(locale.getDisplayLanguage(locale), locale);
-            return String.format(value, languageName);
-        }
-        return value;
-    }
-
-    // Get Locale's middle display name in its locale.
-    // For example:
-    // "fr_CH" is converted to "Français".
-    // "de_QY" is converted to "Deutsche". (Any locale that has country code "QY")
-    // "zz_QY" is converted to "QWERTY". (The language code "zz" means "No language", thus just
-    // ends up with the keyboard layout name.)
-    public static String getMiddleDisplayName(Locale locale) {
-        if (NO_LANGUAGE.equals(locale.getLanguage())) {
-            return lookupExceptionalLocale(locale.getCountry());
-        } else {
-            return StringUtils.toTitleCase(locale.getDisplayLanguage(locale), locale);
+        final String[] locales = res.getStringArray(R.array.subtype_locale_exception_keys);
+        final String[] displayNames = res.getStringArray(R.array.subtype_locale_exception_values);
+        for (int i = 0; i < locales.length; i++) {
+            sExceptionalDisplayNamesMap.put(locales[i], displayNames[i]);
         }
     }
 
-    // Get Locale's short display name in its locale.
-    // For example:
-    // "fr_CH" is converted to "Fr".
-    // "de_QY" is converted to "De". (Any locale that has country code "QY")
-    // "zz_QY" is converter to "QY". (The language code "zz" means "No language", thus just ends
-    // up with the keyboard layout name.)
-    public static String getShortDisplayName(Locale locale) {
-        if (NO_LANGUAGE.equals(locale.getLanguage())) {
-            return locale.getCountry();
-        } else {
-            return StringUtils.toTitleCase(locale.getLanguage(), locale);
+    // Get InputMethodSubtype's display name in its locale.
+    //        isAdditionalSubtype (T=true, F=false)
+    // locale layout | Short  Middle      Full
+    // ------ ------ - ---- --------- -----------------
+    //  en_US qwerty F  En  English   English (US)      exception
+    //  en_GB qwerty F  En  English   English (UK)      exception
+    //  fr    azerty F  Fr  Français  Français
+    //  fr_CA qwerty F  Fr  Français  Français (Canada)
+    //  de    qwertz F  De  Deutsch   Deutsch
+    //  zz    qwerty F      QWERTY    QWERTY
+    //  fr    qwertz T  Fr  Français  Français (QWERTZ)
+    //  de    qwerty T  De  Deutsch   Deutsch (QWERTY)
+    //  en    azerty T  En  English   English (AZERTY)
+    //  zz    azerty T      AZERTY    AZERTY
+
+    // Get InputMethodSubtype's full display name in its locale.
+    public static String getFullDisplayName(InputMethodSubtype subtype) {
+        final String value = sExceptionalDisplayNamesMap.get(subtype.getLocale());
+        if (value != null) {
+            return value;
         }
+
+        if (isNoLanguage(subtype)) {
+            return getKeyboardLayoutSetName(subtype).toUpperCase();
+        }
+
+        final Locale locale = getSubtypeLocale(subtype);
+        final String language = StringUtils.toTitleCase(locale.getDisplayLanguage(locale), locale);
+        if (AdditionalSubtype.isAdditionalSubtype(subtype)) {
+            return String.format("%s (%s)",
+                    language, getKeyboardLayoutSetName(subtype).toUpperCase());
+        }
+        return StringUtils.toTitleCase(locale.getDisplayName(locale), locale);
+    }
+
+    // Get InputMethodSubtype's middle display name in its locale.
+    public static String getMiddleDisplayName(InputMethodSubtype subtype) {
+        if (isNoLanguage(subtype)) {
+            return getKeyboardLayoutSetName(subtype).toUpperCase();
+        }
+        final Locale locale = getSubtypeLocale(subtype);
+        return StringUtils.toTitleCase(locale.getDisplayLanguage(locale), locale);
+    }
+
+    // Get InputMethodSubtype's short display name in its locale.
+    public static String getShortDisplayName(InputMethodSubtype subtype) {
+        if (isNoLanguage(subtype)) {
+            return "";
+        }
+        final Locale locale = getSubtypeLocale(subtype);
+        return StringUtils.toTitleCase(locale.getLanguage(), locale);
+    }
+
+    public static boolean isNoLanguage(InputMethodSubtype subtype) {
+        final String localeString = subtype.getLocale();
+        return localeString.equals(NO_LANGUAGE);
+    }
+
+    public static Locale getSubtypeLocale(InputMethodSubtype subtype) {
+        final String localeString = subtype.getLocale();
+        return LocaleUtils.constructLocaleFromString(localeString);
+    }
+
+    public static String getKeyboardLayoutSetName(InputMethodSubtype subtype) {
+        final String keyboardLayoutSet = subtype.getExtraValueOf(
+                LatinIME.SUBTYPE_EXTRA_VALUE_KEYBOARD_LAYOUT_SET);
+        // TODO: Remove this null check when InputMethodManager.getCurrentInputMethodSubtype is
+        // fixed.
+        if (keyboardLayoutSet == null) return AdditionalSubtype.QWERTY;
+        return keyboardLayoutSet;
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 6612c24..3bb3ab4 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -31,7 +31,6 @@
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
-import com.android.inputmethod.keyboard.KeyboardLayoutSet;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 
 import java.util.ArrayList;
@@ -63,6 +62,7 @@
     /*-----------------------------------------------------------*/
     // Variants which should be changed only by reload functions.
     private boolean mNeedsToDisplayLanguage;
+    private boolean mIsDictionaryAvailable;
     private boolean mIsSystemLanguageSameAsInputLanguage;
     private InputMethodInfo mShortcutInputMethodInfo;
     private InputMethodSubtype mShortcutSubtype;
@@ -104,8 +104,8 @@
         mInputLocaleStr = null;
         mCurrentSubtype = mImm.getCurrentInputMethodSubtype();
         mAllEnabledSubtypesOfCurrentInputMethod = null;
-        mNoLanguageSubtype = SubtypeUtils.findSubtypeByKeyboardLayoutSetLocale(
-                service, SubtypeLocale.LOCALE_NO_LANGUAGE_QWERTY);
+        mNoLanguageSubtype = SubtypeUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
+                service, SubtypeLocale.LOCALE_NO_LANGUAGE, AdditionalSubtype.QWERTY);
 
         final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
         mIsNetworkConnected = (info != null && info.isConnected());
@@ -128,14 +128,14 @@
 
     // Reload enabledSubtypes from the framework.
     private void updateEnabledSubtypes() {
-        final String currentMode = getCurrentSubtypeMode();
+        final String currentMode = mCurrentSubtype.getMode();
         boolean foundCurrentSubtypeBecameDisabled = true;
         mAllEnabledSubtypesOfCurrentInputMethod = mImm.getEnabledInputMethodSubtypeList(
                 null, true);
         mEnabledLanguagesOfCurrentInputMethod.clear();
         mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
         for (InputMethodSubtype ims : mAllEnabledSubtypesOfCurrentInputMethod) {
-            final String locale = KeyboardLayoutSet.getKeyboardLayoutSetLocaleString(ims);
+            final String locale = ims.getLocale();
             final String mode = ims.getMode();
             mLocaleSplitter.setString(locale);
             if (mLocaleSplitter.hasNext()) {
@@ -165,8 +165,7 @@
                     + (mShortcutInputMethodInfo == null
                             ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
                     + (mShortcutSubtype == null ? "<null>" : (
-                            KeyboardLayoutSet.getKeyboardLayoutSetLocaleString(mShortcutSubtype)
-                            + ", " + mShortcutSubtype.getMode())));
+                            mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
         }
         // TODO: Update an icon for shortcut IME
         final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
@@ -188,16 +187,15 @@
                     + (mShortcutInputMethodInfo == null
                             ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
                     + (mShortcutSubtype == null ? "<null>" : (
-                            KeyboardLayoutSet.getKeyboardLayoutSetLocaleString(mShortcutSubtype)
-                            + ", " + mShortcutSubtype.getMode())));
+                            mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
         }
     }
 
     // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
     public void updateSubtype(InputMethodSubtype newSubtype) {
-        final String newLocale = KeyboardLayoutSet.getKeyboardLayoutSetLocaleString(newSubtype);
+        final String newLocale = newSubtype.getLocale();
         final String newMode = newSubtype.getMode();
-        final String oldMode = getCurrentSubtypeMode();
+        final String oldMode = mCurrentSubtype.getMode();
         if (DBG) {
             Log.w(TAG, "Update subtype to:" + newLocale + "," + newMode
                     + ", from: " + mInputLocaleStr + ", " + oldMode);
@@ -217,7 +215,7 @@
         }
         mCurrentSubtype = newSubtype;
 
-        if (isKeyboardMode()) {
+        if (KEYBOARD_MODE.equals(mCurrentSubtype.getMode())) {
             if (modeChanged || languageChanged) {
                 updateShortcutIME();
                 mService.onRefreshKeyboard();
@@ -232,12 +230,10 @@
             }
             Log.w(TAG, "Unknown subtype mode: " + newMode + "," + version + ", " + packageName
                     + ". IME is already changed to other IME.");
-            if (newSubtype != null) {
-                Log.w(TAG, "Subtype mode:" + newSubtype.getMode());
-                Log.w(TAG, "Subtype locale:" + newSubtype.getLocale());
-                Log.w(TAG, "Subtype extra value:" + newSubtype.getExtraValue());
-                Log.w(TAG, "Subtype is auxiliary:" + newSubtype.isAuxiliary());
-            }
+            Log.w(TAG, "Subtype mode:" + newSubtype.getMode());
+            Log.w(TAG, "Subtype locale:" + newSubtype.getLocale());
+            Log.w(TAG, "Subtype extra value:" + newSubtype.getExtraValue());
+            Log.w(TAG, "Subtype is auxiliary:" + newSubtype.isAuxiliary());
         }
     }
 
@@ -260,6 +256,7 @@
                 getInputLocale().getLanguage());
         mNeedsToDisplayLanguage = !(getEnabledKeyboardLocaleCount() <= 1
                 && mIsSystemLanguageSameAsInputLanguage);
+        mIsDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(mService, mInputLocale);
     }
 
     ////////////////////////////
@@ -280,10 +277,11 @@
         if (token == null) {
             return;
         }
+        final InputMethodManagerCompatWrapper imm = mImm;
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
-                mImm.setInputMethodAndSubtype(token, imiId, subtype);
+                imm.setInputMethodAndSubtype(token, imiId, subtype);
                 return null;
             }
         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@@ -335,7 +333,7 @@
     }
 
     public boolean needsToDisplayLanguage(Locale keyboardLocale) {
-        if (keyboardLocale.equals(SubtypeLocale.LOCALE_NO_LANGUAGE_QWERTY)) {
+        if (keyboardLocale.equals(SubtypeLocale.LOCALE_NO_LANGUAGE)) {
             return true;
         }
         if (!keyboardLocale.equals(mInputLocale)) {
@@ -379,20 +377,8 @@
         }
     }
 
-    // TODO: Remove this method
-    private boolean isKeyboardMode() {
-        return KEYBOARD_MODE.equals(getCurrentSubtypeMode());
-    }
-
-    // TODO: Remove this method
-    private String getCurrentSubtypeMode() {
-        return mCurrentSubtype.getMode();
-    }
-
-    // TODO: Remove this method
-    public boolean currentSubtypeContainsExtraValueKey(String key) {
-        // If null, return what an empty ExtraValue would return : false.
-        return mCurrentSubtype.containsExtraValueKey(key);
+    public boolean isDictionaryAvailable() {
+        return mIsDictionaryAvailable;
     }
 
     public InputMethodSubtype getCurrentSubtype() {
diff --git a/java/src/com/android/inputmethod/latin/SubtypeUtils.java b/java/src/com/android/inputmethod/latin/SubtypeUtils.java
index a747c9a..4d0f1c2 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeUtils.java
@@ -21,7 +21,6 @@
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
-import com.android.inputmethod.keyboard.KeyboardLayoutSet;
 
 import java.util.Collections;
 import java.util.List;
@@ -125,24 +124,26 @@
             throw new RuntimeException("Input method manager not found");
         }
 
-        for (final InputMethodInfo imi : imm.getEnabledInputMethodList()) {
+        for (final InputMethodInfo imi : imm.getInputMethodList()) {
             if (imi.getPackageName().equals(packageName))
                 return imi;
         }
         throw new RuntimeException("Can not find input method id for " + packageName);
     }
 
-    public static InputMethodSubtype findSubtypeByKeyboardLayoutSetLocale(
-            Context context, Locale locale) {
+    public static InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(
+            Context context, Locale locale, String keyoardLayoutSet) {
         final String localeString = locale.toString();
-        final InputMethodInfo imi = SubtypeUtils.getInputMethodInfo(context.getPackageName());
+        final InputMethodInfo imi = getInputMethodInfo(context.getPackageName());
         final int count = imi.getSubtypeCount();
         for (int i = 0; i < count; i++) {
             final InputMethodSubtype subtype = imi.getSubtypeAt(i);
-            if (localeString.equals(KeyboardLayoutSet.getKeyboardLayoutSetLocaleString(subtype))) {
+            final String layout = SubtypeLocale.getKeyboardLayoutSetName(subtype);
+            if (localeString.equals(subtype.getLocale()) && keyoardLayoutSet.equals(layout)) {
                 return subtype;
             }
         }
-        throw new RuntimeException("Can not find subtype of locale " + localeString);
+        throw new RuntimeException("Can't find subtype for locale " + localeString
+                + " and keyboard layout " + keyoardLayoutSet);
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicFrenchTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
similarity index 75%
rename from tests/src/com/android/inputmethod/latin/InputLogicFrenchTests.java
rename to tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
index 60a7b05..b2b9601 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicFrenchTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.latin;
 
-public class InputLogicFrenchTests extends InputTestsBase {
+public class InputLogicTestsNonEnglish extends InputTestsBase {
 
     public void testAutoCorrectForFrench() {
         final String STRING_TO_TYPE = "irq ";
@@ -54,4 +54,22 @@
         assertEquals("type word then type space then punctuation from strip twice for French",
                 EXPECTED_RESULT, mTextView.getText().toString());
     }
+
+    public void testAutoCorrectForGerman() {
+        final String STRING_TO_TYPE = "unf ";
+        final String EXPECTED_RESULT = "und ";
+        changeLanguage("de");
+        type(STRING_TO_TYPE);
+        assertEquals("simple auto-correct for German", EXPECTED_RESULT,
+                mTextView.getText().toString());
+    }
+
+    public void testAutoCorrectWithUmlautForGerman() {
+        final String STRING_TO_TYPE = "ueber ";
+        final String EXPECTED_RESULT = "über ";
+        changeLanguage("de");
+        type(STRING_TO_TYPE);
+        assertEquals("auto-correct with umlaut for German", EXPECTED_RESULT,
+                mTextView.getText().toString());
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
index 4ac7657..d34055a 100644
--- a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
+++ b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
@@ -18,58 +18,35 @@
 
 import android.content.Context;
 import android.test.AndroidTestCase;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
 
-import com.android.inputmethod.keyboard.KeyboardLayoutSet;
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
 
 import java.util.ArrayList;
 import java.util.Locale;
 
 public class SubtypeLocaleTests extends AndroidTestCase {
-    private static final Locale LOCALE_zz_QY = SubtypeLocale.LOCALE_NO_LANGUAGE_QWERTY;
-    private static final Locale LOCALE_de_QY =
-            new Locale(Locale.GERMAN.getLanguage(), SubtypeLocale.QWERTY);
-
-    private ArrayList<InputMethodSubtype> mSubtypesList;
+    // Locale to subtypes list.
+    private final ArrayList<InputMethodSubtype> mSubtypesList = new ArrayList<InputMethodSubtype>();
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-
         final Context context = getContext();
-        final String packageName = context.getApplicationInfo().packageName;
-
+        InputMethodManagerCompatWrapper.init(context);
         SubtypeLocale.init(context);
-
-        final InputMethodManager imm = (InputMethodManager) context.getSystemService(
-                Context.INPUT_METHOD_SERVICE);
-        for (final InputMethodInfo imi : imm.getInputMethodList()) {
-            if (imi.getPackageName().equals(packageName)) {
-                mSubtypesList = new ArrayList<InputMethodSubtype>();
-                final int subtypeCount = imi.getSubtypeCount();
-                for (int i = 0; i < subtypeCount; i++) {
-                    final InputMethodSubtype ims = imi.getSubtypeAt(i);
-                    mSubtypesList.add(ims);
-                }
-                break;
-            }
-        }
-        assertNotNull("Can not find input method " + packageName, mSubtypesList);
-        assertTrue("Can not find keyboard subtype", mSubtypesList.size() > 0);
     }
 
-    public void testFullDisplayName() {
+    public void testAllFullDisplayName() {
         final StringBuilder messages = new StringBuilder();
         int failedCount = 0;
         for (final InputMethodSubtype subtype : mSubtypesList) {
-            final Locale locale = KeyboardLayoutSet.getKeyboardLayoutSetLocale(subtype);
-            if (locale.getLanguage().equals(SubtypeLocale.NO_LANGUAGE)) {
+            final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
+            if (SubtypeLocale.isNoLanguage(subtype)) {
                 // This is special language name for language agnostic usage.
                 continue;
             }
-            final String keyboardName = SubtypeLocale.getFullDisplayName(locale);
+            final String keyboardName = SubtypeLocale.getFullDisplayName(subtype);
             final String languageName = StringUtils.toTitleCase(
                     locale.getDisplayLanguage(locale), locale);
             if (!keyboardName.contains(languageName)) {
@@ -82,24 +59,16 @@
         assertEquals(messages.toString(), 0, failedCount);
     }
 
-    public void testFullDisplayNameNoLanguage() {
-        assertEquals("zz_QY", "QWERTY", SubtypeLocale.getFullDisplayName(LOCALE_zz_QY));
-
-        final String de_QY = SubtypeLocale.getFullDisplayName(LOCALE_de_QY);
-        assertTrue("de_QY", de_QY.contains("(QWERTY"));
-        assertTrue("de_QY", de_QY.contains(Locale.GERMAN.getDisplayLanguage(Locale.GERMAN)));
-    }
-
-    public void testMiddleDisplayName() {
+   public void testAllMiddleDisplayName() {
         final StringBuilder messages = new StringBuilder();
         int failedCount = 0;
         for (final InputMethodSubtype subtype : mSubtypesList) {
-            final Locale locale = KeyboardLayoutSet.getKeyboardLayoutSetLocale(subtype);
-            if (locale.getLanguage().equals(SubtypeLocale.NO_LANGUAGE)) {
+            final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
+            if (SubtypeLocale.isNoLanguage(subtype)) {
                 // This is special language name for language agnostic usage.
                 continue;
             }
-            final String keyboardName = SubtypeLocale.getMiddleDisplayName(locale);
+            final String keyboardName = SubtypeLocale.getMiddleDisplayName(subtype);
             final String languageName = StringUtils.toTitleCase(
                     locale.getDisplayLanguage(locale), locale);
             if (!keyboardName.equals(languageName)) {
@@ -112,21 +81,12 @@
         assertEquals(messages.toString(), 0, failedCount);
     }
 
-    public void testMiddleDisplayNameNoLanguage() {
-        assertEquals("zz_QY", "QWERTY", SubtypeLocale.getMiddleDisplayName(LOCALE_zz_QY));
-        assertEquals("de_QY", "Deutsch", SubtypeLocale.getMiddleDisplayName(LOCALE_de_QY));
-    }
-
-    public void testShortDisplayName() {
+    public void testAllShortDisplayName() {
         final StringBuilder messages = new StringBuilder();
         int failedCount = 0;
         for (final InputMethodSubtype subtype : mSubtypesList) {
-            final Locale locale = KeyboardLayoutSet.getKeyboardLayoutSetLocale(subtype);
-            if (locale.getCountry().equals(SubtypeLocale.QWERTY)) {
-                // This is special country code for QWERTY keyboard.
-                continue;
-            }
-            final String keyboardName = SubtypeLocale.getShortDisplayName(locale);
+            final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
+            final String keyboardName = SubtypeLocale.getShortDisplayName(subtype);
             final String languageCode = StringUtils.toTitleCase(locale.getLanguage(), locale);
             if (!keyboardName.equals(languageCode)) {
                 failedCount++;
@@ -138,8 +98,99 @@
         assertEquals(messages.toString(), 0, failedCount);
     }
 
-    public void testShortDisplayNameNoLanguage() {
-        assertEquals("zz_QY", "QY", SubtypeLocale.getShortDisplayName(LOCALE_zz_QY));
-        assertEquals("de_QY", "De", SubtypeLocale.getShortDisplayName(LOCALE_de_QY));
+    // Get InputMethodSubtype's display name in its locale.
+    //            additional
+    // locale layout  Short  Middle      Full
+    // ------ ------ - ---- --------- -----------------
+    //  en_US qwerty F  En  English   English (US)      exception
+    //  en_GB qwerty F  En  English   English (UK)      exception
+    //  fr    azerty F  Fr  Français  Français
+    //  fr_CA qwerty F  Fr  Français  Français (Canada)
+    //  de    qwertz F  De  Deutsch   Deutsch
+    //  zz    qwerty F      QWERTY    QWERTY
+    //  fr    qwertz T  Fr  Français  Français (QWERTZ)
+    //  de    qwerty T  De  Deutsch   Deutsch (QWERTY)
+    //  zz    azerty T      AZERTY    AZERTY
+
+    public void testSampleSubtypes() {
+        final Context context = getContext();
+        final InputMethodSubtype EN_US = SubtypeUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
+                context, Locale.US, AdditionalSubtype.QWERTY);
+        final InputMethodSubtype EN_GB = SubtypeUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
+                context, Locale.UK, AdditionalSubtype.QWERTY);
+        final InputMethodSubtype FR = SubtypeUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
+                context, Locale.FRENCH, AdditionalSubtype.AZERTY);
+        final InputMethodSubtype FR_CA = SubtypeUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
+                context, Locale.CANADA_FRENCH, AdditionalSubtype.QWERTY);
+        final InputMethodSubtype DE = SubtypeUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
+                context, Locale.GERMAN, AdditionalSubtype.QWERTZ);
+        final InputMethodSubtype ZZ = SubtypeUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
+                context, SubtypeLocale.LOCALE_NO_LANGUAGE, AdditionalSubtype.QWERTY);
+
+        assertFalse(AdditionalSubtype.isAdditionalSubtype(EN_US));
+        assertFalse(AdditionalSubtype.isAdditionalSubtype(EN_GB));
+        assertFalse(AdditionalSubtype.isAdditionalSubtype(FR));
+        assertFalse(AdditionalSubtype.isAdditionalSubtype(FR_CA));
+        assertFalse(AdditionalSubtype.isAdditionalSubtype(DE));
+        assertFalse(AdditionalSubtype.isAdditionalSubtype(ZZ));
+
+        assertEquals("en_US", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(EN_US));
+        assertEquals("en_GB", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(EN_GB));
+        assertEquals("fr   ", "azerty", SubtypeLocale.getKeyboardLayoutSetName(FR));
+        assertEquals("fr_CA", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(FR_CA));
+        assertEquals("de   ", "qwertz", SubtypeLocale.getKeyboardLayoutSetName(DE));
+        assertEquals("zz   ", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(ZZ));
+
+        assertEquals("en_US", "English (US)",      SubtypeLocale.getFullDisplayName(EN_US));
+        assertEquals("en_GB", "English (UK)",      SubtypeLocale.getFullDisplayName(EN_GB));
+        assertEquals("fr   ", "Français",          SubtypeLocale.getFullDisplayName(FR));
+        assertEquals("fr_CA", "Français (Canada)", SubtypeLocale.getFullDisplayName(FR_CA));
+        assertEquals("de   ", "Deutsch",           SubtypeLocale.getFullDisplayName(DE));
+        assertEquals("zz   ", "QWERTY",            SubtypeLocale.getFullDisplayName(ZZ));
+
+        assertEquals("en_US", "English",  SubtypeLocale.getMiddleDisplayName(EN_US));
+        assertEquals("en_GB", "English",  SubtypeLocale.getMiddleDisplayName(EN_GB));
+        assertEquals("fr   ", "Français", SubtypeLocale.getMiddleDisplayName(FR));
+        assertEquals("fr_CA", "Français", SubtypeLocale.getMiddleDisplayName(FR_CA));
+        assertEquals("de   ", "Deutsch",  SubtypeLocale.getMiddleDisplayName(DE));
+        assertEquals("zz   ", "QWERTY",   SubtypeLocale.getMiddleDisplayName(ZZ));
+
+        assertEquals("en_US", "En", SubtypeLocale.getShortDisplayName(EN_US));
+        assertEquals("en_GB", "En", SubtypeLocale.getShortDisplayName(EN_GB));
+        assertEquals("fr   ", "Fr", SubtypeLocale.getShortDisplayName(FR));
+        assertEquals("fr_CA", "Fr", SubtypeLocale.getShortDisplayName(FR_CA));
+        assertEquals("de   ", "De", SubtypeLocale.getShortDisplayName(DE));
+        assertEquals("zz   ", "", SubtypeLocale.getShortDisplayName(ZZ));
+    }
+
+    public void testAdditionalSubtype() {
+        final InputMethodSubtype DE_QWERTY = AdditionalSubtype.createAddtionalSubtype(
+                Locale.GERMAN, AdditionalSubtype.QWERTY);
+        final InputMethodSubtype FR_QWERTZ = AdditionalSubtype.createAddtionalSubtype(
+                Locale.FRENCH, AdditionalSubtype.QWERTZ);
+        final InputMethodSubtype EN_AZERTY = AdditionalSubtype.createAddtionalSubtype(
+                Locale.ENGLISH, AdditionalSubtype.AZERTY);
+        final InputMethodSubtype ZZ_AZERTY = AdditionalSubtype.createAddtionalSubtype(
+                SubtypeLocale.LOCALE_NO_LANGUAGE, AdditionalSubtype.AZERTY);
+
+        assertTrue(AdditionalSubtype.isAdditionalSubtype(FR_QWERTZ));
+        assertTrue(AdditionalSubtype.isAdditionalSubtype(DE_QWERTY));
+        assertTrue(AdditionalSubtype.isAdditionalSubtype(EN_AZERTY));
+        assertTrue(AdditionalSubtype.isAdditionalSubtype(ZZ_AZERTY));
+
+        assertEquals("fr qwertz", "Français (QWERTZ)", SubtypeLocale.getFullDisplayName(FR_QWERTZ));
+        assertEquals("de qwerty", "Deutsch (QWERTY)",  SubtypeLocale.getFullDisplayName(DE_QWERTY));
+        assertEquals("en azerty", "English (AZERTY)",  SubtypeLocale.getFullDisplayName(EN_AZERTY));
+        assertEquals("zz azerty", "AZERTY",            SubtypeLocale.getFullDisplayName(ZZ_AZERTY));
+
+        assertEquals("fr qwertz", "Français", SubtypeLocale.getMiddleDisplayName(FR_QWERTZ));
+        assertEquals("de qwerty", "Deutsch",  SubtypeLocale.getMiddleDisplayName(DE_QWERTY));
+        assertEquals("en azerty", "English",  SubtypeLocale.getMiddleDisplayName(EN_AZERTY));
+        assertEquals("zz azerty", "AZERTY",   SubtypeLocale.getMiddleDisplayName(ZZ_AZERTY));
+
+        assertEquals("fr qwertz", "Fr", SubtypeLocale.getShortDisplayName(FR_QWERTZ));
+        assertEquals("de qwerty", "De", SubtypeLocale.getShortDisplayName(DE_QWERTY));
+        assertEquals("en azerty", "En", SubtypeLocale.getShortDisplayName(EN_AZERTY));
+        assertEquals("zz azerty", "", SubtypeLocale.getShortDisplayName(ZZ_AZERTY));
     }
 }