Add a functionarity to handle Subtype

Change-Id: Ic6055ba218b64f4276e3db99a435f546c3990ee5
diff --git a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
index 4f4f7e3..7f64bcb 100644
--- a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
+++ b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
@@ -37,7 +37,7 @@
     private String mSelectedLanguages;
     private ArrayList<Loc> mAvailableLanguages = new ArrayList<Loc>();
     private static final String[] BLACKLIST_LANGUAGES = {
-        "ko", "ja", "zh", "el"
+        "ko", "ja", "zh", "el", "zz"
     };
 
     private static class Loc implements Comparable<Object> {
@@ -74,7 +74,7 @@
         for (int i = 0; i < mAvailableLanguages.size(); i++) {
             CheckBoxPreference pref = new CheckBoxPreference(this);
             Locale locale = mAvailableLanguages.get(i).locale;
-            pref.setTitle(SubtypeSwitcher.getLanguageName(locale));
+            pref.setTitle(SubtypeSwitcher.getFullDisplayName(locale, true));
             boolean checked = isLocaleIn(locale, languageList);
             pref.setChecked(checked);
             if (hasDictionary(locale)) {
@@ -167,7 +167,7 @@
 
                 if (finalSize == 0) {
                     preprocess[finalSize++] =
-                            new Loc(SubtypeSwitcher.getLanguageName(l), l);
+                            new Loc(SubtypeSwitcher.getFullDisplayName(l, true), l);
                 } else {
                     // check previous entry:
                     //  same lang and a country -> upgrade to full name and
@@ -175,15 +175,15 @@
                     //  diff lang -> insert ours with lang-only name
                     if (preprocess[finalSize-1].locale.getLanguage().equals(
                             language)) {
-                        preprocess[finalSize-1].label = SubtypeSwitcher.getLanguageName(
-                                preprocess[finalSize-1].locale);
+                        preprocess[finalSize-1].label = SubtypeSwitcher.getFullDisplayName(
+                                preprocess[finalSize-1].locale, false);
                         preprocess[finalSize++] =
-                                new Loc(SubtypeSwitcher.getLanguageName(l), l);
+                                new Loc(SubtypeSwitcher.getFullDisplayName(l, false), l);
                     } else {
                         String displayName;
                         if (s.equals("zz_ZZ")) {
                         } else {
-                            displayName = SubtypeSwitcher.getLanguageName(l);
+                            displayName = SubtypeSwitcher.getFullDisplayName(l, true);
                             preprocess[finalSize++] = new Loc(displayName, l);
                         }
                     }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 9a800ba..295a03b 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -462,7 +462,7 @@
 
     private void initSuggest() {
         updateAutoTextEnabled();
-        String locale = mSubtypeSwitcher.getInputLanguage();
+        String locale = mSubtypeSwitcher.getInputLocaleStr();
 
         Resources orig = getResources();
         Locale savedLocale = mSubtypeSwitcher.changeSystemLocale(new Locale(locale));
@@ -519,6 +519,7 @@
     @Override
     public void onConfigurationChanged(Configuration conf) {
         mSubtypeSwitcher.onConfigurationChanged(conf);
+        onLanguageChanged();
         updateAutoTextEnabled();
 
         // If orientation changed while predicting, commit the change
@@ -577,9 +578,11 @@
             return;
         }
 
+        SubtypeSwitcher.getInstance().updateParametersOnStartInputView();
+
         if (mRefreshKeyboardRequired) {
             mRefreshKeyboardRequired = false;
-            toggleLanguage(true, true);
+            onLanguageChanged();
         }
 
         TextEntryState.newSession(this);
@@ -2176,8 +2179,18 @@
         return mWord.isFirstCharCapitalized();
     }
 
+    // Notify that Language has been changed and toggleLanguage will update KeyboaredID according
+    // to new Language.
+    private void onLanguageChanged() {
+        toggleLanguage(true, true);
+    }
+
+    // "reset" and "next" are used only for USE_SPACEBAR_LANGUAGE_SWITCHER.
     private void toggleLanguage(boolean reset, boolean next) {
-        mSubtypeSwitcher.toggleLanguage(reset, next);
+        if (SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCHER) {
+            mSubtypeSwitcher.toggleLanguage(reset, next);
+        }
+        // Reload keyboard because the current language has been changed.
         KeyboardSwitcher switcher = mKeyboardSwitcher;
         final int mode = switcher.getKeyboardMode();
         final EditorInfo attribute = getCurrentInputEditorInfo();
@@ -2249,7 +2262,7 @@
         return new FieldContext(
                 getCurrentInputConnection(),
                 getCurrentInputEditorInfo(),
-                mSubtypeSwitcher.getInputLanguage(),
+                mSubtypeSwitcher.getInputLocaleStr(),
                 mSubtypeSwitcher.getEnabledLanguages());
     }
 
@@ -2372,7 +2385,7 @@
     private void updateAutoTextEnabled() {
         if (mSuggest == null) return;
         mSuggest.setAutoTextEnabled(mQuickFixes
-                && SubtypeSwitcher.getInstance().isSystemLocaleSameAsInputLocale());
+                && SubtypeSwitcher.getInstance().isSystemLanguageSameAsInputLanguage());
     }
 
     private void updateSuggestionVisibility(SharedPreferences prefs) {
@@ -2419,7 +2432,7 @@
                 sp.getBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, false);
 
         mLocaleSupportedForVoiceInput = SubtypeSwitcher.getInstance().isVoiceSupported(
-                SubtypeSwitcher.getInstance().getInputLanguage());
+                SubtypeSwitcher.getInstance().getInputLocaleStr());
 
         mAutoCorrectEnabled = isAutoCorrectEnabled(sp);
         mBigramSuggestionEnabled = mAutoCorrectEnabled && isBigramSuggestionEnabled(sp);
@@ -2580,6 +2593,7 @@
 
     @Override
     public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
-        SubtypeSwitcher.getInstance().onCurrentInputMethodSubtypeChanged(subtype);
+        SubtypeSwitcher.getInstance().updateSubtype(subtype);
+        onLanguageChanged();
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIMESettings.java b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
index df2cd27..960c54d 100644
--- a/java/src/com/android/inputmethod/latin/LatinIMESettings.java
+++ b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
@@ -16,7 +16,6 @@
 
 package com.android.inputmethod.latin;
 
-import java.util.ArrayList;
 import java.util.Locale;
 
 import android.app.AlertDialog;
@@ -34,7 +33,6 @@
 import android.text.AutoText;
 import android.util.Log;
 
-import com.android.inputmethod.voice.SettingsUtil;
 import com.android.inputmethod.voice.VoiceInputLogger;
 
 public class LatinIMESettings extends PreferenceActivity
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboard.java b/java/src/com/android/inputmethod/latin/LatinKeyboard.java
index 9473eaf..6520d08 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboard.java
@@ -419,7 +419,7 @@
         final Rect bounds = new Rect();
 
         // Estimate appropriate language name text size to fit in maxTextWidth.
-        String language = SubtypeSwitcher.getLanguageName(locale);
+        String language = SubtypeSwitcher.getDisplayLanguage(locale);
         int textWidth = getTextWidth(paint, language, origTextSize, bounds);
         // Assuming text width and text size are proportional to each other.
         float textSize = origTextSize * Math.min(maxTextWidth / textWidth, 1.0f);
@@ -435,7 +435,7 @@
             textSize = origTextSize;
         }
         if (useShortName) {
-            language = SubtypeSwitcher.getShortLanguageName(locale);
+            language = SubtypeSwitcher.getShortDisplayLanguage(locale);
             textWidth = getTextWidth(paint, language, origTextSize, bounds);
             textSize = origTextSize * Math.min(maxTextWidth / textWidth, 1.0f);
         }
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 768274e..fe62994 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -18,14 +18,18 @@
 
 import com.android.inputmethod.voice.SettingsUtil;
 
+import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.inputmethodservice.InputMethodService;
 import android.preference.PreferenceManager;
 import android.text.TextUtils;
+import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
@@ -33,12 +37,34 @@
 public class SubtypeSwitcher {
     // This flag indicates if we support language switching by swipe on space bar.
     // We may or may not draw the current language on space bar regardless of this flag.
-    public static final boolean USE_SPACEBAR_LANGUAGE_SWITCHER = true;
+    public static final boolean USE_SPACEBAR_LANGUAGE_SWITCHER = false;
+    private static final boolean DBG = false;
     private static final String TAG = "SubtypeSwitcher";
+
+    private static final char LOCALE_SEPARATER = '_';
+    private static final String KEYBOARD_MODE = "keyboard";
+    private static final String VOICE_MODE = "voice";
+    private final TextUtils.SimpleStringSplitter mLocaleSplitter =
+            new TextUtils.SimpleStringSplitter(LOCALE_SEPARATER);
+
     private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
-    private InputMethodService mService;
-    private Resources mResources;
+    private /* final */ InputMethodService mService;
+    private /* final */ InputMethodManager mImm;
+    private /* final */ Resources mResources;
+    private final ArrayList<InputMethodSubtype> mEnabledKeyboardSubtypesOfCurrentInputMethod =
+            new ArrayList<InputMethodSubtype>();
+    private final ArrayList<String> mEnabledLanguagesOfCurrentInputMethod = new ArrayList<String>();
+
+    /*-----------------------------------------------------------*/
+    // Variants which should be changed only by reload functions.
     private Locale mSystemLocale;
+    private Locale mInputLocale;
+    private String mInputLocaleStr;
+    private String mMode;
+    private List<InputMethodSubtype> mAllEnabledSubtypesOfCurrentInputMethod;
+    private boolean mNeedsToDisplayLanguage;
+    private boolean mIsSystemLanguageSameAsInputLanguage;
+    /*-----------------------------------------------------------*/
 
     public static SubtypeSwitcher getInstance() {
         return sInstance;
@@ -47,16 +73,97 @@
     public static void init(LatinIME service) {
         sInstance.mService = service;
         sInstance.mResources = service.getResources();
-        sInstance.mSystemLocale = sInstance.mResources.getConfiguration().locale;
+        sInstance.mImm = (InputMethodManager) service.getSystemService(
+                Context.INPUT_METHOD_SERVICE);
         if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
             sInstance.initLanguageSwitcher(service);
         }
+
+        sInstance.updateAllParameters();
     }
 
     private SubtypeSwitcher() {
     }
 
-    public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
+    // Update all parameters stored in SubtypeSwitcher.
+    // Only configuration changed event is allowed to call this because this is heavy.
+    private void updateAllParameters() {
+        mSystemLocale = mResources.getConfiguration().locale;
+        updateSubtype(mImm.getCurrentInputMethodSubtype());
+        updateParametersOnStartInputView();
+    }
+
+    // Update parameters which are changed outside LatinIME. This parameters affect UI so they
+    // should be updated every time onStartInputview.
+    public void updateParametersOnStartInputView() {
+        if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
+            updateForSpaceBarLanguageSwitch();
+        } else {
+            updateEnabledSubtypes();
+        }
+    }
+
+    // Reload enabledSubtypes from the framework.
+    private void updateEnabledSubtypes() {
+        boolean foundCurrentSubtypeBecameDisabled = true;
+        mAllEnabledSubtypesOfCurrentInputMethod = mImm.getEnabledInputMethodSubtypeList(null);
+        mEnabledLanguagesOfCurrentInputMethod.clear();
+        mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
+        for (InputMethodSubtype ims: mAllEnabledSubtypesOfCurrentInputMethod) {
+            final String locale = ims.getLocale();
+            final String mode = ims.getMode();
+            mLocaleSplitter.setString(locale);
+            if (mLocaleSplitter.hasNext()) {
+                mEnabledLanguagesOfCurrentInputMethod.add(mLocaleSplitter.next());
+            }
+            if (locale.equals(mInputLocaleStr) && mode.equals(mMode)) {
+                foundCurrentSubtypeBecameDisabled = false;
+            }
+            if (KEYBOARD_MODE.equals(ims.getMode())) {
+                mEnabledKeyboardSubtypesOfCurrentInputMethod.add(ims);
+            }
+        }
+        mNeedsToDisplayLanguage = !(getEnabledKeyboardLocaleCount() <= 1
+                && mIsSystemLanguageSameAsInputLanguage);
+        if (foundCurrentSubtypeBecameDisabled) {
+            updateSubtype(mImm.getCurrentInputMethodSubtype());
+        }
+    }
+
+    // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
+    public void updateSubtype(InputMethodSubtype newSubtype) {
+        if (DBG) {
+            Log.w(TAG, "Update subtype to:" + newSubtype.getLocale() + "," + newSubtype.getMode());
+        }
+        updateInputLocale(newSubtype.getLocale());
+        mMode = newSubtype.getMode();
+    }
+
+    // Update the current input locale from Locale string.
+    private void updateInputLocale(String inputLocaleStr) {
+        // example: inputLocaleStr = "en_US" "en" ""
+        // "en_US" --> language: en  & country: US
+        // "en" --> language: en
+        // "" --> the system locale
+        mLocaleSplitter.setString(inputLocaleStr);
+        if (mLocaleSplitter.hasNext()) {
+            String language = mLocaleSplitter.next();
+            if (mLocaleSplitter.hasNext()) {
+                mInputLocale = new Locale(language, mLocaleSplitter.next());
+            } else {
+                mInputLocale = new Locale(language);
+            }
+            mInputLocaleStr = inputLocaleStr;
+        } else {
+            mInputLocale = mSystemLocale;
+            String country = mSystemLocale.getCountry();
+            mInputLocaleStr = mSystemLocale.getLanguage()
+                    + (TextUtils.isEmpty(country) ? "" : "_" + mSystemLocale.getLanguage());
+        }
+        mIsSystemLanguageSameAsInputLanguage = getSystemLocale().getLanguage().equalsIgnoreCase(
+                getInputLocale().getLanguage());
+        mNeedsToDisplayLanguage = !(getEnabledKeyboardLocaleCount() <= 1
+                && mIsSystemLanguageSameAsInputLanguage);
     }
 
     //////////////////////////////////
@@ -66,82 +173,94 @@
     public int getEnabledKeyboardLocaleCount() {
         if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
             return mLanguageSwitcher.getLocaleCount();
+        } else {
+            return mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
         }
-        // TODO: Implement for no legacy mode
-        return 0;
     }
 
