InputMethodAndSubtypeEnabler.java revision cabde98bcddc2c609a5929b019ebe73f1a68e004
14a5f889f80b683446e498f244d0eadfd979ca5d0satok/*
24a5f889f80b683446e498f244d0eadfd979ca5d0satok * Copyright (C) 2010 The Android Open Source Project
34a5f889f80b683446e498f244d0eadfd979ca5d0satok *
44a5f889f80b683446e498f244d0eadfd979ca5d0satok * Licensed under the Apache License, Version 2.0 (the "License");
54a5f889f80b683446e498f244d0eadfd979ca5d0satok * you may not use this file except in compliance with the License.
64a5f889f80b683446e498f244d0eadfd979ca5d0satok * You may obtain a copy of the License at
74a5f889f80b683446e498f244d0eadfd979ca5d0satok *
84a5f889f80b683446e498f244d0eadfd979ca5d0satok *      http://www.apache.org/licenses/LICENSE-2.0
94a5f889f80b683446e498f244d0eadfd979ca5d0satok *
104a5f889f80b683446e498f244d0eadfd979ca5d0satok * Unless required by applicable law or agreed to in writing, software
114a5f889f80b683446e498f244d0eadfd979ca5d0satok * distributed under the License is distributed on an "AS IS" BASIS,
124a5f889f80b683446e498f244d0eadfd979ca5d0satok * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134a5f889f80b683446e498f244d0eadfd979ca5d0satok * See the License for the specific language governing permissions and
144a5f889f80b683446e498f244d0eadfd979ca5d0satok * limitations under the License.
154a5f889f80b683446e498f244d0eadfd979ca5d0satok */
164a5f889f80b683446e498f244d0eadfd979ca5d0satok
170417e4094713c5f4dac700b645000d0959bf62fasatokpackage com.android.settings.inputmethod;
180417e4094713c5f4dac700b645000d0959bf62fasatok
19993f6ecf4065501433271b5fd5daf21a6f3ae586Satoshi Kataokaimport com.android.internal.inputmethod.InputMethodUtils;
200417e4094713c5f4dac700b645000d0959bf62fasatokimport com.android.settings.R;
210417e4094713c5f4dac700b645000d0959bf62fasatokimport com.android.settings.SettingsPreferenceFragment;
224a5f889f80b683446e498f244d0eadfd979ca5d0satok
234a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.app.AlertDialog;
244a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.content.Context;
254a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.content.DialogInterface;
265e9c5e7bcfbdc3d61f80bc95d16a7d57a362bc39satokimport android.content.Intent;
274a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.content.pm.PackageManager;
284a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.content.res.Configuration;
294a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.os.Bundle;
304a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.preference.CheckBoxPreference;
314a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.preference.Preference;
324a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.preference.PreferenceCategory;
334a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.preference.PreferenceScreen;
34c88a7ff1efd10374974e45768bde1658cc1d8483satokimport android.text.TextUtils;
3594bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatokimport android.util.Log;
364a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.view.inputmethod.InputMethodInfo;
374a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.view.inputmethod.InputMethodManager;
384a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.view.inputmethod.InputMethodSubtype;
394a5f889f80b683446e498f244d0eadfd979ca5d0satok
40ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataokaimport java.text.Collator;
414a5f889f80b683446e498f244d0eadfd979ca5d0satokimport java.util.ArrayList;
42ae70ee49492bd89c949946562b43a75f3c81b0adsatokimport java.util.Collections;
43ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataokaimport java.util.Comparator;
44c88a7ff1efd10374974e45768bde1658cc1d8483satokimport java.util.HashMap;
454a5f889f80b683446e498f244d0eadfd979ca5d0satokimport java.util.List;
46ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataokaimport java.util.Locale;
474a5f889f80b683446e498f244d0eadfd979ca5d0satok
484a5f889f80b683446e498f244d0eadfd979ca5d0satokpublic class InputMethodAndSubtypeEnabler extends SettingsPreferenceFragment {
4994bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok    private static final String TAG =InputMethodAndSubtypeEnabler.class.getSimpleName();
504a5f889f80b683446e498f244d0eadfd979ca5d0satok    private AlertDialog mDialog = null;
51c88a7ff1efd10374974e45768bde1658cc1d8483satok    private boolean mHaveHardKeyboard;
521c58c15e7a84b582b7d4d20c26824cabce80bce9satok    final private HashMap<String, List<Preference>> mInputMethodAndSubtypePrefsMap =
531c58c15e7a84b582b7d4d20c26824cabce80bce9satok            new HashMap<String, List<Preference>>();
541c58c15e7a84b582b7d4d20c26824cabce80bce9satok    final private HashMap<String, CheckBoxPreference> mSubtypeAutoSelectionCBMap =
551c58c15e7a84b582b7d4d20c26824cabce80bce9satok            new HashMap<String, CheckBoxPreference>();
561c58c15e7a84b582b7d4d20c26824cabce80bce9satok    private InputMethodManager mImm;
57c88a7ff1efd10374974e45768bde1658cc1d8483satok    private List<InputMethodInfo> mInputMethodProperties;
58c88a7ff1efd10374974e45768bde1658cc1d8483satok    private String mInputMethodId;
5916c3e74d72ec2d18aff007cb88fcf02995918a90satok    private String mTitle;
60ae70ee49492bd89c949946562b43a75f3c81b0adsatok    private String mSystemLocale = "";
61ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataoka    private Collator mCollator = Collator.getInstance();
624a5f889f80b683446e498f244d0eadfd979ca5d0satok
634a5f889f80b683446e498f244d0eadfd979ca5d0satok    @Override
644a5f889f80b683446e498f244d0eadfd979ca5d0satok    public void onCreate(Bundle icicle) {
654a5f889f80b683446e498f244d0eadfd979ca5d0satok        super.onCreate(icicle);
661c58c15e7a84b582b7d4d20c26824cabce80bce9satok        mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
67ae70ee49492bd89c949946562b43a75f3c81b0adsatok        final Configuration config = getResources().getConfiguration();
684a5f889f80b683446e498f244d0eadfd979ca5d0satok        mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY);
69649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa
705e9c5e7bcfbdc3d61f80bc95d16a7d57a362bc39satok        final Bundle arguments = getArguments();
71649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa        // Input method id should be available from an Intent when this preference is launched as a
72649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa        // single Activity (see InputMethodAndSubtypeEnablerActivity). It should be available
73649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa        // from a preference argument when the preference is launched as a part of the other
74649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa        // Activity (like a right pane of 2-pane Settings app)
759f7320132b7a5e7def378ae754c8c708759a8670Tadashi G. Takaoka        mInputMethodId = getActivity().getIntent().getStringExtra(
769f7320132b7a5e7def378ae754c8c708759a8670Tadashi G. Takaoka                android.provider.Settings.EXTRA_INPUT_METHOD_ID);
775e9c5e7bcfbdc3d61f80bc95d16a7d57a362bc39satok        if (mInputMethodId == null && (arguments != null)) {
78649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa            final String inputMethodId =
795e9c5e7bcfbdc3d61f80bc95d16a7d57a362bc39satok                    arguments.getString(android.provider.Settings.EXTRA_INPUT_METHOD_ID);
80649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa            if (inputMethodId != null) {
81649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa                mInputMethodId = inputMethodId;
82649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa            }
83649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa        }
8416c3e74d72ec2d18aff007cb88fcf02995918a90satok        mTitle = getActivity().getIntent().getStringExtra(Intent.EXTRA_TITLE);
8516c3e74d72ec2d18aff007cb88fcf02995918a90satok        if (mTitle == null && (arguments != null)) {
8616c3e74d72ec2d18aff007cb88fcf02995918a90satok            final String title = arguments.getString(Intent.EXTRA_TITLE);
8716c3e74d72ec2d18aff007cb88fcf02995918a90satok            if (title != null) {
8816c3e74d72ec2d18aff007cb88fcf02995918a90satok                mTitle = title;
8916c3e74d72ec2d18aff007cb88fcf02995918a90satok            }
905e9c5e7bcfbdc3d61f80bc95d16a7d57a362bc39satok        }
91649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa
92ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataoka        final Locale locale = config.locale;
93ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataoka        mSystemLocale = locale.toString();
94ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataoka        mCollator = Collator.getInstance(locale);
954a5f889f80b683446e498f244d0eadfd979ca5d0satok        onCreateIMM();
964a5f889f80b683446e498f244d0eadfd979ca5d0satok        setPreferenceScreen(createPreferenceHierarchy());
974a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
984a5f889f80b683446e498f244d0eadfd979ca5d0satok
994a5f889f80b683446e498f244d0eadfd979ca5d0satok    @Override
10016c3e74d72ec2d18aff007cb88fcf02995918a90satok    public void onActivityCreated(Bundle icicle) {
10116c3e74d72ec2d18aff007cb88fcf02995918a90satok        super.onActivityCreated(icicle);
10216c3e74d72ec2d18aff007cb88fcf02995918a90satok        if (!TextUtils.isEmpty(mTitle)) {
10316c3e74d72ec2d18aff007cb88fcf02995918a90satok            getActivity().setTitle(mTitle);
10416c3e74d72ec2d18aff007cb88fcf02995918a90satok        }
10516c3e74d72ec2d18aff007cb88fcf02995918a90satok    }
10616c3e74d72ec2d18aff007cb88fcf02995918a90satok
10716c3e74d72ec2d18aff007cb88fcf02995918a90satok    @Override
1084a5f889f80b683446e498f244d0eadfd979ca5d0satok    public void onResume() {
1094a5f889f80b683446e498f244d0eadfd979ca5d0satok        super.onResume();
110cabde98bcddc2c609a5929b019ebe73f1a68e004Satoshi Kataoka        // Refresh internal states in mInputMethodSettingValues to keep the latest
111cabde98bcddc2c609a5929b019ebe73f1a68e004Satoshi Kataoka        // "InputMethodInfo"s and "InputMethodSubtype"s
112cabde98bcddc2c609a5929b019ebe73f1a68e004Satoshi Kataoka        InputMethodSettingValuesWrapper
113cabde98bcddc2c609a5929b019ebe73f1a68e004Satoshi Kataoka                .getInstance(getActivity()).refreshAllInputMethodAndSubtypes();
1140417e4094713c5f4dac700b645000d0959bf62fasatok        InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
1151c58c15e7a84b582b7d4d20c26824cabce80bce9satok                this, getContentResolver(), mInputMethodProperties, mInputMethodAndSubtypePrefsMap);
1161c58c15e7a84b582b7d4d20c26824cabce80bce9satok        updateAutoSelectionCB();
1174a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
1184a5f889f80b683446e498f244d0eadfd979ca5d0satok
1194a5f889f80b683446e498f244d0eadfd979ca5d0satok    @Override
1204a5f889f80b683446e498f244d0eadfd979ca5d0satok    public void onPause() {
1214a5f889f80b683446e498f244d0eadfd979ca5d0satok        super.onPause();
1221c58c15e7a84b582b7d4d20c26824cabce80bce9satok        // Clear all subtypes of all IMEs to make sure
1231c58c15e7a84b582b7d4d20c26824cabce80bce9satok        clearImplicitlyEnabledSubtypes(null);
1240417e4094713c5f4dac700b645000d0959bf62fasatok        InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
1255fd39cafe1de792300e9e3dd60258a1ce5079f73satok                mInputMethodProperties, mHaveHardKeyboard);
1264a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
1274a5f889f80b683446e498f244d0eadfd979ca5d0satok
1284a5f889f80b683446e498f244d0eadfd979ca5d0satok    @Override
1294a5f889f80b683446e498f244d0eadfd979ca5d0satok    public boolean onPreferenceTreeClick(
1304a5f889f80b683446e498f244d0eadfd979ca5d0satok            PreferenceScreen preferenceScreen, Preference preference) {
1314a5f889f80b683446e498f244d0eadfd979ca5d0satok
1324a5f889f80b683446e498f244d0eadfd979ca5d0satok        if (preference instanceof CheckBoxPreference) {
1334a5f889f80b683446e498f244d0eadfd979ca5d0satok            final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
1341c58c15e7a84b582b7d4d20c26824cabce80bce9satok
1351c58c15e7a84b582b7d4d20c26824cabce80bce9satok            for (String imiId: mSubtypeAutoSelectionCBMap.keySet()) {
1361c58c15e7a84b582b7d4d20c26824cabce80bce9satok                if (mSubtypeAutoSelectionCBMap.get(imiId) == chkPref) {
1371c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    // We look for the first preference item in subtype enabler.
1381c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    // The first item is used for turning on/off subtype auto selection.
1391c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    // We are in the subtype enabler and trying selecting subtypes automatically.
1401c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    setSubtypeAutoSelectionEnabled(imiId, chkPref.isChecked());
1411c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    return super.onPreferenceTreeClick(preferenceScreen, preference);
1421c58c15e7a84b582b7d4d20c26824cabce80bce9satok                }
1431c58c15e7a84b582b7d4d20c26824cabce80bce9satok            }
1441c58c15e7a84b582b7d4d20c26824cabce80bce9satok
1454a5f889f80b683446e498f244d0eadfd979ca5d0satok            final String id = chkPref.getKey();
1464a5f889f80b683446e498f244d0eadfd979ca5d0satok            if (chkPref.isChecked()) {
1474a5f889f80b683446e498f244d0eadfd979ca5d0satok                InputMethodInfo selImi = null;
1484a5f889f80b683446e498f244d0eadfd979ca5d0satok                final int N = mInputMethodProperties.size();
1494a5f889f80b683446e498f244d0eadfd979ca5d0satok                for (int i = 0; i < N; i++) {
1504a5f889f80b683446e498f244d0eadfd979ca5d0satok                    InputMethodInfo imi = mInputMethodProperties.get(i);
1514a5f889f80b683446e498f244d0eadfd979ca5d0satok                    if (id.equals(imi.getId())) {
1524a5f889f80b683446e498f244d0eadfd979ca5d0satok                        selImi = imi;
153993f6ecf4065501433271b5fd5daf21a6f3ae586Satoshi Kataoka                        if (InputMethodUtils.isSystemIme(imi)) {
1549cd11a9aa5ac74ca89432655d019f68d789bc405satok                            InputMethodAndSubtypeUtil.setSubtypesPreferenceEnabled(
1559cd11a9aa5ac74ca89432655d019f68d789bc405satok                                    this, mInputMethodProperties, id, true);
1564a5f889f80b683446e498f244d0eadfd979ca5d0satok                            // This is a built-in IME, so no need to warn.
1574a5f889f80b683446e498f244d0eadfd979ca5d0satok                            return super.onPreferenceTreeClick(preferenceScreen, preference);
1584a5f889f80b683446e498f244d0eadfd979ca5d0satok                        }
1594a5f889f80b683446e498f244d0eadfd979ca5d0satok                        break;
1604a5f889f80b683446e498f244d0eadfd979ca5d0satok                    }
1614a5f889f80b683446e498f244d0eadfd979ca5d0satok                }
1624a5f889f80b683446e498f244d0eadfd979ca5d0satok                if (selImi == null) {
1634a5f889f80b683446e498f244d0eadfd979ca5d0satok                    return super.onPreferenceTreeClick(preferenceScreen, preference);
1644a5f889f80b683446e498f244d0eadfd979ca5d0satok                }
1654a5f889f80b683446e498f244d0eadfd979ca5d0satok                chkPref.setChecked(false);
1664a5f889f80b683446e498f244d0eadfd979ca5d0satok                if (mDialog == null) {
1674a5f889f80b683446e498f244d0eadfd979ca5d0satok                    mDialog = (new AlertDialog.Builder(getActivity()))
1684a5f889f80b683446e498f244d0eadfd979ca5d0satok                            .setTitle(android.R.string.dialog_alert_title)
169c8272b075275f81ddfb9cef2f92514b7ce3365deBjörn Lundén                            .setIconAttribute(android.R.attr.alertDialogIcon)
1704a5f889f80b683446e498f244d0eadfd979ca5d0satok                            .setCancelable(true)
1714a5f889f80b683446e498f244d0eadfd979ca5d0satok                            .setPositiveButton(android.R.string.ok,
1724a5f889f80b683446e498f244d0eadfd979ca5d0satok                                    new DialogInterface.OnClickListener() {
173e077d2b607032fb66a4a046aa4b46945d32d281esatok                                        @Override
1744a5f889f80b683446e498f244d0eadfd979ca5d0satok                                        public void onClick(DialogInterface dialog, int which) {
1754a5f889f80b683446e498f244d0eadfd979ca5d0satok                                            chkPref.setChecked(true);
1769cd11a9aa5ac74ca89432655d019f68d789bc405satok                                            InputMethodAndSubtypeUtil.setSubtypesPreferenceEnabled(
1779cd11a9aa5ac74ca89432655d019f68d789bc405satok                                                    InputMethodAndSubtypeEnabler.this,
1789cd11a9aa5ac74ca89432655d019f68d789bc405satok                                                    mInputMethodProperties, id, true);
1794a5f889f80b683446e498f244d0eadfd979ca5d0satok                                        }
1804a5f889f80b683446e498f244d0eadfd979ca5d0satok
1814a5f889f80b683446e498f244d0eadfd979ca5d0satok                            })
1824a5f889f80b683446e498f244d0eadfd979ca5d0satok                            .setNegativeButton(android.R.string.cancel,
1834a5f889f80b683446e498f244d0eadfd979ca5d0satok                                    new DialogInterface.OnClickListener() {
184e077d2b607032fb66a4a046aa4b46945d32d281esatok                                        @Override
1854a5f889f80b683446e498f244d0eadfd979ca5d0satok                                        public void onClick(DialogInterface dialog, int which) {
1864a5f889f80b683446e498f244d0eadfd979ca5d0satok                                        }
1874a5f889f80b683446e498f244d0eadfd979ca5d0satok
1884a5f889f80b683446e498f244d0eadfd979ca5d0satok                            })
1894a5f889f80b683446e498f244d0eadfd979ca5d0satok                            .create();
1904a5f889f80b683446e498f244d0eadfd979ca5d0satok                } else {
1914a5f889f80b683446e498f244d0eadfd979ca5d0satok                    if (mDialog.isShowing()) {
1924a5f889f80b683446e498f244d0eadfd979ca5d0satok                        mDialog.dismiss();
1934a5f889f80b683446e498f244d0eadfd979ca5d0satok                    }
1944a5f889f80b683446e498f244d0eadfd979ca5d0satok                }
1954a5f889f80b683446e498f244d0eadfd979ca5d0satok                mDialog.setMessage(getResources().getString(
1964a5f889f80b683446e498f244d0eadfd979ca5d0satok                        R.string.ime_security_warning,
1974a5f889f80b683446e498f244d0eadfd979ca5d0satok                        selImi.getServiceInfo().applicationInfo.loadLabel(getPackageManager())));
1984a5f889f80b683446e498f244d0eadfd979ca5d0satok                mDialog.show();
1994a5f889f80b683446e498f244d0eadfd979ca5d0satok            } else {
2009cd11a9aa5ac74ca89432655d019f68d789bc405satok                InputMethodAndSubtypeUtil.setSubtypesPreferenceEnabled(
2019cd11a9aa5ac74ca89432655d019f68d789bc405satok                        this, mInputMethodProperties, id, false);
20274a5414b3c4473106ab2c043d52d8b55a9e4d0c5satok                updateAutoSelectionCB();
2034a5f889f80b683446e498f244d0eadfd979ca5d0satok            }
2044a5f889f80b683446e498f244d0eadfd979ca5d0satok        }
2054a5f889f80b683446e498f244d0eadfd979ca5d0satok        return super.onPreferenceTreeClick(preferenceScreen, preference);
2064a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
2074a5f889f80b683446e498f244d0eadfd979ca5d0satok
2084a5f889f80b683446e498f244d0eadfd979ca5d0satok    @Override
2094a5f889f80b683446e498f244d0eadfd979ca5d0satok    public void onDestroy() {
2104a5f889f80b683446e498f244d0eadfd979ca5d0satok        super.onDestroy();
2114a5f889f80b683446e498f244d0eadfd979ca5d0satok        if (mDialog != null) {
2124a5f889f80b683446e498f244d0eadfd979ca5d0satok            mDialog.dismiss();
2134a5f889f80b683446e498f244d0eadfd979ca5d0satok            mDialog = null;
2144a5f889f80b683446e498f244d0eadfd979ca5d0satok        }
2154a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
2164a5f889f80b683446e498f244d0eadfd979ca5d0satok
2174a5f889f80b683446e498f244d0eadfd979ca5d0satok    private void onCreateIMM() {
2184a5f889f80b683446e498f244d0eadfd979ca5d0satok        InputMethodManager imm = (InputMethodManager) getSystemService(
2194a5f889f80b683446e498f244d0eadfd979ca5d0satok                Context.INPUT_METHOD_SERVICE);
2204a5f889f80b683446e498f244d0eadfd979ca5d0satok
2214a5f889f80b683446e498f244d0eadfd979ca5d0satok        // TODO: Change mInputMethodProperties to Map
2224a5f889f80b683446e498f244d0eadfd979ca5d0satok        mInputMethodProperties = imm.getInputMethodList();
2234a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
2244a5f889f80b683446e498f244d0eadfd979ca5d0satok
2254a5f889f80b683446e498f244d0eadfd979ca5d0satok    private PreferenceScreen createPreferenceHierarchy() {
2264a5f889f80b683446e498f244d0eadfd979ca5d0satok        // Root
227f0ae329740eb66663b6f099ca24a13ea7572418csatok        final PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity());
228f0ae329740eb66663b6f099ca24a13ea7572418csatok        final Context context = getActivity();
2294a5f889f80b683446e498f244d0eadfd979ca5d0satok
2304a5f889f80b683446e498f244d0eadfd979ca5d0satok        int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties.size());
231c88a7ff1efd10374974e45768bde1658cc1d8483satok
2324a5f889f80b683446e498f244d0eadfd979ca5d0satok        for (int i = 0; i < N; ++i) {
233c88a7ff1efd10374974e45768bde1658cc1d8483satok            final InputMethodInfo imi = mInputMethodProperties.get(i);
2345a813ba7f99f1c797ead4a58fdf05f91296158a4Ken Wakasa            final int subtypeCount = imi.getSubtypeCount();
2355a813ba7f99f1c797ead4a58fdf05f91296158a4Ken Wakasa            if (subtypeCount <= 1) continue;
2361c58c15e7a84b582b7d4d20c26824cabce80bce9satok            final String imiId = imi.getId();
2371c58c15e7a84b582b7d4d20c26824cabce80bce9satok            // Add this subtype to the list when no IME is specified or when the IME of this
238c88a7ff1efd10374974e45768bde1658cc1d8483satok            // subtype is the specified IME.
239c88a7ff1efd10374974e45768bde1658cc1d8483satok            if (!TextUtils.isEmpty(mInputMethodId) && !mInputMethodId.equals(imiId)) {
240c88a7ff1efd10374974e45768bde1658cc1d8483satok                continue;
241c88a7ff1efd10374974e45768bde1658cc1d8483satok            }
24294bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok            final PreferenceCategory keyboardSettingsCategory = new PreferenceCategory(context);
2434a5f889f80b683446e498f244d0eadfd979ca5d0satok            root.addPreference(keyboardSettingsCategory);
24494bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok            final PackageManager pm = getPackageManager();
24594bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok            final CharSequence label = imi.loadLabel(pm);
246c88a7ff1efd10374974e45768bde1658cc1d8483satok
2471c58c15e7a84b582b7d4d20c26824cabce80bce9satok            keyboardSettingsCategory.setTitle(label);
248c88a7ff1efd10374974e45768bde1658cc1d8483satok            keyboardSettingsCategory.setKey(imiId);
2491c58c15e7a84b582b7d4d20c26824cabce80bce9satok            // TODO: Use toggle Preference if images are ready.
25094bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok            final CheckBoxPreference autoCB = new CheckBoxPreference(context);
2511c58c15e7a84b582b7d4d20c26824cabce80bce9satok            mSubtypeAutoSelectionCBMap.put(imiId, autoCB);
2521c58c15e7a84b582b7d4d20c26824cabce80bce9satok            keyboardSettingsCategory.addPreference(autoCB);
2531c58c15e7a84b582b7d4d20c26824cabce80bce9satok
25494bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok            final PreferenceCategory activeInputMethodsCategory = new PreferenceCategory(context);
2551c58c15e7a84b582b7d4d20c26824cabce80bce9satok            activeInputMethodsCategory.setTitle(R.string.active_input_method_subtypes);
2561c58c15e7a84b582b7d4d20c26824cabce80bce9satok            root.addPreference(activeInputMethodsCategory);
2574a5f889f80b683446e498f244d0eadfd979ca5d0satok
25894bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok            boolean isAutoSubtype = false;
25994bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok            CharSequence autoSubtypeLabel = null;
26094bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok            final ArrayList<Preference> subtypePreferences = new ArrayList<Preference>();
2615a813ba7f99f1c797ead4a58fdf05f91296158a4Ken Wakasa            if (subtypeCount > 0) {
2625a813ba7f99f1c797ead4a58fdf05f91296158a4Ken Wakasa                for (int j = 0; j < subtypeCount; ++j) {
263f0ae329740eb66663b6f099ca24a13ea7572418csatok                    final InputMethodSubtype subtype = imi.getSubtypeAt(j);
264f0ae329740eb66663b6f099ca24a13ea7572418csatok                    final CharSequence subtypeLabel = subtype.getDisplayName(context,
265f0ae329740eb66663b6f099ca24a13ea7572418csatok                            imi.getPackageName(), imi.getServiceInfo().applicationInfo);
26694bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                    if (subtype.overridesImplicitlyEnabledSubtype()) {
26794bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                        if (!isAutoSubtype) {
26894bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                            isAutoSubtype = true;
26994bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                            autoSubtypeLabel = subtypeLabel;
27094bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                        }
27194bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                    } else {
272ae70ee49492bd89c949946562b43a75f3c81b0adsatok                        final CheckBoxPreference chkbxPref = new SubtypeCheckBoxPreference(
273ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataoka                                context, subtype.getLocale(), mSystemLocale, mCollator);
27494bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                        chkbxPref.setKey(imiId + subtype.hashCode());
27594bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                        chkbxPref.setTitle(subtypeLabel);
27694bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                        subtypePreferences.add(chkbxPref);
27794bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                    }
2784a5f889f80b683446e498f244d0eadfd979ca5d0satok                }
279ae70ee49492bd89c949946562b43a75f3c81b0adsatok                Collections.sort(subtypePreferences);
280ae70ee49492bd89c949946562b43a75f3c81b0adsatok                for (int j = 0; j < subtypePreferences.size(); ++j) {
281ae70ee49492bd89c949946562b43a75f3c81b0adsatok                    activeInputMethodsCategory.addPreference(subtypePreferences.get(j));
282ae70ee49492bd89c949946562b43a75f3c81b0adsatok                }
2831c58c15e7a84b582b7d4d20c26824cabce80bce9satok                mInputMethodAndSubtypePrefsMap.put(imiId, subtypePreferences);
2844a5f889f80b683446e498f244d0eadfd979ca5d0satok            }
28594bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok            if (isAutoSubtype) {
28694bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                if (TextUtils.isEmpty(autoSubtypeLabel)) {
28794bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                    Log.w(TAG, "Title for auto subtype is empty.");
28894bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                    autoCB.setTitle("---");
28994bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                } else {
29094bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                    autoCB.setTitle(autoSubtypeLabel);
29194bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                }
29294bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok            } else {
29394bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok                autoCB.setTitle(R.string.use_system_language_to_select_input_method_subtypes);
29494bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok            }
2954a5f889f80b683446e498f244d0eadfd979ca5d0satok        }
2964a5f889f80b683446e498f244d0eadfd979ca5d0satok        return root;
2974a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
2981c58c15e7a84b582b7d4d20c26824cabce80bce9satok
2991c58c15e7a84b582b7d4d20c26824cabce80bce9satok    private boolean isNoSubtypesExplicitlySelected(String imiId) {
3001c58c15e7a84b582b7d4d20c26824cabce80bce9satok        boolean allSubtypesOff = true;
3011c58c15e7a84b582b7d4d20c26824cabce80bce9satok        final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
3021c58c15e7a84b582b7d4d20c26824cabce80bce9satok        for (Preference subtypePref: subtypePrefs) {
3031c58c15e7a84b582b7d4d20c26824cabce80bce9satok            if (subtypePref instanceof CheckBoxPreference
3041c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    && ((CheckBoxPreference)subtypePref).isChecked()) {
3051c58c15e7a84b582b7d4d20c26824cabce80bce9satok                allSubtypesOff = false;
3061c58c15e7a84b582b7d4d20c26824cabce80bce9satok                break;
3071c58c15e7a84b582b7d4d20c26824cabce80bce9satok            }
3081c58c15e7a84b582b7d4d20c26824cabce80bce9satok        }
3091c58c15e7a84b582b7d4d20c26824cabce80bce9satok        return allSubtypesOff;
3101c58c15e7a84b582b7d4d20c26824cabce80bce9satok    }
3111c58c15e7a84b582b7d4d20c26824cabce80bce9satok
3121c58c15e7a84b582b7d4d20c26824cabce80bce9satok    private void setSubtypeAutoSelectionEnabled(String imiId, boolean autoSelectionEnabled) {
31374a5414b3c4473106ab2c043d52d8b55a9e4d0c5satok        CheckBoxPreference autoSelectionCB = mSubtypeAutoSelectionCBMap.get(imiId);
31474a5414b3c4473106ab2c043d52d8b55a9e4d0c5satok        if (autoSelectionCB == null) return;
31574a5414b3c4473106ab2c043d52d8b55a9e4d0c5satok        autoSelectionCB.setChecked(autoSelectionEnabled);
3161c58c15e7a84b582b7d4d20c26824cabce80bce9satok        final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
3171c58c15e7a84b582b7d4d20c26824cabce80bce9satok        for (Preference subtypePref: subtypePrefs) {
3181c58c15e7a84b582b7d4d20c26824cabce80bce9satok            if (subtypePref instanceof CheckBoxPreference) {
3191c58c15e7a84b582b7d4d20c26824cabce80bce9satok                // When autoSelectionEnabled is true, all subtype prefs need to be disabled with
3201c58c15e7a84b582b7d4d20c26824cabce80bce9satok                // implicitly checked subtypes. In case of false, all subtype prefs need to be
3211c58c15e7a84b582b7d4d20c26824cabce80bce9satok                // enabled.
3221c58c15e7a84b582b7d4d20c26824cabce80bce9satok                subtypePref.setEnabled(!autoSelectionEnabled);
3231c58c15e7a84b582b7d4d20c26824cabce80bce9satok                if (autoSelectionEnabled) {
3241c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    ((CheckBoxPreference)subtypePref).setChecked(false);
3251c58c15e7a84b582b7d4d20c26824cabce80bce9satok                }
3261c58c15e7a84b582b7d4d20c26824cabce80bce9satok            }
3271c58c15e7a84b582b7d4d20c26824cabce80bce9satok        }
3281c58c15e7a84b582b7d4d20c26824cabce80bce9satok        if (autoSelectionEnabled) {
3291c58c15e7a84b582b7d4d20c26824cabce80bce9satok            InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
3301c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    mInputMethodProperties, mHaveHardKeyboard);
3311c58c15e7a84b582b7d4d20c26824cabce80bce9satok            setCheckedImplicitlyEnabledSubtypes(imiId);
3321c58c15e7a84b582b7d4d20c26824cabce80bce9satok        }
3331c58c15e7a84b582b7d4d20c26824cabce80bce9satok    }
3341c58c15e7a84b582b7d4d20c26824cabce80bce9satok
3351c58c15e7a84b582b7d4d20c26824cabce80bce9satok    private void setCheckedImplicitlyEnabledSubtypes(String targetImiId) {
3361c58c15e7a84b582b7d4d20c26824cabce80bce9satok        updateImplicitlyEnabledSubtypes(targetImiId, true);
3371c58c15e7a84b582b7d4d20c26824cabce80bce9satok    }
3381c58c15e7a84b582b7d4d20c26824cabce80bce9satok
3391c58c15e7a84b582b7d4d20c26824cabce80bce9satok    private void clearImplicitlyEnabledSubtypes(String targetImiId) {
3401c58c15e7a84b582b7d4d20c26824cabce80bce9satok        updateImplicitlyEnabledSubtypes(targetImiId, false);
3411c58c15e7a84b582b7d4d20c26824cabce80bce9satok    }
3421c58c15e7a84b582b7d4d20c26824cabce80bce9satok
3431c58c15e7a84b582b7d4d20c26824cabce80bce9satok    private void updateImplicitlyEnabledSubtypes(String targetImiId, boolean check) {
3441c58c15e7a84b582b7d4d20c26824cabce80bce9satok        // When targetImiId is null, apply to all subtypes of all IMEs
3451c58c15e7a84b582b7d4d20c26824cabce80bce9satok        for (InputMethodInfo imi: mInputMethodProperties) {
3461c58c15e7a84b582b7d4d20c26824cabce80bce9satok            String imiId = imi.getId();
3471c58c15e7a84b582b7d4d20c26824cabce80bce9satok            if (targetImiId != null && !targetImiId.equals(imiId)) continue;
3481c58c15e7a84b582b7d4d20c26824cabce80bce9satok            final CheckBoxPreference autoCB = mSubtypeAutoSelectionCBMap.get(imiId);
3491c58c15e7a84b582b7d4d20c26824cabce80bce9satok            // No need to update implicitly enabled subtypes when the user has unchecked the
3501c58c15e7a84b582b7d4d20c26824cabce80bce9satok            // "subtype auto selection".
3511c58c15e7a84b582b7d4d20c26824cabce80bce9satok            if (autoCB == null || !autoCB.isChecked()) continue;
3521c58c15e7a84b582b7d4d20c26824cabce80bce9satok            final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
3531c58c15e7a84b582b7d4d20c26824cabce80bce9satok            final List<InputMethodSubtype> implicitlyEnabledSubtypes =
3541c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    mImm.getEnabledInputMethodSubtypeList(imi, true);
3551c58c15e7a84b582b7d4d20c26824cabce80bce9satok            if (subtypePrefs == null || implicitlyEnabledSubtypes == null) continue;
3561c58c15e7a84b582b7d4d20c26824cabce80bce9satok            for (Preference subtypePref: subtypePrefs) {
3571c58c15e7a84b582b7d4d20c26824cabce80bce9satok                if (subtypePref instanceof CheckBoxPreference) {
3581c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    CheckBoxPreference cb = (CheckBoxPreference)subtypePref;
3591c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    cb.setChecked(false);
3601c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    if (check) {
3611c58c15e7a84b582b7d4d20c26824cabce80bce9satok                        for (InputMethodSubtype subtype: implicitlyEnabledSubtypes) {
3621c58c15e7a84b582b7d4d20c26824cabce80bce9satok                            String implicitlyEnabledSubtypePrefKey = imiId + subtype.hashCode();
3631c58c15e7a84b582b7d4d20c26824cabce80bce9satok                            if (cb.getKey().equals(implicitlyEnabledSubtypePrefKey)) {
3641c58c15e7a84b582b7d4d20c26824cabce80bce9satok                                cb.setChecked(true);
3651c58c15e7a84b582b7d4d20c26824cabce80bce9satok                                break;
3661c58c15e7a84b582b7d4d20c26824cabce80bce9satok                            }
3671c58c15e7a84b582b7d4d20c26824cabce80bce9satok                        }
3681c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    }
3691c58c15e7a84b582b7d4d20c26824cabce80bce9satok                }
3701c58c15e7a84b582b7d4d20c26824cabce80bce9satok            }
3711c58c15e7a84b582b7d4d20c26824cabce80bce9satok        }
3721c58c15e7a84b582b7d4d20c26824cabce80bce9satok    }
3731c58c15e7a84b582b7d4d20c26824cabce80bce9satok
3741c58c15e7a84b582b7d4d20c26824cabce80bce9satok    private void updateAutoSelectionCB() {
3751c58c15e7a84b582b7d4d20c26824cabce80bce9satok        for (String imiId: mInputMethodAndSubtypePrefsMap.keySet()) {
3761c58c15e7a84b582b7d4d20c26824cabce80bce9satok            setSubtypeAutoSelectionEnabled(imiId, isNoSubtypesExplicitlySelected(imiId));
3771c58c15e7a84b582b7d4d20c26824cabce80bce9satok        }
3781c58c15e7a84b582b7d4d20c26824cabce80bce9satok        setCheckedImplicitlyEnabledSubtypes(null);
3791c58c15e7a84b582b7d4d20c26824cabce80bce9satok    }
380ae70ee49492bd89c949946562b43a75f3c81b0adsatok
381ae70ee49492bd89c949946562b43a75f3c81b0adsatok    private static class SubtypeCheckBoxPreference extends CheckBoxPreference {
382ae70ee49492bd89c949946562b43a75f3c81b0adsatok        private final boolean mIsSystemLocale;
383ae70ee49492bd89c949946562b43a75f3c81b0adsatok        private final boolean mIsSystemLanguage;
384ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataoka        private final Collator mCollator;
385ae70ee49492bd89c949946562b43a75f3c81b0adsatok
386ae70ee49492bd89c949946562b43a75f3c81b0adsatok        public SubtypeCheckBoxPreference(
387ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataoka                Context context, String subtypeLocale, String systemLocale, Collator collator) {
388ae70ee49492bd89c949946562b43a75f3c81b0adsatok            super(context);
389ae70ee49492bd89c949946562b43a75f3c81b0adsatok            if (TextUtils.isEmpty(subtypeLocale)) {
390ae70ee49492bd89c949946562b43a75f3c81b0adsatok                mIsSystemLocale = false;
391ae70ee49492bd89c949946562b43a75f3c81b0adsatok                mIsSystemLanguage = false;
392ae70ee49492bd89c949946562b43a75f3c81b0adsatok            } else {
393ae70ee49492bd89c949946562b43a75f3c81b0adsatok                mIsSystemLocale = subtypeLocale.equals(systemLocale);
394ae70ee49492bd89c949946562b43a75f3c81b0adsatok                mIsSystemLanguage = mIsSystemLocale
395ae70ee49492bd89c949946562b43a75f3c81b0adsatok                        || subtypeLocale.startsWith(systemLocale.substring(0, 2));
396ae70ee49492bd89c949946562b43a75f3c81b0adsatok            }
397ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataoka            mCollator = collator;
398ae70ee49492bd89c949946562b43a75f3c81b0adsatok        }
399ae70ee49492bd89c949946562b43a75f3c81b0adsatok
400ae70ee49492bd89c949946562b43a75f3c81b0adsatok        @Override
401ae70ee49492bd89c949946562b43a75f3c81b0adsatok        public int compareTo(Preference p) {
402ae70ee49492bd89c949946562b43a75f3c81b0adsatok            if (p instanceof SubtypeCheckBoxPreference) {
403ae70ee49492bd89c949946562b43a75f3c81b0adsatok                final SubtypeCheckBoxPreference pref = ((SubtypeCheckBoxPreference)p);
404ae70ee49492bd89c949946562b43a75f3c81b0adsatok                final CharSequence t0 = getTitle();
405ae70ee49492bd89c949946562b43a75f3c81b0adsatok                final CharSequence t1 = pref.getTitle();
406ae70ee49492bd89c949946562b43a75f3c81b0adsatok                if (TextUtils.equals(t0, t1)) {
407ae70ee49492bd89c949946562b43a75f3c81b0adsatok                    return 0;
408ae70ee49492bd89c949946562b43a75f3c81b0adsatok                }
409ae70ee49492bd89c949946562b43a75f3c81b0adsatok                if (mIsSystemLocale) {
410ae70ee49492bd89c949946562b43a75f3c81b0adsatok                    return -1;
411ae70ee49492bd89c949946562b43a75f3c81b0adsatok                }
412ae70ee49492bd89c949946562b43a75f3c81b0adsatok                if (pref.mIsSystemLocale) {
413ae70ee49492bd89c949946562b43a75f3c81b0adsatok                    return 1;
414ae70ee49492bd89c949946562b43a75f3c81b0adsatok                }
415ae70ee49492bd89c949946562b43a75f3c81b0adsatok                if (mIsSystemLanguage) {
416ae70ee49492bd89c949946562b43a75f3c81b0adsatok                    return -1;
417ae70ee49492bd89c949946562b43a75f3c81b0adsatok                }
418ae70ee49492bd89c949946562b43a75f3c81b0adsatok                if (pref.mIsSystemLanguage) {
419ae70ee49492bd89c949946562b43a75f3c81b0adsatok                    return 1;
420ae70ee49492bd89c949946562b43a75f3c81b0adsatok                }
421ae70ee49492bd89c949946562b43a75f3c81b0adsatok                if (TextUtils.isEmpty(t0)) {
422ae70ee49492bd89c949946562b43a75f3c81b0adsatok                    return 1;
423ae70ee49492bd89c949946562b43a75f3c81b0adsatok                }
424ae70ee49492bd89c949946562b43a75f3c81b0adsatok                if (TextUtils.isEmpty(t1)) {
425ae70ee49492bd89c949946562b43a75f3c81b0adsatok                    return -1;
426ae70ee49492bd89c949946562b43a75f3c81b0adsatok                }
427ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataoka                return mCollator.compare(t0.toString(), t1.toString());
428ae70ee49492bd89c949946562b43a75f3c81b0adsatok            } else {
429ae70ee49492bd89c949946562b43a75f3c81b0adsatok                Log.w(TAG, "Illegal preference type.");
430ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataoka                return super.compareTo(p);
431ae70ee49492bd89c949946562b43a75f3c81b0adsatok            }
432ae70ee49492bd89c949946562b43a75f3c81b0adsatok        }
433ae70ee49492bd89c949946562b43a75f3c81b0adsatok    }
4344a5f889f80b683446e498f244d0eadfd979ca5d0satok}
435