Implement new design for settings of InputMethods

Change-Id: I82392e4a028abe2d588622a7e89fd035966f603a
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index b91c7ee..280f295 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -302,6 +302,7 @@
     public static class StorageSettingsActivity extends Settings { }
     public static class WifiSettingsActivity extends Settings { }
     public static class InputMethodAndLanguageSettingsActivity extends Settings { }
+    public static class InputMethodConfigActivity extends Settings { }
     public static class InputMethodAndSubtypeEnablerActivity extends Settings { }
     public static class LocalePickerActivity extends Settings { }
     public static class UserDictionarySettingsActivity extends Settings { }
diff --git a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
index ef59201..117bec5 100644
--- a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
+++ b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
@@ -26,127 +26,74 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceGroup;
 import android.preference.PreferenceScreen;
+import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
-public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment {
+public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
+        implements Preference.OnPreferenceChangeListener{
 
     private static final String KEY_PHONE_LANGUAGE = "phone_language";
-    private static final String KEY_INPUT_METHOD = "input_method";
-    private static final String KEY_KEYBOARD_SETTINGS_CATEGORY = "keyboard_settings_category";
-    private static final String KEY_HARDKEYBOARD_CATEGORY = "hardkeyboard_category";
-    private boolean mHaveHardKeyboard;
+    private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method";
+    private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector";
 
-    private List<InputMethodInfo> mInputMethodProperties;
-    private List<CheckBoxPreference> mCheckboxes;
+    private int mDefaultInputMethodSelectorVisibility = 0;
+    private ListPreference mShowInputMethodSelectorPref;
     private Preference mLanguagePref;
 
-    final TextUtils.SimpleStringSplitter mStringColonSplitter
-            = new TextUtils.SimpleStringSplitter(':');
-
-    private AlertDialog mDialog = null;
-    
-    static public String getInputMethodIdFromKey(String key) {
-        return key;
-    }
-
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
         addPreferencesFromResource(R.xml.language_settings);
 
+        try {
+            mDefaultInputMethodSelectorVisibility = Integer.valueOf(
+                    getString(R.string.input_method_selector_visibility_default_value));
+        } catch (NumberFormatException e) {
+        }
+
         if (getActivity().getAssets().getLocales().length == 1) {
-            getPreferenceScreen().
-                removePreference(findPreference(KEY_PHONE_LANGUAGE));
+            // No "Select language" pref if there's only one system locale available.
+            getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE));
         } else {
             mLanguagePref = findPreference(KEY_PHONE_LANGUAGE);
         }
-
-        Configuration config = getResources().getConfiguration();
-        if (config.keyboard != Configuration.KEYBOARD_QWERTY) {
-            getPreferenceScreen().removePreference(
-                    getPreferenceScreen().findPreference(KEY_HARDKEYBOARD_CATEGORY));
-        } else {
-            mHaveHardKeyboard = true;
-        }
-        mCheckboxes = new ArrayList<CheckBoxPreference>();
-        onCreateIMM();
+        mShowInputMethodSelectorPref = (ListPreference)findPreference(
+                KEY_INPUT_METHOD_SELECTOR);
+        mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
+        // TODO: Update current input method name on summary
+        updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility());
 
         new VoiceInputOutputSettings(this).onCreate();
     }
 