-    // TODO: Cache the value
     public boolean needsToDisplayLanguage() {
-     // TODO: Takes care of two-char locale such as "en" in addition to "en_US"
-        return !(getEnabledKeyboardLocaleCount() <= 1 && getSystemLocale().getLanguage(
-                ).equalsIgnoreCase(getInputLocale().getLanguage()));
+        return mNeedsToDisplayLanguage;
     }
 
     public Locale getInputLocale() {
         if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
             return mLanguageSwitcher.getInputLocale();
+        } else {
+            return mInputLocale;
         }
-        // TODO: Implement for no legacy mode
-        return null;
     }
 
-    public String getInputLanguage() {
-        String inputLanguage = null;
+    public String getInputLocaleStr() {
         if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
+            String inputLanguage = null;
             inputLanguage = mLanguageSwitcher.getInputLanguage();
+            // Should return system locale if there is no Language available.
+            if (inputLanguage == null) {
+                inputLanguage = getSystemLocale().getLanguage();
+            }
+            return inputLanguage;
+        } else {
+            return mInputLocaleStr;
         }
-        // Should return system locale if there is no Language available.
-        if (inputLanguage == null) {
-            inputLanguage = getSystemLocale().getLanguage();
-        }
-        return inputLanguage;
     }
 
     public String[] getEnabledLanguages() {
         if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
             return mLanguageSwitcher.getEnabledLanguages();
+        } else {
+            return mEnabledLanguagesOfCurrentInputMethod.toArray(
+                    new String[mEnabledLanguagesOfCurrentInputMethod.size()]);
         }
