InputMethodAndSubtypeEnabler.java revision 3460a2683ca820d8d7d184bc1e98241ea3986a4a
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
194a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.app.AlertDialog;
204a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.content.Context;
214a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.content.DialogInterface;
225e9c5e7bcfbdc3d61f80bc95d16a7d57a362bc39satokimport android.content.Intent;
234a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.content.pm.PackageManager;
244a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.content.res.Configuration;
254a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.os.Bundle;
264a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.preference.CheckBoxPreference;
274a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.preference.Preference;
284a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.preference.PreferenceCategory;
294a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.preference.PreferenceScreen;
30c88a7ff1efd10374974e45768bde1658cc1d8483satokimport android.text.TextUtils;
314a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.view.inputmethod.InputMethodInfo;
324a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.view.inputmethod.InputMethodManager;
334a5f889f80b683446e498f244d0eadfd979ca5d0satokimport android.view.inputmethod.InputMethodSubtype;
344a5f889f80b683446e498f244d0eadfd979ca5d0satok
3555aee124063dc28e740efcfdadafa5738b0b538aTadashi G. Takaokaimport com.android.internal.inputmethod.InputMethodUtils;
3655aee124063dc28e740efcfdadafa5738b0b538aTadashi G. Takaokaimport com.android.settings.R;
3755aee124063dc28e740efcfdadafa5738b0b538aTadashi G. Takaokaimport com.android.settings.SettingsPreferenceFragment;
3855aee124063dc28e740efcfdadafa5738b0b538aTadashi G. Takaoka
39ef120bea7b8cb3b891ae3f39d7a97989c107a16fSatoshi Kataokaimport java.text.Collator;
404a5f889f80b683446e498f244d0eadfd979ca5d0satokimport java.util.ArrayList;
41ae70ee49492bd89c949946562b43a75f3c81b0adsatokimport java.util.Collections;
4255aee124063dc28e740efcfdadafa5738b0b538aTadashi G. Takaokaimport java.util.Comparator;
43c88a7ff1efd10374974e45768bde1658cc1d8483satokimport java.util.HashMap;
444a5f889f80b683446e498f244d0eadfd979ca5d0satokimport java.util.List;
454a5f889f80b683446e498f244d0eadfd979ca5d0satok
464a5f889f80b683446e498f244d0eadfd979ca5d0satokpublic class InputMethodAndSubtypeEnabler extends SettingsPreferenceFragment {
47c88a7ff1efd10374974e45768bde1658cc1d8483satok    private boolean mHaveHardKeyboard;
48989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka    private final HashMap<String, List<Preference>> mInputMethodAndSubtypePrefsMap =
49d2dd7d33c9206f7aa29abbff48119d0f86ddfc36Tadashi G. Takaoka            new HashMap<>();
50989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka    private final HashMap<String, CheckBoxPreference> mAutoSelectionPrefsMap = new HashMap<>();
511c58c15e7a84b582b7d4d20c26824cabce80bce9satok    private InputMethodManager mImm;
5272c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka    // TODO: Change mInputMethodInfoList to Map
5372c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka    private List<InputMethodInfo> mInputMethodInfoList;
54989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka    private Collator mCollator;
55989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka    private AlertDialog mDialog = null;
564a5f889f80b683446e498f244d0eadfd979ca5d0satok
574a5f889f80b683446e498f244d0eadfd979ca5d0satok    @Override
5872c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka    public void onCreate(final Bundle icicle) {
594a5f889f80b683446e498f244d0eadfd979ca5d0satok        super.onCreate(icicle);
601c58c15e7a84b582b7d4d20c26824cabce80bce9satok        mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
61ae70ee49492bd89c949946562b43a75f3c81b0adsatok        final Configuration config = getResources().getConfiguration();
624a5f889f80b683446e498f244d0eadfd979ca5d0satok        mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY);
63649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa
64649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa        // Input method id should be available from an Intent when this preference is launched as a
65649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa        // single Activity (see InputMethodAndSubtypeEnablerActivity). It should be available
66649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa        // from a preference argument when the preference is launched as a part of the other
67649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa        // Activity (like a right pane of 2-pane Settings app)
68fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final String targetImi = getStringExtraFromIntentOrArguments(
699f7320132b7a5e7def378ae754c8c708759a8670Tadashi G. Takaoka                android.provider.Settings.EXTRA_INPUT_METHOD_ID);
70649b9f19e1193547f339239afa32d569f43358fcDaisuke Miyakawa
7172c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        mInputMethodInfoList = mImm.getInputMethodList();
72989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        mCollator = Collator.getInstance();
73fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka
74fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity());
75fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final int imiCount = mInputMethodInfoList.size();
76fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        for (int index = 0; index < imiCount; ++index) {
77fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            final InputMethodInfo imi = mInputMethodInfoList.get(index);
78fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            // Add subtype preferences of this IME when it is specified or no IME is specified.
79fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            if (imi.getId().equals(targetImi) || TextUtils.isEmpty(targetImi)) {
80fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                addInputMethodSubtypePreferences(imi, root);
81fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            }
82fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        }
83fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        setPreferenceScreen(root);
844a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
854a5f889f80b683446e498f244d0eadfd979ca5d0satok
8672c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka    private String getStringExtraFromIntentOrArguments(final String name) {
8772c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        final Intent intent = getActivity().getIntent();
8872c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        final String fromIntent = intent.getStringExtra(name);
8972c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        if (fromIntent != null) {
9072c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka            return fromIntent;
9172c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        }
9272c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        final Bundle arguments = getArguments();
9372c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        return (arguments == null) ? null : arguments.getString(name);
9472c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka    }
9572c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka
964a5f889f80b683446e498f244d0eadfd979ca5d0satok    @Override
9772c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka    public void onActivityCreated(final Bundle icicle) {
9816c3e74d72ec2d18aff007cb88fcf02995918a90satok        super.onActivityCreated(icicle);
9972c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        final String title = getStringExtraFromIntentOrArguments(Intent.EXTRA_TITLE);
10072c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        if (!TextUtils.isEmpty(title)) {
10172c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka            getActivity().setTitle(title);
10216c3e74d72ec2d18aff007cb88fcf02995918a90satok        }
10316c3e74d72ec2d18aff007cb88fcf02995918a90satok    }
10416c3e74d72ec2d18aff007cb88fcf02995918a90satok
10516c3e74d72ec2d18aff007cb88fcf02995918a90satok    @Override
1064a5f889f80b683446e498f244d0eadfd979ca5d0satok    public void onResume() {
1074a5f889f80b683446e498f244d0eadfd979ca5d0satok        super.onResume();
108cabde98bcddc2c609a5929b019ebe73f1a68e004Satoshi Kataoka        // Refresh internal states in mInputMethodSettingValues to keep the latest
109cabde98bcddc2c609a5929b019ebe73f1a68e004Satoshi Kataoka        // "InputMethodInfo"s and "InputMethodSubtype"s
110cabde98bcddc2c609a5929b019ebe73f1a68e004Satoshi Kataoka        InputMethodSettingValuesWrapper
111cabde98bcddc2c609a5929b019ebe73f1a68e004Satoshi Kataoka                .getInstance(getActivity()).refreshAllInputMethodAndSubtypes();
1120417e4094713c5f4dac700b645000d0959bf62fasatok        InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
11372c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka                this, getContentResolver(), mInputMethodInfoList, mInputMethodAndSubtypePrefsMap);
114989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        updateAutoSelectionPreferences();
1154a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
1164a5f889f80b683446e498f244d0eadfd979ca5d0satok
1174a5f889f80b683446e498f244d0eadfd979ca5d0satok    @Override
1184a5f889f80b683446e498f244d0eadfd979ca5d0satok    public void onPause() {
1194a5f889f80b683446e498f244d0eadfd979ca5d0satok        super.onPause();
1201c58c15e7a84b582b7d4d20c26824cabce80bce9satok        // Clear all subtypes of all IMEs to make sure
121989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        updateImplicitlyEnabledSubtypes(null /* targetImiId */, false /* check */);
1220417e4094713c5f4dac700b645000d0959bf62fasatok        InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
12372c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka                mInputMethodInfoList, mHaveHardKeyboard);
1244a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
1254a5f889f80b683446e498f244d0eadfd979ca5d0satok
126fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka    // TODO: Stop overriding this method. Instead start using {@link OnPreferenceChangedListener}.
1274a5f889f80b683446e498f244d0eadfd979ca5d0satok    @Override
12872c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka    public boolean onPreferenceTreeClick(final PreferenceScreen preferenceScreen,
12972c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka            final Preference preference) {
130fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        if (!(preference instanceof CheckBoxPreference)) {
131fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            return super.onPreferenceTreeClick(preferenceScreen, preference);
132fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        }
133fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
1344a5f889f80b683446e498f244d0eadfd979ca5d0satok
135fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        for (final String imiId : mAutoSelectionPrefsMap.keySet()) {
136fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            if (mAutoSelectionPrefsMap.get(imiId) == chkPref) {
137fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                // We look for the first preference item in subtype enabler. The first item is used
138fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                // for turning on/off subtype auto selection. We are in the subtype enabler and
139fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                // trying selecting subtypes automatically.
140fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                setAutoSelectionSubtypesEnabled(imiId, chkPref.isChecked());
141fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                return super.onPreferenceTreeClick(preferenceScreen, preference);
1421c58c15e7a84b582b7d4d20c26824cabce80bce9satok            }
143fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        }
1441c58c15e7a84b582b7d4d20c26824cabce80bce9satok
145fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final String id = chkPref.getKey();
146fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        // Turns off a subtype.
147fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        if (!chkPref.isChecked()) {
148fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            // TODO: Because no preference on this screen has {@link InputMethodInfo} id as a key,
149fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            // the following setSubtypesPreferenceEnabled call is effectively no-operation and
150fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            // can be removed.
151fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            InputMethodAndSubtypeUtil.setSubtypesPreferenceEnabled(
152fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                    this, mInputMethodInfoList, id, false);
153fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            updateAutoSelectionPreferences();
154fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            return super.onPreferenceTreeClick(preferenceScreen, preference);
155fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        }
1564a5f889f80b683446e498f244d0eadfd979ca5d0satok
157fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        // Turns on a subtype.
158fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final InputMethodInfo imi = getInputMethodInfoById(id);
159fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        // TODO: Because no preference on this screen has {@link InputMethodInfo} id as a key,
160fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        // <code>imi</code> is always null and the following code can be removed.
161fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        if (imi == null) {
162fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            return super.onPreferenceTreeClick(preferenceScreen, preference);
163fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        }
164fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        // Turns on a system IME's subtype.
165fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        if (InputMethodUtils.isSystemIme(imi)) {
166fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            InputMethodAndSubtypeUtil.setSubtypesPreferenceEnabled(
167fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                    this, mInputMethodInfoList, id, true);
168fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            // This is a built-in IME, so no need to warn.
169fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            return super.onPreferenceTreeClick(preferenceScreen, preference);
170fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        }
171fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        // Turns on a 3rd party IME's subtype.
172fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        // Turns off a subtype before showing a security warning dialog.
173fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        chkPref.setChecked(false);
174fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        if (mDialog != null && mDialog.isShowing()) {
175fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            mDialog.dismiss();
176fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        }
177fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
178fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        builder.setCancelable(true);
179fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        builder.setTitle(android.R.string.dialog_alert_title);
180fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final CharSequence label = imi.getServiceInfo().applicationInfo
181fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                .loadLabel(getPackageManager());
182fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        builder.setMessage(getString(R.string.ime_security_warning, label));
183fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
184fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            @Override
185fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            public void onClick(final DialogInterface dialog, final int which) {
186fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                // The user explicitly enable the subtype.
187fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                chkPref.setChecked(true);
1889cd11a9aa5ac74ca89432655d019f68d789bc405satok                InputMethodAndSubtypeUtil.setSubtypesPreferenceEnabled(
189fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                        InputMethodAndSubtypeEnabler.this, mInputMethodInfoList, id, true);
1904a5f889f80b683446e498f244d0eadfd979ca5d0satok            }
191fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        });
192fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
193fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            @Override
194fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            public void onClick(final DialogInterface dialog, final int which) {}
195fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        });
196fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        mDialog = builder.create();
197fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        mDialog.show();
1984a5f889f80b683446e498f244d0eadfd979ca5d0satok        return super.onPreferenceTreeClick(preferenceScreen, preference);
1994a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
2004a5f889f80b683446e498f244d0eadfd979ca5d0satok
201989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka    private InputMethodInfo getInputMethodInfoById(final String imiId) {
202989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        final int imiCount = mInputMethodInfoList.size();
203989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        for (int index = 0; index < imiCount; ++index) {
204989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka            final InputMethodInfo imi = mInputMethodInfoList.get(index);
205989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka            if (imi.getId().equals(imiId)) {
206989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka                return imi;
207989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka            }
208989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        }
209989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        return null;
210989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka    }
211989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka
2124a5f889f80b683446e498f244d0eadfd979ca5d0satok    @Override
2134a5f889f80b683446e498f244d0eadfd979ca5d0satok    public void onDestroy() {
2144a5f889f80b683446e498f244d0eadfd979ca5d0satok        super.onDestroy();
2154a5f889f80b683446e498f244d0eadfd979ca5d0satok        if (mDialog != null) {
2164a5f889f80b683446e498f244d0eadfd979ca5d0satok            mDialog.dismiss();
2174a5f889f80b683446e498f244d0eadfd979ca5d0satok            mDialog = null;
2184a5f889f80b683446e498f244d0eadfd979ca5d0satok        }
2194a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
2204a5f889f80b683446e498f244d0eadfd979ca5d0satok
221fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka    private void addInputMethodSubtypePreferences(final InputMethodInfo imi,
222fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            final PreferenceScreen root) {
223f0ae329740eb66663b6f099ca24a13ea7572418csatok        final Context context = getActivity();
224fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final int subtypeCount = imi.getSubtypeCount();
225fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        if (subtypeCount <= 1) {
226fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            return;
227fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        }
228fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final String imiId = imi.getId();
229fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final PreferenceCategory keyboardSettingsCategory = new PreferenceCategory(context);
230fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        root.addPreference(keyboardSettingsCategory);
231fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final PackageManager pm = getPackageManager();
232fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final CharSequence label = imi.loadLabel(pm);
2334a5f889f80b683446e498f244d0eadfd979ca5d0satok
234fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        keyboardSettingsCategory.setTitle(label);
235fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        keyboardSettingsCategory.setKey(imiId);
236fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        // TODO: Use toggle Preference if images are ready.
237fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final CheckBoxPreference autoSelectionPref = new CheckBoxPreference(context);
238fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        mAutoSelectionPrefsMap.put(imiId, autoSelectionPref);
239fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        keyboardSettingsCategory.addPreference(autoSelectionPref);
2401c58c15e7a84b582b7d4d20c26824cabce80bce9satok
241fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final PreferenceCategory activeInputMethodsCategory = new PreferenceCategory(context);
242fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        activeInputMethodsCategory.setTitle(R.string.active_input_method_subtypes);
243fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        root.addPreference(activeInputMethodsCategory);
2444a5f889f80b683446e498f244d0eadfd979ca5d0satok
245fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        CharSequence autoSubtypeLabel = null;
246fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final ArrayList<Preference> subtypePreferences = new ArrayList<>();
247fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        for (int index = 0; index < subtypeCount; ++index) {
248fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            final InputMethodSubtype subtype = imi.getSubtypeAt(index);
249fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            if (subtype.overridesImplicitlyEnabledSubtype()) {
250fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                if (autoSubtypeLabel == null) {
251fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                    autoSubtypeLabel = subtype.getDisplayName(
252fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                            context, imi.getPackageName(), imi.getServiceInfo().applicationInfo);
2534a5f889f80b683446e498f244d0eadfd979ca5d0satok                }
254fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            } else {
255fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                final Preference subtypePref = new InputMethodSubtypePreference(
256fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                        context, subtype, imi);
257fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                subtypePreferences.add(subtypePref);
258989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka            }
259fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        }
260fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        Collections.sort(subtypePreferences, new Comparator<Preference>() {
261fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            @Override
262fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            public int compare(final Preference lhs, final Preference rhs) {
263fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                if (lhs instanceof InputMethodSubtypePreference) {
264fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                    return ((InputMethodSubtypePreference) lhs).compareTo(rhs, mCollator);
265ae70ee49492bd89c949946562b43a75f3c81b0adsatok                }
266fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                return lhs.compareTo(rhs);
26794bc76f1aa887e90b31f375cb1e9a2ecc3c59c8bsatok            }
268fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        });
269fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final int prefCount = subtypePreferences.size();
270fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        for (int index = 0; index < prefCount; ++index) {
271fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            final Preference pref = subtypePreferences.get(index);
272fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            activeInputMethodsCategory.addPreference(pref);
2733460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka            InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref);
274fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        }
275fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        mInputMethodAndSubtypePrefsMap.put(imiId, subtypePreferences);
276fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        if (TextUtils.isEmpty(autoSubtypeLabel)) {
277fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            autoSelectionPref.setTitle(
278fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                    R.string.use_system_language_to_select_input_method_subtypes);
279fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        } else {
280fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            autoSelectionPref.setTitle(autoSubtypeLabel);
2814a5f889f80b683446e498f244d0eadfd979ca5d0satok        }
2824a5f889f80b683446e498f244d0eadfd979ca5d0satok    }
2831c58c15e7a84b582b7d4d20c26824cabce80bce9satok
284989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka    private boolean isNoSubtypesExplicitlySelected(final String imiId) {
2851c58c15e7a84b582b7d4d20c26824cabce80bce9satok        final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
28672c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        for (final Preference pref : subtypePrefs) {
28772c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka            if (pref instanceof CheckBoxPreference && ((CheckBoxPreference)pref).isChecked()) {
288989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka                return false;
2891c58c15e7a84b582b7d4d20c26824cabce80bce9satok            }
2901c58c15e7a84b582b7d4d20c26824cabce80bce9satok        }
291989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        return true;
2921c58c15e7a84b582b7d4d20c26824cabce80bce9satok    }
2931c58c15e7a84b582b7d4d20c26824cabce80bce9satok
294989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka    private void setAutoSelectionSubtypesEnabled(final String imiId,
295989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka            final boolean autoSelectionEnabled) {
296989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        final CheckBoxPreference autoSelectionPref = mAutoSelectionPrefsMap.get(imiId);
297989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        if (autoSelectionPref == null) {
29872c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka            return;
29972c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        }
300989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        autoSelectionPref.setChecked(autoSelectionEnabled);
3011c58c15e7a84b582b7d4d20c26824cabce80bce9satok        final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
302989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        for (final Preference pref : subtypePrefs) {
303989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka            if (pref instanceof CheckBoxPreference) {
3041c58c15e7a84b582b7d4d20c26824cabce80bce9satok                // When autoSelectionEnabled is true, all subtype prefs need to be disabled with
3051c58c15e7a84b582b7d4d20c26824cabce80bce9satok                // implicitly checked subtypes. In case of false, all subtype prefs need to be
3061c58c15e7a84b582b7d4d20c26824cabce80bce9satok                // enabled.
307989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka                pref.setEnabled(!autoSelectionEnabled);
3081c58c15e7a84b582b7d4d20c26824cabce80bce9satok                if (autoSelectionEnabled) {
309989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka                    ((CheckBoxPreference)pref).setChecked(false);
3101c58c15e7a84b582b7d4d20c26824cabce80bce9satok                }
3111c58c15e7a84b582b7d4d20c26824cabce80bce9satok            }
3121c58c15e7a84b582b7d4d20c26824cabce80bce9satok        }
3131c58c15e7a84b582b7d4d20c26824cabce80bce9satok        if (autoSelectionEnabled) {
31472c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka            InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(
31572c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka                    this, getContentResolver(), mInputMethodInfoList, mHaveHardKeyboard);
316989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka            updateImplicitlyEnabledSubtypes(imiId, true /* check */);
3171c58c15e7a84b582b7d4d20c26824cabce80bce9satok        }
3181c58c15e7a84b582b7d4d20c26824cabce80bce9satok    }
3191c58c15e7a84b582b7d4d20c26824cabce80bce9satok
320989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka    private void updateImplicitlyEnabledSubtypes(final String targetImiId, final boolean check) {
3211c58c15e7a84b582b7d4d20c26824cabce80bce9satok        // When targetImiId is null, apply to all subtypes of all IMEs
32272c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        for (final InputMethodInfo imi : mInputMethodInfoList) {
32372c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka            final String imiId = imi.getId();
324989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka            final CheckBoxPreference autoSelectionPref = mAutoSelectionPrefsMap.get(imiId);
3251c58c15e7a84b582b7d4d20c26824cabce80bce9satok            // No need to update implicitly enabled subtypes when the user has unchecked the
3261c58c15e7a84b582b7d4d20c26824cabce80bce9satok            // "subtype auto selection".
327989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka            if (autoSelectionPref == null || !autoSelectionPref.isChecked()) {
32872c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka                continue;
32972c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka            }
330fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            if (imiId.equals(targetImiId) || targetImiId == null) {
331fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                updateImplicitlyEnabledSubtypesOf(imi, check);
332fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            }
333fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        }
334fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka    }
335fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka
336fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka    private void updateImplicitlyEnabledSubtypesOf(final InputMethodInfo imi, final boolean check) {
337fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final String imiId = imi.getId();
338fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
339fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        final List<InputMethodSubtype> implicitlyEnabledSubtypes =
340fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                mImm.getEnabledInputMethodSubtypeList(imi, true);
341fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        if (subtypePrefs == null || implicitlyEnabledSubtypes == null) {
342fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            return;
343fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        }
344fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka        for (final Preference pref : subtypePrefs) {
345fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            if (!(pref instanceof CheckBoxPreference)) {
34672c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka                continue;
34772c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka            }
348fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            final CheckBoxPreference subtypePref = (CheckBoxPreference)pref;
349fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            subtypePref.setChecked(false);
350fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka            if (check) {
351fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                for (final InputMethodSubtype subtype : implicitlyEnabledSubtypes) {
352fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                    final String implicitlyEnabledSubtypePrefKey = imiId + subtype.hashCode();
353fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                    if (subtypePref.getKey().equals(implicitlyEnabledSubtypePrefKey)) {
354fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                        subtypePref.setChecked(true);
355fc8202463e64d9e85af3c99c1d8552d4bb74b7dcTadashi G. Takaoka                        break;
3561c58c15e7a84b582b7d4d20c26824cabce80bce9satok                    }
3571c58c15e7a84b582b7d4d20c26824cabce80bce9satok                }
3581c58c15e7a84b582b7d4d20c26824cabce80bce9satok            }
3591c58c15e7a84b582b7d4d20c26824cabce80bce9satok        }
3601c58c15e7a84b582b7d4d20c26824cabce80bce9satok    }
3611c58c15e7a84b582b7d4d20c26824cabce80bce9satok
362989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka    private void updateAutoSelectionPreferences() {
36372c2a6e5c2990df7f36c4f7305d4ba4b28e10023Tadashi G. Takaoka        for (final String imiId : mInputMethodAndSubtypePrefsMap.keySet()) {
364989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka            setAutoSelectionSubtypesEnabled(imiId, isNoSubtypesExplicitlySelected(imiId));
3651c58c15e7a84b582b7d4d20c26824cabce80bce9satok        }
366989c364f2de3eeb2c92fa6713b8e0f137e8d971aTadashi G. Takaoka        updateImplicitlyEnabledSubtypes(null /* targetImiId */, true /* check */);
3671c58c15e7a84b582b7d4d20c26824cabce80bce9satok    }
3684a5f889f80b683446e498f244d0eadfd979ca5d0satok}
369