-    private boolean isSystemIme(InputMethodInfo property) {
-        return (property.getServiceInfo().applicationInfo.flags
-                & ApplicationInfo.FLAG_SYSTEM) != 0;
-    }
-
-    private void onCreateIMM() {
-        InputMethodManager imm = (InputMethodManager) getSystemService(
-                Context.INPUT_METHOD_SERVICE);
-
-        mInputMethodProperties = imm.getInputMethodList();
-
-        PreferenceGroup keyboardSettingsCategory = (PreferenceGroup) findPreference(
-                KEY_KEYBOARD_SETTINGS_CATEGORY);
-        
-        int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties
-                .size());
-        for (int i = 0; i < N; ++i) {
-            InputMethodInfo property = mInputMethodProperties.get(i);
-            String prefKey = property.getId();
-
-            CharSequence label = property.loadLabel(getActivity().getPackageManager());
-            boolean systemIME = isSystemIme(property);
-            // Add a check box.
-            // Don't show the toggle if it's the only keyboard in the system, or it's a system IME.
-            if (mHaveHardKeyboard || (N > 1 && !systemIME)) {
-                CheckBoxPreference chkbxPref = new CheckBoxPreference(getActivity());
-                chkbxPref.setKey(prefKey);
-                chkbxPref.setTitle(label);
-                keyboardSettingsCategory.addPreference(chkbxPref);
-                mCheckboxes.add(chkbxPref);
-            }
-
-            // If setting activity is available, add a setting screen entry.
-            if (null != property.getSettingsActivity()) {
-                PreferenceScreen prefScreen = new PreferenceScreen(getActivity(), null);
-                String settingsActivity = property.getSettingsActivity();
-                if (settingsActivity.lastIndexOf("/") < 0) {
-                    settingsActivity = property.getPackageName() + "/" + settingsActivity;
-                }
-                prefScreen.setKey(settingsActivity);
-                prefScreen.setTitle(label);
-                if (N == 1) {
-                    prefScreen.setSummary(getResources().getString(
-                            R.string.onscreen_keyboard_settings_summary));
-                } else {
-                    CharSequence settingsLabel = getResources().getString(
-                            R.string.input_methods_settings_label_format, label);
-                    prefScreen.setSummary(settingsLabel);
-                }
-                keyboardSettingsCategory.addPreference(prefScreen);
-            }
+    private void updateInputMethodSelectorSummary(int value) {
+        String[] inputMethodSelectorTitles = getResources().getStringArray(
+                R.array.input_method_selector_titles);
+        if (inputMethodSelectorTitles.length > value) {
+            mShowInputMethodSelectorPref.setSummary(inputMethodSelectorTitles[value]);
+            mShowInputMethodSelectorPref.setValue(String.valueOf(value));
         }
     }
 
     @Override
     public void onResume() {
         super.onResume();
-
-        InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
-                this, getContentResolver(), mInputMethodProperties);
-
         if (mLanguagePref != null) {
             Configuration conf = getResources().getConfiguration();
             String locale = conf.locale.getDisplayName(conf.locale);
@@ -155,107 +102,58 @@
                 mLanguagePref.setSummary(locale);
             }
         }
+
+        mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
     }
 
     @Override
     public void onPause() {
         super.onPause();
-        InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
-                mInputMethodProperties, mHaveHardKeyboard);
+        mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null);
     }
 
     @Override
     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
-
         // Input Method stuff
         if (Utils.isMonkeyRunning()) {
             return false;
         }
-
-        if (preference instanceof CheckBoxPreference) {
-            final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
-            final String id = getInputMethodIdFromKey(chkPref.getKey());
-            if (chkPref.isChecked()) {
-                InputMethodInfo selImi = null;
-                final int N = mInputMethodProperties.size();
-                for (int i=0; i<N; i++) {
-                    InputMethodInfo imi = mInputMethodProperties.get(i);
-                    if (id.equals(imi.getId())) {
-                        selImi = imi;
-                        if (isSystemIme(imi)) {
-                            // This is a built-in IME, so no need to warn.
-                            return super.onPreferenceTreeClick(preferenceScreen, preference);
-                        }
-                    }
-                }
-                chkPref.setChecked(false);
-                if (selImi == null) {
-                    return super.onPreferenceTreeClick(preferenceScreen, preference);
-                }
-                if (mDialog == null) {
-                    // TODO: DialogFragment?
-                    mDialog = (new AlertDialog.Builder(getActivity()))
-                            .setTitle(android.R.string.dialog_alert_title)
-                            .setIcon(android.R.drawable.ic_dialog_alert)
-                            .setCancelable(true)
-                            .setPositiveButton(android.R.string.ok,
-                                    new DialogInterface.OnClickListener() {
-                                        public void onClick(DialogInterface dialog, int which) {
-                                            chkPref.setChecked(true);
-                                        }
-
-                            })
-                            .setNegativeButton(android.R.string.cancel,
-                                    new DialogInterface.OnClickListener() {
-                                        public void onClick(DialogInterface dialog, int which) {
-                                        }
-
-                            })
-                            .create();
-                } else {
-                    if (mDialog.isShowing()) {
-                        mDialog.dismiss();
-                    }
-                }
-                mDialog.setMessage(getResources().getString(
-                        R.string.ime_security_warning,
-                        selImi.getServiceInfo().applicationInfo.loadLabel(getPackageManager())));
-                mDialog.show();
-            }
-        } else if (preference instanceof PreferenceScreen) {
+        if (preference instanceof PreferenceScreen) {
             if (preference.getFragment() != null) {
                 // Fragment will be handled correctly by the super class.
-            } else if (KEY_INPUT_METHOD.equals(preference.getKey())) {
+            } else if (KEY_CURRENT_INPUT_METHOD.equals(preference.getKey())) {
                 final InputMethodManager imm = (InputMethodManager)
                         getSystemService(Context.INPUT_METHOD_SERVICE);
                 imm.showInputMethodPicker();
-            } else if (preference.getIntent() == null) {
-                PreferenceScreen pref = (PreferenceScreen) preference;
-                String activityName = pref.getKey();
-                String packageName = activityName.substring(0, activityName
-                        .lastIndexOf("."));
-                int slash = activityName.indexOf("/");
-                if (slash > 0) {
-                    packageName = activityName.substring(0, slash);
-                    activityName = activityName.substring(slash + 1);
-                }
-                if (activityName.length() > 0) {
-                    Intent i = new Intent(Intent.ACTION_MAIN);
-                    i.setClassName(packageName, activityName);
-                    startActivity(i);
-                }
             }
         }
         return super.onPreferenceTreeClick(preferenceScreen, preference);
     }
 
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        if (mDialog != null) {
-            mDialog.dismiss();
-            mDialog = null;
+    private void saveInputMethodSelectorVisibility(String value) {
+        try {
+            int intValue = Integer.valueOf(value);
+            Settings.Secure.putInt(getContentResolver(),
+                    Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, intValue);
+            updateInputMethodSelectorSummary(intValue);
+        } catch(NumberFormatException e) {
         }
     }
 
+    private int loadInputMethodSelectorVisibility() {
+        return Settings.Secure.getInt(getContentResolver(),
+                Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY,
+                mDefaultInputMethodSelectorVisibility);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object value) {
+        if (preference == mShowInputMethodSelectorPref) {
+            if (value instanceof String) {
+                saveInputMethodSelectorVisibility((String)value);
+            }
+        }
+        return false;
+    }
+
 }