-        // TODO: Implement for no legacy mode
-        return null;
     }
 
     public Locale getSystemLocale() {
-        return mSystemLocale;
+        if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
+            return mLanguageSwitcher.getSystemLocale();
+        } else {
+            return mSystemLocale;
+        }
     }
 
-    // TODO: Cache the value for faster processing.
-    public boolean isSystemLocaleSameAsInputLocale() {
-        // TODO: Takes care of two-char locale such as "en" in addition to "en_US"
-        return getSystemLocale().getLanguage().equalsIgnoreCase(
-                getInputLanguage().substring(0, 2));
+    public boolean isSystemLanguageSameAsInputLanguage() {
+        if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
+            return getSystemLocale().getLanguage().equalsIgnoreCase(
+                    getInputLocaleStr().substring(0, 2));
+        } else {
+            return mIsSystemLanguageSameAsInputLanguage;
+        }
     }
 
     public void onConfigurationChanged(Configuration conf) {
-        if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
-            // If the system locale changes and is different from the saved
-            // locale (mSystemLocale), then reload the input locale list from the
-            // latin ime settings (shared prefs) and reset the input locale
-            // to the first one.
-            final Locale systemLocale = conf.locale;
-            if (!TextUtils.equals(systemLocale.toString(), mSystemLocale.toString())) {
-                mSystemLocale = systemLocale;
-                mLanguageSwitcher.loadLocales(
-                        PreferenceManager.getDefaultSharedPreferences(mService));
+        final Locale systemLocale = conf.locale;
+        // If system configuration was changed, update all parameters.
+        if (!TextUtils.equals(systemLocale.toString(), mSystemLocale.toString())) {
+            if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
+                // If the system locale changes and is different from the saved
+                // locale (mSystemLocale), then reload the input locale list from the
+                // latin ime settings (shared prefs) and reset the input locale
+                // to the first one.
+                mLanguageSwitcher.loadLocales(PreferenceManager
+                        .getDefaultSharedPreferences(mService));
                 mLanguageSwitcher.setSystemLocale(systemLocale);
-                toggleLanguage(true, true);
+            } else {
+                updateAllParameters();
             }
-            return;
         }
     }
 
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
         if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
