16fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka/*
26fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka * Copyright (C) 2012 The Android Open Source Project
36fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka *
46fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
56fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka * you may not use this file except in compliance with the License.
66fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka * You may obtain a copy of the License at
76fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka *
86fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
96fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka *
106fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
116fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
126fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka * See the License for the specific language governing permissions and
146fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka * limitations under the License.
156fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka */
166fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
176fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaokapackage com.android.inputmethod.latin;
186fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
199342484e8d573a40f470b6a593df31c602fa4076Ken Wakasaimport static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE;
206fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
216fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaokaimport android.content.Context;
2285e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaokaimport android.content.SharedPreferences;
238a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaokaimport android.inputmethodservice.InputMethodService;
248a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaokaimport android.os.AsyncTask;
258ba4f33709e6c40ade96922f88feace6e4b75b56Yohei Yukawaimport android.os.Build;
266fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaokaimport android.os.IBinder;
27f90fc105ab1159f43f536bcacdd1224c2c05bacbTadashi G. Takaokaimport android.preference.PreferenceManager;
2885629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaokaimport android.util.Log;
296fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaokaimport android.view.inputmethod.InputMethodInfo;
306fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaokaimport android.view.inputmethod.InputMethodManager;
316fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaokaimport android.view.inputmethod.InputMethodSubtype;
326fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
337fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaokaimport com.android.inputmethod.annotations.UsedForTesting;
346fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaokaimport com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
35809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawaimport com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
36a7d2fc6befa1b16883200a653fc01deb4d94944dKen Wakasaimport com.android.inputmethod.latin.settings.Settings;
37a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaokaimport com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
3831a10e226d23b30b24d9c902608ff013cc1c3e0cTadashi G. Takaokaimport com.android.inputmethod.latin.utils.LanguageOnSpacebarUtils;
39a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaokaimport com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
406fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
416fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaokaimport java.util.Collections;
42ff6445ed0eac57f9daf573178ffe9f4e1e2b246aJean Chalardimport java.util.HashMap;
432a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaokaimport java.util.HashSet;
446fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaokaimport java.util.List;
452a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaokaimport java.util.Locale;
468a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaokaimport java.util.Map;
472a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaokaimport java.util.Set;
486fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
49d3a4c5132422b189c8dbb94dbbe84a9b9761b0a8Tadashi G. Takaokaimport javax.annotation.Nonnull;
50b86ca76cea9aedf47a81f9272fb59897de3bbbe7Dan Zivkovicimport javax.annotation.Nullable;
51d3a4c5132422b189c8dbb94dbbe84a9b9761b0a8Tadashi G. Takaoka
526fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka/**
536fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka * Enrichment class for InputMethodManager to simplify interaction and add functionality.
546fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka */
55698b19ef35d1d865943ec9d9ee05f8f0e66dc3f8Mohammadinamul Sheik// non final for easy mocking.
56698b19ef35d1d865943ec9d9ee05f8f0e66dc3f8Mohammadinamul Sheikpublic class RichInputMethodManager {
576fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    private static final String TAG = RichInputMethodManager.class.getSimpleName();
588a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka    private static final boolean DEBUG = false;
596fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
606fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    private RichInputMethodManager() {
616fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        // This utility class is not publicly instantiable.
626fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
636fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
646fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    private static final RichInputMethodManager sInstance = new RichInputMethodManager();
656fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
66f13487dfbf2b7547b48a5e9123235ee8a1d660c8Jean Chalard    private Context mContext;
676fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    private InputMethodManagerCompatWrapper mImmWrapper;
68ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka    private InputMethodInfoCache mInputMethodInfoCache;
697fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka    private RichInputMethodSubtype mCurrentRichInputMethodSubtype;
708a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka    private InputMethodInfo mShortcutInputMethodInfo;
718a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka    private InputMethodSubtype mShortcutSubtype;
726fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
7385629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    private static final int INDEX_NOT_FOUND = -1;
7485629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka
756fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    public static RichInputMethodManager getInstance() {
766fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        sInstance.checkInitialized();
776fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        return sInstance;
786fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
796fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
80f90fc105ab1159f43f536bcacdd1224c2c05bacbTadashi G. Takaoka    public static void init(final Context context) {
81052ec62abd577182af8d5b50564d8075b18be3c9Yohei Yukawa        sInstance.initInternal(context);
8285e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka    }
8385e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka
8485e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka    private boolean isInitialized() {
8585e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        return mImmWrapper != null;
866fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
876fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
886fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    private void checkInitialized() {
8985e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        if (!isInitialized()) {
906fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            throw new RuntimeException(TAG + " is used before initialization");
916fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        }
926fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
936fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
94052ec62abd577182af8d5b50564d8075b18be3c9Yohei Yukawa    private void initInternal(final Context context) {
9585e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        if (isInitialized()) {
9685e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka            return;
9785e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        }
986fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        mImmWrapper = new InputMethodManagerCompatWrapper(context);
99f13487dfbf2b7547b48a5e9123235ee8a1d660c8Jean Chalard        mContext = context;
100ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        mInputMethodInfoCache = new InputMethodInfoCache(
101ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka                mImmWrapper.mImm, context.getPackageName());
10285e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka
10385e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        // Initialize additional subtypes.
104a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaoka        SubtypeLocaleUtils.init(context);
1058a711f2a547a61b9f4f3ef3bdb79a66b618db58fTadashi G. Takaoka        final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes();
1064486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka        mImmWrapper.mImm.setAdditionalInputMethodSubtypes(
1074486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka                getInputMethodIdOfThisIme(), additionalSubtypes);
1084486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka
1094486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka        // Initialize the current input method subtype and the shortcut IME.
1104486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka        refreshSubtypeCaches();
111052ec62abd577182af8d5b50564d8075b18be3c9Yohei Yukawa    }
112052ec62abd577182af8d5b50564d8075b18be3c9Yohei Yukawa
1138a711f2a547a61b9f4f3ef3bdb79a66b618db58fTadashi G. Takaoka    public InputMethodSubtype[] getAdditionalSubtypes() {
1148a711f2a547a61b9f4f3ef3bdb79a66b618db58fTadashi G. Takaoka        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
115d3b0ecec22cda883150851dced32c1eda2910a66Tadashi G. Takaoka        final String prefAdditionalSubtypes = Settings.readPrefAdditionalSubtypes(
1168a711f2a547a61b9f4f3ef3bdb79a66b618db58fTadashi G. Takaoka                prefs, mContext.getResources());
117052ec62abd577182af8d5b50564d8075b18be3c9Yohei Yukawa        return AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefAdditionalSubtypes);
1186fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
1196fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
1206fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    public InputMethodManager getInputMethodManager() {
1216fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        checkInitialized();
1226fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        return mImmWrapper.mImm;
1236fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
1246fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
125b902109000bcef184e69daac7dc3906fc969791eSatoshi Kataoka    public List<InputMethodSubtype> getMyEnabledInputMethodSubtypeList(
126b902109000bcef184e69daac7dc3906fc969791eSatoshi Kataoka            boolean allowsImplicitlySelectedSubtypes) {
127ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        return getEnabledInputMethodSubtypeList(
128ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka                getInputMethodInfoOfThisIme(), allowsImplicitlySelectedSubtypes);
129b902109000bcef184e69daac7dc3906fc969791eSatoshi Kataoka    }
130b902109000bcef184e69daac7dc3906fc969791eSatoshi Kataoka
1316fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) {
13285629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        if (mImmWrapper.switchToNextInputMethod(token, onlyCurrentIme)) {
13385629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            return true;
13485629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        }
13585629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        // Was not able to call {@link InputMethodManager#switchToNextInputMethodIBinder,boolean)}
13685629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        // because the current device is running ICS or previous and lacks the API.
13785629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        if (switchToNextInputSubtypeInThisIme(token, onlyCurrentIme)) {
13885629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            return true;
13985629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        }
14085629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        return switchToNextInputMethodAndSubtype(token);
14185629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    }
14285629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka
14385629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    private boolean switchToNextInputSubtypeInThisIme(final IBinder token,
14485629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            final boolean onlyCurrentIme) {
14585629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final InputMethodManager imm = mImmWrapper.mImm;
14685629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype();
147b902109000bcef184e69daac7dc3906fc969791eSatoshi Kataoka        final List<InputMethodSubtype> enabledSubtypes = getMyEnabledInputMethodSubtypeList(
148b902109000bcef184e69daac7dc3906fc969791eSatoshi Kataoka                true /* allowsImplicitlySelectedSubtypes */);
14985629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final int currentIndex = getSubtypeIndexInList(currentSubtype, enabledSubtypes);
15085629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        if (currentIndex == INDEX_NOT_FOUND) {
15185629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            Log.w(TAG, "Can't find current subtype in enabled subtypes: subtype="
152a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaoka                    + SubtypeLocaleUtils.getSubtypeNameForLogging(currentSubtype));
1536fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            return false;
1546fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        }
15585629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final int nextIndex = (currentIndex + 1) % enabledSubtypes.size();
15685629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        if (nextIndex <= currentIndex && !onlyCurrentIme) {
15785629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            // The current subtype is the last or only enabled one and it needs to switch to
15885629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            // next IME.
15985629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            return false;
16085629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        }
16185629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final InputMethodSubtype nextSubtype = enabledSubtypes.get(nextIndex);
16285629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        setInputMethodAndSubtype(token, nextSubtype);
16385629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        return true;
16485629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    }
16585629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka
16685629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    private boolean switchToNextInputMethodAndSubtype(final IBinder token) {
16785629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final InputMethodManager imm = mImmWrapper.mImm;
16885629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
169ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        final int currentIndex = getImiIndexInList(getInputMethodInfoOfThisIme(), enabledImis);
17085629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        if (currentIndex == INDEX_NOT_FOUND) {
17185629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            Log.w(TAG, "Can't find current IME in enabled IMEs: IME package="
172ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka                    + getInputMethodInfoOfThisIme().getPackageName());
17385629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            return false;
17485629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        }
17585629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final InputMethodInfo nextImi = getNextNonAuxiliaryIme(currentIndex, enabledImis);
176ff6445ed0eac57f9daf573178ffe9f4e1e2b246aJean Chalard        final List<InputMethodSubtype> enabledSubtypes = getEnabledInputMethodSubtypeList(nextImi,
177ff6445ed0eac57f9daf573178ffe9f4e1e2b246aJean Chalard                true /* allowsImplicitlySelectedSubtypes */);
17885629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        if (enabledSubtypes.isEmpty()) {
17985629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            // The next IME has no subtype.
18085629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            imm.setInputMethod(token, nextImi.getId());
18185629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            return true;
18285629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        }
18385629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final InputMethodSubtype firstSubtype = enabledSubtypes.get(0);
18485629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        imm.setInputMethodAndSubtype(token, nextImi.getId(), firstSubtype);
18585629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        return true;
18685629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    }
18785629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka
18885629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    private static int getImiIndexInList(final InputMethodInfo inputMethodInfo,
18985629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            final List<InputMethodInfo> imiList) {
19085629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final int count = imiList.size();
19185629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        for (int index = 0; index < count; index++) {
19285629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            final InputMethodInfo imi = imiList.get(index);
19385629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            if (imi.equals(inputMethodInfo)) {
19485629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka                return index;
19585629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            }
19685629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        }
19785629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        return INDEX_NOT_FOUND;
19885629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    }
19985629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka
20085629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    // This method mimics {@link InputMethodManager#switchToNextInputMethod(IBinder,boolean)}.
20185629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    private static InputMethodInfo getNextNonAuxiliaryIme(final int currentIndex,
20285629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            final List<InputMethodInfo> imiList) {
20385629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final int count = imiList.size();
20485629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        for (int i = 1; i < count; i++) {
20585629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            final int nextIndex = (currentIndex + i) % count;
20685629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            final InputMethodInfo nextImi = imiList.get(nextIndex);
20785629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            if (!isAuxiliaryIme(nextImi)) {
20885629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka                return nextImi;
20985629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            }
21085629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        }
21185629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        return imiList.get(currentIndex);
21285629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    }
21385629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka
21485629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    // Copied from {@link InputMethodInfo}. See how auxiliary of IME is determined.
21585629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    private static boolean isAuxiliaryIme(final InputMethodInfo imi) {
21685629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final int count = imi.getSubtypeCount();
21785629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        if (count == 0) {
21885629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            return false;
21985629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        }
22085629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        for (int index = 0; index < count; index++) {
22185629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            final InputMethodSubtype subtype = imi.getSubtypeAt(index);
22285629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            if (!subtype.isAuxiliary()) {
22385629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka                return false;
22485629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            }
22585629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        }
2266fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        return true;
2276fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
2286fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
229ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka    private static class InputMethodInfoCache {
230ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        private final InputMethodManager mImm;
231ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        private final String mImePackageName;
232ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka
233afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka        private InputMethodInfo mCachedThisImeInfo;
234afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka        private final HashMap<InputMethodInfo, List<InputMethodSubtype>>
235afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka                mCachedSubtypeListWithImplicitlySelected;
236afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka        private final HashMap<InputMethodInfo, List<InputMethodSubtype>>
237afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka                mCachedSubtypeListOnlyExplicitlySelected;
238ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka
239ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        public InputMethodInfoCache(final InputMethodManager imm, final String imePackageName) {
240ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka            mImm = imm;
241ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka            mImePackageName = imePackageName;
242afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            mCachedSubtypeListWithImplicitlySelected = new HashMap<>();
243afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            mCachedSubtypeListOnlyExplicitlySelected = new HashMap<>();
244ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        }
245ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka
246afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka        public synchronized InputMethodInfo getInputMethodOfThisIme() {
247afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            if (mCachedThisImeInfo != null) {
248afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka                return mCachedThisImeInfo;
249ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka            }
250ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka            for (final InputMethodInfo imi : mImm.getInputMethodList()) {
251ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka                if (imi.getPackageName().equals(mImePackageName)) {
252afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka                    mCachedThisImeInfo = imi;
253ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka                    return imi;
254ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka                }
255ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka            }
256ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka            throw new RuntimeException("Input method id for " + mImePackageName + " not found.");
257ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        }
258ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka
259afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka        public synchronized List<InputMethodSubtype> getEnabledInputMethodSubtypeList(
260afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka                final InputMethodInfo imi, final boolean allowsImplicitlySelectedSubtypes) {
261afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            final HashMap<InputMethodInfo, List<InputMethodSubtype>> cache =
262afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka                    allowsImplicitlySelectedSubtypes
263afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka                    ? mCachedSubtypeListWithImplicitlySelected
264afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka                    : mCachedSubtypeListOnlyExplicitlySelected;
265afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            final List<InputMethodSubtype> cachedList = cache.get(imi);
266afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            if (cachedList != null) {
267afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka                return cachedList;
268afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            }
269afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            final List<InputMethodSubtype> result = mImm.getEnabledInputMethodSubtypeList(
270afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka                    imi, allowsImplicitlySelectedSubtypes);
271afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            cache.put(imi, result);
272afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            return result;
273afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka        }
274afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka
275ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        public synchronized void clear() {
276afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            mCachedThisImeInfo = null;
277afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            mCachedSubtypeListWithImplicitlySelected.clear();
278afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka            mCachedSubtypeListOnlyExplicitlySelected.clear();
279ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        }
280ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka    }
281ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka
2826fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    public InputMethodInfo getInputMethodInfoOfThisIme() {
283afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka        return mInputMethodInfoCache.getInputMethodOfThisIme();
2846fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
2856fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
2866fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    public String getInputMethodIdOfThisIme() {
287ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        return getInputMethodInfoOfThisIme().getId();
2886fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
2896fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
29076d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    public boolean checkIfSubtypeBelongsToThisImeAndEnabled(final InputMethodSubtype subtype) {
29112d80ebead6a1d7f704a5a3af3b6fe3313ceab05Dan Zivkovic        return checkIfSubtypeBelongsToList(subtype,
29212d80ebead6a1d7f704a5a3af3b6fe3313ceab05Dan Zivkovic                getEnabledInputMethodSubtypeList(
29312d80ebead6a1d7f704a5a3af3b6fe3313ceab05Dan Zivkovic                        getInputMethodInfoOfThisIme(),
29412d80ebead6a1d7f704a5a3af3b6fe3313ceab05Dan Zivkovic                        true /* allowsImplicitlySelectedSubtypes */));
29576d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    }
29676d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka
29776d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    public boolean checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(
29876d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka            final InputMethodSubtype subtype) {
29976d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka        final boolean subtypeEnabled = checkIfSubtypeBelongsToThisImeAndEnabled(subtype);
30012d80ebead6a1d7f704a5a3af3b6fe3313ceab05Dan Zivkovic        final boolean subtypeExplicitlyEnabled = checkIfSubtypeBelongsToList(subtype,
30112d80ebead6a1d7f704a5a3af3b6fe3313ceab05Dan Zivkovic                getMyEnabledInputMethodSubtypeList(false /* allowsImplicitlySelectedSubtypes */));
30276d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka        return subtypeEnabled && !subtypeExplicitlyEnabled;
3036fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
3046fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
30576d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    private static boolean checkIfSubtypeBelongsToList(final InputMethodSubtype subtype,
30676d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka            final List<InputMethodSubtype> subtypes) {
30785629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        return getSubtypeIndexInList(subtype, subtypes) != INDEX_NOT_FOUND;
30885629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    }
30985629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka
31085629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka    private static int getSubtypeIndexInList(final InputMethodSubtype subtype,
31185629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            final List<InputMethodSubtype> subtypes) {
31285629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        final int count = subtypes.size();
31385629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        for (int index = 0; index < count; index++) {
31485629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka            final InputMethodSubtype ims = subtypes.get(index);
31576d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka            if (ims.equals(subtype)) {
31685629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka                return index;
3176fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            }
3186fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        }
31985629debaaaa576047a4f01430411f0d7b41762dTadashi G. Takaoka        return INDEX_NOT_FOUND;
3206fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
3216fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
3224486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka    public void onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) {
3234486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka        updateCurrentSubtype(newSubtype);
3244486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka        updateShortcutIme();
3257fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka        if (DEBUG) {
3264486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka            Log.w(TAG, "onSubtypeChanged: " + mCurrentRichInputMethodSubtype.getNameForLogging());
3277fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka        }
3287fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka    }
3297fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka
3307fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka    private static RichInputMethodSubtype sForcedSubtypeForTesting = null;
3317fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka
3327fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka    @UsedForTesting
3334486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka    static void forceSubtype(@Nonnull final InputMethodSubtype subtype) {
334b86ca76cea9aedf47a81f9272fb59897de3bbbe7Dan Zivkovic        sForcedSubtypeForTesting = RichInputMethodSubtype.getRichInputMethodSubtype(subtype);
3357fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka    }
3367fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka
3374486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka    @Nonnull
338107fb4c476779df16be23e245547253978c197acDan Zivkovic    public Locale getCurrentSubtypeLocale() {
3397fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka        if (null != sForcedSubtypeForTesting) {
340107fb4c476779df16be23e245547253978c197acDan Zivkovic            return sForcedSubtypeForTesting.getLocale();
3417fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka        }
342107fb4c476779df16be23e245547253978c197acDan Zivkovic        return getCurrentSubtype().getLocale();
3437fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka    }
3447fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka
3454486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka    @Nonnull
3467fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka    public RichInputMethodSubtype getCurrentSubtype() {
3477fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka        if (null != sForcedSubtypeForTesting) {
3487fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka            return sForcedSubtypeForTesting;
3497fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka        }
3507fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka        return mCurrentRichInputMethodSubtype;
3517fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka    }
3527fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka
3537fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka
3547fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka    public String getCombiningRulesExtraValueOfCurrentSubtype() {
3557fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka        return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype().getRawSubtype());
3567fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka    }
3577fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka
3586fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) {
3596fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        final List<InputMethodInfo> enabledImis = mImmWrapper.mImm.getEnabledInputMethodList();
3606fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, enabledImis);
3616fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
3626fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
3636fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    public boolean hasMultipleEnabledSubtypesInThisIme(
3646fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            final boolean shouldIncludeAuxiliarySubtypes) {
365ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        final List<InputMethodInfo> imiList = Collections.singletonList(
366ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka                getInputMethodInfoOfThisIme());
3676fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, imiList);
3686fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
3696fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
3706fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    private boolean hasMultipleEnabledSubtypes(final boolean shouldIncludeAuxiliarySubtypes,
3716fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            final List<InputMethodInfo> imiList) {
3726fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        // Number of the filtered IMEs
3736fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        int filteredImisCount = 0;
3746fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
3756fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        for (InputMethodInfo imi : imiList) {
3766fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            // We can return true immediately after we find two or more filtered IMEs.
3776fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            if (filteredImisCount > 1) return true;
378ff6445ed0eac57f9daf573178ffe9f4e1e2b246aJean Chalard            final List<InputMethodSubtype> subtypes = getEnabledInputMethodSubtypeList(imi, true);
3796fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            // IMEs that have no subtypes should be counted.
3806fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            if (subtypes.isEmpty()) {
3816fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                ++filteredImisCount;
3826fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                continue;
3836fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            }
3846fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
3856fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            int auxCount = 0;
3866fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            for (InputMethodSubtype subtype : subtypes) {
3876fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                if (subtype.isAuxiliary()) {
3886fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                    ++auxCount;
3896fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                }
3906fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            }
3916fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            final int nonAuxCount = subtypes.size() - auxCount;
3926fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
3936fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            // IMEs that have one or more non-auxiliary subtypes should be counted.
3946fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
3956fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            // subtypes should be counted as well.
3966fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
3976fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                ++filteredImisCount;
3986fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            }
3996fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        }
4006fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
4016fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        if (filteredImisCount > 1) {
4026fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            return true;
4036fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        }
404b902109000bcef184e69daac7dc3906fc969791eSatoshi Kataoka        final List<InputMethodSubtype> subtypes = getMyEnabledInputMethodSubtypeList(true);
4056fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        int keyboardCount = 0;
4066fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's
4076fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        // both explicitly and implicitly enabled input method subtype.
4086fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        // (The current IME should be LatinIME.)
4096fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        for (InputMethodSubtype subtype : subtypes) {
4106fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            if (KEYBOARD_MODE.equals(subtype.getMode())) {
4116fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                ++keyboardCount;
4126fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            }
4136fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        }
4146fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        return keyboardCount > 1;
4156fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
4166fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
4176fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    public InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(final String localeString,
4186fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            final String keyboardLayoutSetName) {
419ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        final InputMethodInfo myImi = getInputMethodInfoOfThisIme();
4206fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        final int count = myImi.getSubtypeCount();
4216fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        for (int i = 0; i < count; i++) {
4226fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            final InputMethodSubtype subtype = myImi.getSubtypeAt(i);
423a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaoka            final String layoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
4246fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            if (localeString.equals(subtype.getLocale())
4256fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                    && keyboardLayoutSetName.equals(layoutName)) {
4266fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                return subtype;
4276fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            }
4286fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        }
4296fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        return null;
4306fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
4316fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka
432809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa    public InputMethodSubtype findSubtypeByLocale(final Locale locale) {
433809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        // Find the best subtype based on a straightforward matching algorithm.
434809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        // TODO: Use LocaleList#getFirstMatch() instead.
435809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        final List<InputMethodSubtype> subtypes =
436809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa                getMyEnabledInputMethodSubtypeList(true /* allowsImplicitlySelectedSubtypes */);
437809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        final int count = subtypes.size();
438809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        for (int i = 0; i < count; ++i) {
439809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            final InputMethodSubtype subtype = subtypes.get(i);
440809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
441809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            if (subtypeLocale.equals(locale)) {
442809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa                return subtype;
443809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            }
444809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        }
445809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        for (int i = 0; i < count; ++i) {
446809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            final InputMethodSubtype subtype = subtypes.get(i);
447809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
448809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            if (subtypeLocale.getLanguage().equals(locale.getLanguage()) &&
449809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa                    subtypeLocale.getCountry().equals(locale.getCountry()) &&
450809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa                    subtypeLocale.getVariant().equals(locale.getVariant())) {
451809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa                return subtype;
452809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            }
453809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        }
454809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        for (int i = 0; i < count; ++i) {
455809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            final InputMethodSubtype subtype = subtypes.get(i);
456809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
457809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            if (subtypeLocale.getLanguage().equals(locale.getLanguage()) &&
458809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa                    subtypeLocale.getCountry().equals(locale.getCountry())) {
459809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa                return subtype;
460809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            }
461809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        }
462809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        for (int i = 0; i < count; ++i) {
463809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            final InputMethodSubtype subtype = subtypes.get(i);
464809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
465809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            if (subtypeLocale.getLanguage().equals(locale.getLanguage())) {
466809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa                return subtype;
467809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa            }
468809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        }
469809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa        return null;
470809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa    }
471809c93214bd85f038c3abb09d8dee60f778b7746Yohei Yukawa
4721931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka    public void setInputMethodAndSubtype(final IBinder token, final InputMethodSubtype subtype) {
4731931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        mImmWrapper.mImm.setInputMethodAndSubtype(
474ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka                token, getInputMethodIdOfThisIme(), subtype);
4751931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka    }
4761931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka
4776fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    public void setAdditionalInputMethodSubtypes(final InputMethodSubtype[] subtypes) {
4786fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        mImmWrapper.mImm.setAdditionalInputMethodSubtypes(
479ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka                getInputMethodIdOfThisIme(), subtypes);
480ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        // Clear the cache so that we go read the {@link InputMethodInfo} of this IME and list of
481ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        // subtypes again next time.
4824486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka        refreshSubtypeCaches();
483ff6445ed0eac57f9daf573178ffe9f4e1e2b246aJean Chalard    }
484ff6445ed0eac57f9daf573178ffe9f4e1e2b246aJean Chalard
485ff6445ed0eac57f9daf573178ffe9f4e1e2b246aJean Chalard    private List<InputMethodSubtype> getEnabledInputMethodSubtypeList(final InputMethodInfo imi,
486ff6445ed0eac57f9daf573178ffe9f4e1e2b246aJean Chalard            final boolean allowsImplicitlySelectedSubtypes) {
487afd52dfc601c635e8a729b30b4ccf7a7fd7ad134Tadashi G. Takaoka        return mInputMethodInfoCache.getEnabledInputMethodSubtypeList(
488ff6445ed0eac57f9daf573178ffe9f4e1e2b246aJean Chalard                imi, allowsImplicitlySelectedSubtypes);
489ff6445ed0eac57f9daf573178ffe9f4e1e2b246aJean Chalard    }
490ff6445ed0eac57f9daf573178ffe9f4e1e2b246aJean Chalard
4914486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka    public void refreshSubtypeCaches() {
492ad5795a89117dbb5ebe4f1f308bc7e8a685ebf46Tadashi G. Takaoka        mInputMethodInfoCache.clear();
4934486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka        updateCurrentSubtype(mImmWrapper.mImm.getCurrentInputMethodSubtype());
4944486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka        updateShortcutIme();
4956fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    }
4968ba4f33709e6c40ade96922f88feace6e4b75b56Yohei Yukawa
4978ba4f33709e6c40ade96922f88feace6e4b75b56Yohei Yukawa    public boolean shouldOfferSwitchingToNextInputMethod(final IBinder binder,
4988ba4f33709e6c40ade96922f88feace6e4b75b56Yohei Yukawa            boolean defaultValue) {
49958e248ebda82ef5ae16f3b5192635409ffad5f00Yohei Yukawa        // Use the default value instead on Jelly Bean MR2 and previous where
50058e248ebda82ef5ae16f3b5192635409ffad5f00Yohei Yukawa        // {@link InputMethodManager#shouldOfferSwitchingToNextInputMethod} isn't yet available
50158e248ebda82ef5ae16f3b5192635409ffad5f00Yohei Yukawa        // and on KitKat where the API is still just a stub to return true always.
50258e248ebda82ef5ae16f3b5192635409ffad5f00Yohei Yukawa        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
5038ba4f33709e6c40ade96922f88feace6e4b75b56Yohei Yukawa            return defaultValue;
5048ba4f33709e6c40ade96922f88feace6e4b75b56Yohei Yukawa        }
5058ba4f33709e6c40ade96922f88feace6e4b75b56Yohei Yukawa        return mImmWrapper.shouldOfferSwitchingToNextInputMethod(binder);
5068ba4f33709e6c40ade96922f88feace6e4b75b56Yohei Yukawa    }
5072a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka
5082a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka    public boolean isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes() {
5092a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka        final Locale systemLocale = mContext.getResources().getConfiguration().locale;
5102a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka        final Set<InputMethodSubtype> enabledSubtypesOfEnabledImes = new HashSet<>();
5112a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka        final InputMethodManager inputMethodManager = getInputMethodManager();
5122a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka        final List<InputMethodInfo> enabledInputMethodInfoList =
5132a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka                inputMethodManager.getEnabledInputMethodList();
5142a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka        for (final InputMethodInfo info : enabledInputMethodInfoList) {
5152a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka            final List<InputMethodSubtype> enabledSubtypes =
5162a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka                    inputMethodManager.getEnabledInputMethodSubtypeList(
5172a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka                            info, true /* allowsImplicitlySelectedSubtypes */);
5182a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka            if (enabledSubtypes.isEmpty()) {
5192a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka                // An IME with no subtypes is found.
5202a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka                return false;
5212a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka            }
5222a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka            enabledSubtypesOfEnabledImes.addAll(enabledSubtypes);
5232a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka        }
5242a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka        for (final InputMethodSubtype subtype : enabledSubtypesOfEnabledImes) {
5252a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka            if (!subtype.isAuxiliary() && !subtype.getLocale().isEmpty()
5262a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka                    && !systemLocale.equals(SubtypeLocaleUtils.getSubtypeLocale(subtype))) {
5272a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka                return false;
5282a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka            }
5292a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka        }
5302a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka        return true;
5312a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka    }
5328a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka
533b86ca76cea9aedf47a81f9272fb59897de3bbbe7Dan Zivkovic    private void updateCurrentSubtype(@Nullable final InputMethodSubtype subtype) {
534b86ca76cea9aedf47a81f9272fb59897de3bbbe7Dan Zivkovic        mCurrentRichInputMethodSubtype = RichInputMethodSubtype.getRichInputMethodSubtype(subtype);
5354486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka    }
5364486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka
5374486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka    private void updateShortcutIme() {
5388a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        if (DEBUG) {
5398a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            Log.d(TAG, "Update shortcut IME from : "
5408a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka                    + (mShortcutInputMethodInfo == null
5418a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka                            ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
5428a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka                    + (mShortcutSubtype == null ? "<null>" : (
5438a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka                            mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
5448a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        }
54531a10e226d23b30b24d9c902608ff013cc1c3e0cTadashi G. Takaoka        final RichInputMethodSubtype richSubtype = mCurrentRichInputMethodSubtype;
54631a10e226d23b30b24d9c902608ff013cc1c3e0cTadashi G. Takaoka        final boolean implicitlyEnabledSubtype = checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(
54731a10e226d23b30b24d9c902608ff013cc1c3e0cTadashi G. Takaoka                richSubtype.getRawSubtype());
54831a10e226d23b30b24d9c902608ff013cc1c3e0cTadashi G. Takaoka        final Locale systemLocale = mContext.getResources().getConfiguration().locale;
54931a10e226d23b30b24d9c902608ff013cc1c3e0cTadashi G. Takaoka        LanguageOnSpacebarUtils.onSubtypeChanged(
55031a10e226d23b30b24d9c902608ff013cc1c3e0cTadashi G. Takaoka                richSubtype, implicitlyEnabledSubtype, systemLocale);
55131a10e226d23b30b24d9c902608ff013cc1c3e0cTadashi G. Takaoka        LanguageOnSpacebarUtils.setEnabledSubtypes(getMyEnabledInputMethodSubtypeList(
55231a10e226d23b30b24d9c902608ff013cc1c3e0cTadashi G. Takaoka                true /* allowsImplicitlySelectedSubtypes */));
55331a10e226d23b30b24d9c902608ff013cc1c3e0cTadashi G. Takaoka
5548a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        // TODO: Update an icon for shortcut IME
5558a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
5568a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka                getInputMethodManager().getShortcutInputMethodsAndSubtypes();
5578a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        mShortcutInputMethodInfo = null;
5588a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        mShortcutSubtype = null;
5598a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        for (final InputMethodInfo imi : shortcuts.keySet()) {
5608a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            final List<InputMethodSubtype> subtypes = shortcuts.get(imi);
5618a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            // TODO: Returns the first found IMI for now. Should handle all shortcuts as
5628a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            // appropriate.
5638a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            mShortcutInputMethodInfo = imi;
5648a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            // TODO: Pick up the first found subtype for now. Should handle all subtypes
5658a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            // as appropriate.
5668a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null;
5678a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            break;
5688a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        }
5698a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        if (DEBUG) {
5708a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            Log.d(TAG, "Update shortcut IME to : "
5718a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka                    + (mShortcutInputMethodInfo == null
5728a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka                            ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
5738a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka                    + (mShortcutSubtype == null ? "<null>" : (
5748a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka                            mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
5758a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        }
5768a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka    }
5778a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka
5784486314225c4bbb97f35cdbdbb2da1de4fc28be2Tadashi G. Takaoka    public void switchToShortcutIme(final InputMethodService context) {
5798a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        if (mShortcutInputMethodInfo == null) {
5808a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            return;
5818a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        }
5828a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka
5838a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        final String imiId = mShortcutInputMethodInfo.getId();
5848a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        switchToTargetIME(imiId, mShortcutSubtype, context);
5858a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka    }
5868a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka
5878a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka    private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype,
5888a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            final InputMethodService context) {
5898a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        final IBinder token = context.getWindow().getWindow().getAttributes().token;
5908a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        if (token == null) {
5918a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            return;
5928a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        }
5938a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        final InputMethodManager imm = getInputMethodManager();
5948a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        new AsyncTask<Void, Void, Void>() {
5958a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            @Override
5968a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            protected Void doInBackground(Void... params) {
5978a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka                imm.setInputMethodAndSubtype(token, imiId, subtype);
5988a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka                return null;
5998a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            }
6008a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
6018a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka    }
6028a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka
6038a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka    public boolean isShortcutImeReady() {
6048a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        if (mShortcutInputMethodInfo == null) {
6058a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            return false;
6068a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        }
6078a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        if (mShortcutSubtype == null) {
6088a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka            return true;
6098a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        }
6108a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka        return true;
6118a2c4afad4c30fe7c082387b4beafd95c3c823e8Tadashi G. Takaoka    }
6126fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka}
613