diff --git a/src/com/android/settings/inputmethod/InputMethodAndSubtypeEnabler.java b/src/com/android/settings/inputmethod/InputMethodAndSubtypeEnabler.java
index cb90514..c2c0a07 100644
--- a/src/com/android/settings/inputmethod/InputMethodAndSubtypeEnabler.java
+++ b/src/com/android/settings/inputmethod/InputMethodAndSubtypeEnabler.java
@@ -29,26 +29,32 @@
 import android.preference.Preference;
 import android.preference.PreferenceCategory;
 import android.preference.PreferenceScreen;
+import android.text.TextUtils;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 public class InputMethodAndSubtypeEnabler extends SettingsPreferenceFragment {
 
-    private boolean mHaveHardKeyboard;
-
-    private List<InputMethodInfo> mInputMethodProperties;
+    public static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
 
     private AlertDialog mDialog = null;
+    private boolean mHaveHardKeyboard;
+    final private HashMap<String, List<Preference>> mInputMethodPrefsMap =
+        new HashMap<String, List<Preference>>();
+    private List<InputMethodInfo> mInputMethodProperties;
+    private String mInputMethodId;
 
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
         Configuration config = getResources().getConfiguration();
         mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY);
+        mInputMethodId = getActivity().getIntent().getStringExtra(EXTRA_INPUT_METHOD_ID);
         onCreateIMM();
         setPreferenceScreen(createPreferenceHierarchy());
     }
@@ -57,7 +63,7 @@
     public void onResume() {
         super.onResume();
         InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
-                this, getContentResolver(), mInputMethodProperties);
+                this, getContentResolver(), mInputMethodProperties, mInputMethodPrefsMap);
     }
 
     @Override
@@ -156,53 +162,48 @@
         PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity());
 
         int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties.size());