-            mLanguageSwitcher.loadLocales(sharedPreferences);
-            return;
+            if (LatinIME.PREF_SELECTED_LANGUAGES.equals(key)) {
+                mLanguageSwitcher.loadLocales(sharedPreferences);
+            }
         }
     }
 
+    /**
+     * Change system locale for this application
+     * @param newLocale
+     * @return oldLocale
+     */
     public Locale changeSystemLocale(Locale newLocale) {
         Configuration conf = mResources.getConfiguration();
         Locale oldLocale = conf.locale;
@@ -150,16 +269,25 @@
         return oldLocale;
     }
 
-    ////////////////////////////////////////////
-    // Legacy Language Switch support //
-    ////////////////////////////////////////////
+    //////////////////////////////////////
+    // SpaceBar Language Switch support //
+    //////////////////////////////////////
+
     private LanguageSwitcher mLanguageSwitcher;
 
-    public static String getLanguageName(Locale locale) {
+    public static String getFullDisplayName(Locale locale, boolean returnsNameInThisLocale) {
+        if (returnsNameInThisLocale) {
+            return toTitleCase(locale.getDisplayName(locale));
+        } else {
+            return toTitleCase(locale.getDisplayName());
+        }
+    }
+
+    public static String getDisplayLanguage(Locale locale) {
         return toTitleCase(locale.getDisplayLanguage(locale));
     }
 
-    public static String getShortLanguageName(Locale locale) {
+    public static String getShortDisplayLanguage(Locale locale) {
         return toTitleCase(locale.getLanguage());
     }
 
@@ -170,25 +298,34 @@
         return Character.toUpperCase(s.charAt(0)) + s.substring(1);
     }
 
+    private void updateForSpaceBarLanguageSwitch() {
+        // We need to update mNeedsToDisplayLanguage in onStartInputView because
+        // getEnabledKeyboardLocaleCount could have been changed.
+        mNeedsToDisplayLanguage = !(getEnabledKeyboardLocaleCount() <= 1
+                && getSystemLocale().getLanguage().equalsIgnoreCase(
+                        getInputLocale().getLanguage()));
+    }
+
     public String getInputLanguageName() {
-        return getLanguageName(getInputLocale());
+        return getDisplayLanguage(getInputLocale());
     }
 
     public String getNextInputLanguageName() {
         if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
-            return getLanguageName(mLanguageSwitcher.getNextInputLocale());
+            return getDisplayLanguage(mLanguageSwitcher.getNextInputLocale());
+        } else {
+            return "";
         }
-        return "";
     }
 
     public String getPreviousInputLanguageName() {
         if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
-            return getLanguageName(mLanguageSwitcher.getPrevInputLocale());
+            return getDisplayLanguage(mLanguageSwitcher.getPrevInputLocale());
+        } else {
+            return "";
         }
-        return "";
     }
 
-    // TODO: This can be an array of String
     // A list of locales which are supported by default for voice input, unless we get a
     // different list from Gservices.
     private static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES =
@@ -225,16 +362,18 @@
     }
 
     public void toggleLanguage(boolean reset, boolean next) {
-        if (reset) {
-            mLanguageSwitcher.reset();
-        } else {
-            if (next) {
-                mLanguageSwitcher.next();
+        if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
+            if (reset) {
+                mLanguageSwitcher.reset();
             } else {
-                mLanguageSwitcher.prev();
+                if (next) {
+                    mLanguageSwitcher.next();
+                } else {
+                    mLanguageSwitcher.prev();
+                }
             }
+            mLanguageSwitcher.persist();
         }
-        mLanguageSwitcher.persist();
     }
 
     private void initLanguageSwitcher(LatinIME service) {