-        // TODO: Use iterator.
+
         for (int i = 0; i < N; ++i) {
+            final InputMethodInfo imi = mInputMethodProperties.get(i);
+            if (imi.getSubtypes().size() <= 1) continue;
+            String imiId = imi.getId();
+            // Add to this subtype the list when no IME is specified or when the IME of this
+            // subtype is the specified IME.
+            if (!TextUtils.isEmpty(mInputMethodId) && !mInputMethodId.equals(imiId)) {
+                continue;
+            }
             PreferenceCategory keyboardSettingsCategory = new PreferenceCategory(getActivity());
             root.addPreference(keyboardSettingsCategory);
-            InputMethodInfo property = mInputMethodProperties.get(i);
-            String prefKey = property.getId();
 
             PackageManager pm = getPackageManager();
-            CharSequence label = property.loadLabel(pm);
-            boolean systemIME = InputMethodAndSubtypeUtil.isSystemIme(property);
+            CharSequence label = imi.loadLabel(pm);
 
-            keyboardSettingsCategory.setTitle(label);
+            keyboardSettingsCategory.setTitle(getResources().getString(
+                    R.string.input_methods_and_subtype_enabler_title_format, label));
+            keyboardSettingsCategory.setKey(imiId);
 
-            // Add a check box.
-            // Don't show the toggle if it's the only keyboard in the system, or it's a system IME.
-            if (mHaveHardKeyboard || (N > 1 && !systemIME)) {
-                CheckBoxPreference chkbxPref = new CheckBoxPreference(getActivity());
-                chkbxPref.setKey(prefKey);
-                chkbxPref.setTitle(label);
-                keyboardSettingsCategory.addPreference(chkbxPref);
-            }
-
-            ArrayList<InputMethodSubtype> subtypes = property.getSubtypes();
+            ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+            ArrayList<Preference> subtypePreferences = new ArrayList<Preference>();
             if (subtypes.size() > 0) {
-                PreferenceCategory subtypesCategory = new PreferenceCategory(getActivity());
-                subtypesCategory.setTitle(getResources().getString(
-                        R.string.input_methods_and_subtype_enabler_title_format, label));
-                root.addPreference(subtypesCategory);
                 for (InputMethodSubtype subtype: subtypes) {
                     CharSequence subtypeLabel;
                     int nameResId = subtype.getNameResId();
                     if (nameResId != 0) {
-                        subtypeLabel = pm.getText(property.getPackageName(), nameResId,
-                                property.getServiceInfo().applicationInfo);
+                        subtypeLabel = pm.getText(imi.getPackageName(), nameResId,
+                                imi.getServiceInfo().applicationInfo);
                     } else {
                         String mode = subtype.getMode();
                         CharSequence language = subtype.getLocale();
-                        // TODO: Use more friendly Title and UI
                         subtypeLabel = (mode == null ? "" : mode) + ","
                                 + (language == null ? "" : language);
                     }
                     CheckBoxPreference chkbxPref = new CheckBoxPreference(getActivity());
-                    chkbxPref.setKey(prefKey + subtype.hashCode());
+                    chkbxPref.setKey(imiId + subtype.hashCode());
                     chkbxPref.setTitle(subtypeLabel);
-                    chkbxPref.setSummary(label);
-                    subtypesCategory.addPreference(chkbxPref);
+                    keyboardSettingsCategory.addPreference(chkbxPref);
+                    subtypePreferences.add(chkbxPref);
                 }
+                mInputMethodPrefsMap.put(imiId, subtypePreferences);
             }
         }
         return root;
diff --git a/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java b/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java
index 288e6c2..460bc9c 100644
--- a/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java
+++ b/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java
@@ -21,6 +21,7 @@
 import android.content.ContentResolver;
 import android.content.pm.ApplicationInfo;
 import android.preference.CheckBoxPreference;
+import android.preference.Preference;
 import android.preference.PreferenceScreen;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
@@ -32,6 +33,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 
 public class InputMethodAndSubtypeUtil {
 
@@ -48,6 +50,42 @@
     private static final TextUtils.SimpleStringSplitter sStringInputMethodSubtypeSplitter
             = new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER);
 
+    private static void buildEnabledInputMethodsString(
+            StringBuilder builder, String imi, HashSet<String> subtypes) {
+        builder.append(imi);
+        // Inputmethod and subtypes are saved in the settings as follows:
+        // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
+        for (String subtypeId: subtypes) {
+            builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
+        }
+    }
+
+    public static void buildInputMethodsAndSubtypesString(
+            StringBuilder builder, HashMap<String, HashSet<String>> imsList) {
+        boolean needsAppendSeparator = false;
+        for (String imi: imsList.keySet()) {
+            if (needsAppendSeparator) {
+                builder.append(INPUT_METHOD_SEPARATER);
+            } else {
+                needsAppendSeparator = true;
+            }
+            buildEnabledInputMethodsString(builder, imi, imsList.get(imi));
+        }
+    }
+
+    public static void buildDisabledSystemInputMethods(
+            StringBuilder builder, HashSet<String> imes) {
+        boolean needsAppendSeparator = false;
+        for (String ime: imes) {
+            if (needsAppendSeparator) {
+                builder.append(INPUT_METHOD_SEPARATER);
+            } else {
+                needsAppendSeparator = true;
+            }
+            builder.append(ime);
+        }
+    }
+
     private static int getInputMethodSubtypeSelected(ContentResolver resolver) {
         try {
             return Settings.Secure.getInt(resolver,
@@ -96,82 +134,117 @@
         return imsList;
     }
 
-    public static void saveInputMethodSubtypeList(
-            SettingsPreferenceFragment context, ContentResolver resolver,
-            List<InputMethodInfo> inputMethodProperties, boolean hasHardKeyboard) {
+    private static HashSet<String> getDisabledSystemIMEs(ContentResolver resolver) {
+        HashSet<String> set = new HashSet<String>();
+        String disabledIMEsStr = Settings.Secure.getString(
+                resolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS);
+        sStringInputMethodSplitter.setString(disabledIMEsStr);
+        while(sStringInputMethodSplitter.hasNext()) {
+            set.add(sStringInputMethodSplitter.next());
+        }
+        return set;
+    }
+
+    public static void saveInputMethodSubtypeList(SettingsPreferenceFragment context,
+            ContentResolver resolver, List<InputMethodInfo> inputMethodInfos,
+            boolean hasHardKeyboard) {
         String currentInputMethodId = Settings.Secure.getString(resolver,
                 Settings.Secure.DEFAULT_INPUT_METHOD);
         final int selectedInputMethodSubtype = getInputMethodSubtypeSelected(resolver);
+        HashMap<String, HashSet<String>> enabledIMEAndSubtypesMap =
+                getEnabledInputMethodsAndSubtypeList(resolver);
+        HashSet<String> disabledSystemIMEs = getDisabledSystemIMEs(resolver);
 
-        StringBuilder builder = new StringBuilder();
-        StringBuilder disabledSysImes = new StringBuilder();
-        int firstSubtypeHashCode = NOT_A_SUBTYPE_ID;
-
-        final boolean onlyOneIME = inputMethodProperties.size() == 1;
-        boolean existsSelectedIME = false;
-        for (InputMethodInfo property : inputMethodProperties) {
-            final String id = property.getId();
-            CheckBoxPreference pref = (CheckBoxPreference) context.findPreference(id);
-            boolean isCurrentInputMethod = id.equals(currentInputMethodId);
-            boolean systemIme = isSystemIme(property);
-            // TODO: Append subtypes by using the separator ";"
-            if (((onlyOneIME || systemIme) && !hasHardKeyboard)
-                    || (pref != null && pref.isChecked())) {
-                if (builder.length() > 0) builder.append(INPUT_METHOD_SEPARATER);
-                builder.append(id);
-                for (InputMethodSubtype subtype : property.getSubtypes()) {
+        final boolean onlyOneIME = inputMethodInfos.size() == 1;
+        boolean existsSelectedSubtype = false;
+        for (InputMethodInfo imi : inputMethodInfos) {
+            final String imiId = imi.getId();
+            Preference pref = context.findPreference(imiId);
+            if (pref == null) continue;
+            // In the Configure input method screen or in the subtype enabler screen.
+            // pref is instance of CheckBoxPreference in the Configure input method screen.
+            final boolean isImeChecked = (pref instanceof CheckBoxPreference) ?
+                    ((CheckBoxPreference) pref).isChecked()
+                            : enabledIMEAndSubtypesMap.containsKey(imiId);
+            boolean isCurrentInputMethod = imiId.equals(currentInputMethodId);
+            boolean systemIme = isSystemIme(imi);
+            if (((onlyOneIME || systemIme) && !hasHardKeyboard) || isImeChecked) {
+                if (!enabledIMEAndSubtypesMap.containsKey(imiId)) {
+                    // imiId has just been enabled
+                    enabledIMEAndSubtypesMap.put(imiId, new HashSet<String>());
+                }
+                HashSet<String> subtypesSet = enabledIMEAndSubtypesMap.get(imiId);
+                for (InputMethodSubtype subtype : imi.getSubtypes()) {
+                    final String subtypeHashCodeStr = String.valueOf(subtype.hashCode());
                     CheckBoxPreference subtypePref = (CheckBoxPreference) context.findPreference(
-                            id + subtype.hashCode());
-                    if (subtypePref != null && subtypePref.isChecked()) {
-                        builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtype.hashCode());
+                            imiId + subtypeHashCodeStr);
+                    // In the Configure input method screen which does not have subtype preferences.
+                    if (subtypePref == null) continue;
+                    if (subtypePref.isChecked()) {
+                        subtypesSet.add(subtypeHashCodeStr);
                         if (isCurrentInputMethod) {
                             if (selectedInputMethodSubtype == subtype.hashCode()) {
-                                existsSelectedIME = true;
-                            } else if (firstSubtypeHashCode == NOT_A_SUBTYPE_ID) {
-                                firstSubtypeHashCode = subtype.hashCode();
+                                existsSelectedSubtype = true;
                             }
                         }
+                    } else {
+                        subtypesSet.remove(subtypeHashCodeStr);
                     }
                 }
-            } else if (isCurrentInputMethod) {
-                // We are processing the current input method, but found that it's not enabled.
-                // This means that the current input method has been uninstalled.
-                // If currentInputMethod is already uninstalled, InputMethodManagerService will
-                // find the applicable IME from the history and the system locale.
-                if (DEBUG) {
-                    Log.d(TAG, "Current IME was uninstalled or disabled.");
+            } else {
+                enabledIMEAndSubtypesMap.remove(imiId);
+                if (isCurrentInputMethod) {
+                    // We are processing the current input method, but found that it's not enabled.
+                    // This means that the current input method has been uninstalled.
+                    // If currentInputMethod is already uninstalled, InputMethodManagerService will
+                    // find the applicable IME from the history and the system locale.
+                    if (DEBUG) {
+                        Log.d(TAG, "Current IME was uninstalled or disabled.");
+                    }
                 }
             }
             // If it's a disabled system ime, add it to the disabled list so that it
             // doesn't get enabled automatically on any changes to the package list
-            if (pref != null && !pref.isChecked() && systemIme && hasHardKeyboard) {
-                if (disabledSysImes.length() > 0) disabledSysImes.append(INPUT_METHOD_SEPARATER);
-                disabledSysImes.append(id);
+            if (systemIme && hasHardKeyboard) {
+                if (disabledSystemIMEs.contains(imiId)) {
+                    if (isImeChecked) {
+                        disabledSystemIMEs.remove(imiId);
+                    }
+                } else {
+                    if (!isImeChecked) {
+                        disabledSystemIMEs.add(imiId);
+                    }
+                }
             }
         }
 
+        StringBuilder builder = new StringBuilder();
+        buildInputMethodsAndSubtypesString(builder, enabledIMEAndSubtypesMap);
+        StringBuilder disabledSysImesBuilder = new StringBuilder();
+        buildDisabledSystemInputMethods(disabledSysImesBuilder, disabledSystemIMEs);
         if (DEBUG) {
             Log.d(TAG, "--- Save enabled inputmethod settings. :" + builder.toString());
             Log.d(TAG, "--- Save disable system inputmethod settings. :"
-                    + disabledSysImes.toString());
+                    + disabledSysImesBuilder.toString());
             Log.d(TAG, "--- Save default inputmethod settings. :" + currentInputMethodId);
         }
 
         // Redefines SelectedSubtype when all subtypes are unchecked or there is no subtype
         // selected. And if the selected subtype of the current input method was disabled,
         // We should reset the selected input method's subtype.
-        if (!existsSelectedIME || !isInputMethodSubtypeSelected(resolver)) {
+        if (!existsSelectedSubtype || !isInputMethodSubtypeSelected(resolver)) {
             if (DEBUG) {
-                Log.d(TAG, "--- Set inputmethod subtype because it's not defined."
-                        + firstSubtypeHashCode);
+                Log.d(TAG, "--- Reset inputmethod subtype because it's not defined.");
             }
-            putSelectedInputMethodSubtype(resolver, firstSubtypeHashCode);
+            putSelectedInputMethodSubtype(resolver, NOT_A_SUBTYPE_ID);
         }
 
         Settings.Secure.putString(resolver,
                 Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
-        Settings.Secure.putString(resolver,
-                Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS, disabledSysImes.toString());
+        if (disabledSysImesBuilder.length() > 0) {
+            Settings.Secure.putString(resolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
+                    disabledSysImesBuilder.toString());
+        }
         // If the current input method is unset, InputMethodManagerService will find the applicable
         // IME from the history and the system locale.
         Settings.Secure.putString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD,
@@ -180,18 +253,25 @@
 
     public static void loadInputMethodSubtypeList(
             SettingsPreferenceFragment context, ContentResolver resolver,
-            List<InputMethodInfo> inputMethodProperties) {
+            List<InputMethodInfo> inputMethodInfos,
+            final Map<String, List<Preference>> inputMethodPrefsMap) {
         HashMap<String, HashSet<String>> enabledSubtypes =
-                getEnabledInputMethodsAndSubtypeList(resolver);
+            getEnabledInputMethodsAndSubtypeList(resolver);
 
-        for (InputMethodInfo property : inputMethodProperties) {
-            final String id = property.getId();
-            CheckBoxPreference pref = (CheckBoxPreference) context.findPreference(id);
-            if (pref != null) {
-                boolean isEnabled = enabledSubtypes.containsKey(id);
-                pref.setChecked(isEnabled);
-                setSubtypesPreferenceEnabled(context, inputMethodProperties, id, isEnabled);
-                updateSubtypesPreferenceChecked(context, inputMethodProperties, enabledSubtypes);
+        for (InputMethodInfo imi : inputMethodInfos) {
+            final String imiId = imi.getId();
+            Preference pref = context.findPreference(imiId);
+            if (pref != null && pref instanceof CheckBoxPreference) {
+                CheckBoxPreference checkBoxPreference = (CheckBoxPreference) pref;
+                boolean isEnabled = enabledSubtypes.containsKey(imiId);
+                checkBoxPreference.setChecked(isEnabled);
+                if (inputMethodPrefsMap != null) {
+                    for (Preference childPref: inputMethodPrefsMap.get(imiId)) {
+                        childPref.setEnabled(isEnabled);
+                    }
+                }
+                setSubtypesPreferenceEnabled(context, inputMethodInfos, imiId, isEnabled);
+                updateSubtypesPreferenceChecked(context, inputMethodInfos, enabledSubtypes);
             }
         }
     }
diff --git a/src/com/android/settings/inputmethod/InputMethodConfig.java b/src/com/android/settings/inputmethod/InputMethodConfig.java
new file mode 100644
index 0000000..640a773
--- /dev/null
+++ b/src/com/android/settings/inputmethod/InputMethodConfig.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2010 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.settings.inputmethod;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class InputMethodConfig extends SettingsPreferenceFragment {
+
+    private static final String KEY_PHYSICALKEYBOARD_CATEGORY = "hardkeyboard_category";
+    private static final String PHYSICALKEYBOARD_SETTINGS_FRAGMENT
+            = "com.android.settings.PhysicalKeyboardSettings";
+
+    private AlertDialog mDialog = null;
+    private boolean mHaveHardKeyboard;
+    // Map of imi and its preferences
+    final private HashMap<String, List<Preference>> mInputMethodPrefsMap =
+        new HashMap<String, List<Preference>>();
+    private List<InputMethodInfo> mInputMethodProperties;
+
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        Configuration config = getResources().getConfiguration();
+        mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY);
+        InputMethodManager imm = (InputMethodManager) getSystemService(
+                Context.INPUT_METHOD_SERVICE);
+
+        // TODO: Change mInputMethodProperties to Map
+        mInputMethodProperties = imm.getInputMethodList();
+        setPreferenceScreen(createPreferenceHierarchy());
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
+                this, getContentResolver(), mInputMethodProperties, mInputMethodPrefsMap);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
+                mInputMethodProperties, mHaveHardKeyboard);
+    }
+
+    private void showSecurityWarnDialog(InputMethodInfo imi, final CheckBoxPreference chkPref,
+            final String imiId) {
+        if (mDialog == null) {
+            mDialog = (new AlertDialog.Builder(getActivity()))
+                    .setTitle(android.R.string.dialog_alert_title)
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setCancelable(true)
+                    .setPositiveButton(android.R.string.ok,
+                            new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {
+                            chkPref.setChecked(true);
+                            for (Preference pref: mInputMethodPrefsMap.get(imiId)) {
+                                pref.setEnabled(true);
+                            }
+                        }
+                    })
+                    .setNegativeButton(android.R.string.cancel,
+                            new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {
+                        }
+                    })
+                    .create();
+        } else {
+            if (mDialog.isShowing()) {
+                mDialog.dismiss();
+            }
+        }
+        mDialog.setMessage(getResources().getString(R.string.ime_security_warning,
+                imi.getServiceInfo().applicationInfo.loadLabel(getPackageManager())));
+        mDialog.show();
+    }
+
+    private InputMethodInfo getInputMethodInfoFromImiId(String imiId) {
+        final int N = mInputMethodProperties.size();
+        for (int i = 0; i < N; ++i) {
+            InputMethodInfo imi = mInputMethodProperties.get(i);
+            if (imiId.equals(imi.getId())) {
+                return imi;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(
+            PreferenceScreen preferenceScreen, Preference preference) {
+
+        if (preference instanceof CheckBoxPreference) {
+            final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
+            final String imiId = chkPref.getKey();
+            if (chkPref.isChecked()) {
+                InputMethodInfo selImi = getInputMethodInfoFromImiId(imiId);
+                if (selImi != null) {
+                    if (InputMethodAndSubtypeUtil.isSystemIme(selImi)) {
+                        // This is a built-in IME, so no need to warn.
+                        return super.onPreferenceTreeClick(preferenceScreen, preference);
+                    }
+                } else {
+                    return super.onPreferenceTreeClick(preferenceScreen, preference);
+                }
+                chkPref.setChecked(false);
+                showSecurityWarnDialog(selImi, chkPref, imiId);
+            } else {
+                for (Preference pref: mInputMethodPrefsMap.get(imiId)) {
+                    pref.setEnabled(false);
+                }
+            }
+        }
+        return super.onPreferenceTreeClick(preferenceScreen, preference);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mDialog != null) {
+            mDialog.dismiss();
+            mDialog = null;
+        }
+    }
+
+    private void addHardKeyboardPreference(PreferenceScreen root) {
+        PreferenceCategory keyboardSettingsCategory = new PreferenceCategory(getActivity());
+        keyboardSettingsCategory.setTitle(R.string.builtin_keyboard_settings_title);
+        root.addPreference(keyboardSettingsCategory);
+        PreferenceScreen prefScreen = new PreferenceScreen(getActivity(), null);
+        prefScreen.setKey(KEY_PHYSICALKEYBOARD_CATEGORY);
+        prefScreen.setTitle(R.string.builtin_keyboard_settings_title);
+        prefScreen.setSummary(R.string.builtin_keyboard_settings_summary);
+        prefScreen.setFragment(PHYSICALKEYBOARD_SETTINGS_FRAGMENT);
+    }
+
+    private void addInputMethodPreference(PreferenceScreen root, InputMethodInfo imi,
+            final int imiSize) {
+        PreferenceCategory keyboardSettingsCategory = new PreferenceCategory(getActivity());
+        root.addPreference(keyboardSettingsCategory);
+        final String imiId = imi.getId();
+        mInputMethodPrefsMap.put(imiId, new ArrayList<Preference>());
+
+        PackageManager pm = getPackageManager();
+        CharSequence label = imi.loadLabel(pm);
+        keyboardSettingsCategory.setTitle(label);
+
+        final boolean isSystemIME = InputMethodAndSubtypeUtil.isSystemIme(imi);
+        // Add a check box for enabling/disabling IME
+        CheckBoxPreference chkbxPref = new CheckBoxPreference(getActivity());
+        chkbxPref.setKey(imiId);
+        chkbxPref.setTitle(label);
+        keyboardSettingsCategory.addPreference(chkbxPref);
+        // Disable the toggle if it's the only keyboard in the system, or it's a system IME.
+        if (!mHaveHardKeyboard && (imiSize <= 1 || isSystemIME)) {
+            chkbxPref.setEnabled(false);
+        }
+
+        Intent intent;
+        // Add subtype settings when this IME has two or more subtypes.
+        PreferenceScreen prefScreen = new PreferenceScreen(getActivity(), null);
+        prefScreen.setTitle(R.string.active_input_method_subtypes);
+        if (imi.getSubtypes().size() > 1) {
+            intent = new Intent(Settings.ACTION_INPUT_METHOD_AND_SUBTYPE_ENABLER);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                    | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            intent.putExtra(InputMethodAndSubtypeEnabler.EXTRA_INPUT_METHOD_ID, imiId);
+            prefScreen.setIntent(intent);
+            keyboardSettingsCategory.addPreference(prefScreen);
+            mInputMethodPrefsMap.get(imiId).add(prefScreen);
+        }
+
+        // Add IME settings
+        String settingsActivity = imi.getSettingsActivity();
+        if (!TextUtils.isEmpty(settingsActivity)) {
+            prefScreen = new PreferenceScreen(getActivity(), null);
+            prefScreen.setTitle(R.string.input_method_settings);
+            intent = new Intent(Intent.ACTION_MAIN);
+            intent.setClassName(imi.getPackageName(), settingsActivity);
+            prefScreen.setIntent(intent);
+            keyboardSettingsCategory.addPreference(prefScreen);
+            mInputMethodPrefsMap.get(imiId).add(prefScreen);
+        }
+    }
+
+    private PreferenceScreen createPreferenceHierarchy() {
+        // Root
+        PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity());
+        if (mHaveHardKeyboard) {
+            addHardKeyboardPreference(root);
+        }
+
+        final int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties.size());
+        for (int i = 0; i < N; ++i) {
+            addInputMethodPreference(root, mInputMethodProperties.get(i), N);
+        }
+        return root;
+    }
+}