aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/org/kelar/inputmethod/latin/RichInputMethodManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/org/kelar/inputmethod/latin/RichInputMethodManager.java')
-rw-r--r--java/src/org/kelar/inputmethod/latin/RichInputMethodManager.java612
1 files changed, 612 insertions, 0 deletions
diff --git a/java/src/org/kelar/inputmethod/latin/RichInputMethodManager.java b/java/src/org/kelar/inputmethod/latin/RichInputMethodManager.java
new file mode 100644
index 000000000..f364ce982
--- /dev/null
+++ b/java/src/org/kelar/inputmethod/latin/RichInputMethodManager.java
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.kelar.inputmethod.latin;
+
+import static org.kelar.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.inputmethodservice.InputMethodService;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.IBinder;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+
+import org.kelar.inputmethod.annotations.UsedForTesting;
+import org.kelar.inputmethod.compat.InputMethodManagerCompatWrapper;
+import org.kelar.inputmethod.compat.InputMethodSubtypeCompatUtils;
+import org.kelar.inputmethod.latin.settings.Settings;
+import org.kelar.inputmethod.latin.utils.AdditionalSubtypeUtils;
+import org.kelar.inputmethod.latin.utils.LanguageOnSpacebarUtils;
+import org.kelar.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * Enrichment class for InputMethodManager to simplify interaction and add functionality.
+ */
+// non final for easy mocking.
+public class RichInputMethodManager {
+ private static final String TAG = RichInputMethodManager.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private RichInputMethodManager() {
+ // This utility class is not publicly instantiable.
+ }
+
+ private static final RichInputMethodManager sInstance = new RichInputMethodManager();
+
+ private Context mContext;
+ private InputMethodManagerCompatWrapper mImmWrapper;
+ private InputMethodInfoCache mInputMethodInfoCache;
+ private RichInputMethodSubtype mCurrentRichInputMethodSubtype;
+ private InputMethodInfo mShortcutInputMethodInfo;
+ private InputMethodSubtype mShortcutSubtype;
+
+ private static final int INDEX_NOT_FOUND = -1;
+
+ public static RichInputMethodManager getInstance() {
+ sInstance.checkInitialized();
+ return sInstance;
+ }
+
+ public static void init(final Context context) {
+ sInstance.initInternal(context);
+ }
+
+ private boolean isInitialized() {
+ return mImmWrapper != null;
+ }
+
+ private void checkInitialized() {
+ if (!isInitialized()) {
+ throw new RuntimeException(TAG + " is used before initialization");
+ }
+ }
+
+ private void initInternal(final Context context) {
+ if (isInitialized()) {
+ return;
+ }
+ mImmWrapper = new InputMethodManagerCompatWrapper(context);
+ mContext = context;
+ mInputMethodInfoCache = new InputMethodInfoCache(
+ mImmWrapper.mImm, context.getPackageName());
+
+ // Initialize additional subtypes.
+ SubtypeLocaleUtils.init(context);
+ final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes();
+ mImmWrapper.mImm.setAdditionalInputMethodSubtypes(
+ getInputMethodIdOfThisIme(), additionalSubtypes);
+
+ // Initialize the current input method subtype and the shortcut IME.
+ refreshSubtypeCaches();
+ }
+
+ public InputMethodSubtype[] getAdditionalSubtypes() {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+ final String prefAdditionalSubtypes = Settings.readPrefAdditionalSubtypes(
+ prefs, mContext.getResources());
+ return AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefAdditionalSubtypes);
+ }
+
+ public InputMethodManager getInputMethodManager() {
+ checkInitialized();
+ return mImmWrapper.mImm;
+ }
+
+ public List<InputMethodSubtype> getMyEnabledInputMethodSubtypeList(
+ boolean allowsImplicitlySelectedSubtypes) {
+ return getEnabledInputMethodSubtypeList(
+ getInputMethodInfoOfThisIme(), allowsImplicitlySelectedSubtypes);
+ }
+
+ public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) {
+ if (mImmWrapper.switchToNextInputMethod(token, onlyCurrentIme)) {
+ return true;
+ }
+ // Was not able to call {@link InputMethodManager#switchToNextInputMethodIBinder,boolean)}
+ // because the current device is running ICS or previous and lacks the API.
+ if (switchToNextInputSubtypeInThisIme(token, onlyCurrentIme)) {
+ return true;
+ }
+ return switchToNextInputMethodAndSubtype(token);
+ }
+
+ private boolean switchToNextInputSubtypeInThisIme(final IBinder token,
+ final boolean onlyCurrentIme) {
+ final InputMethodManager imm = mImmWrapper.mImm;
+ final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype();
+ final List<InputMethodSubtype> enabledSubtypes = getMyEnabledInputMethodSubtypeList(
+ true /* allowsImplicitlySelectedSubtypes */);
+ final int currentIndex = getSubtypeIndexInList(currentSubtype, enabledSubtypes);
+ if (currentIndex == INDEX_NOT_FOUND) {
+ Log.w(TAG, "Can't find current subtype in enabled subtypes: subtype="
+ + SubtypeLocaleUtils.getSubtypeNameForLogging(currentSubtype));
+ return false;
+ }
+ final int nextIndex = (currentIndex + 1) % enabledSubtypes.size();
+ if (nextIndex <= currentIndex && !onlyCurrentIme) {
+ // The current subtype is the last or only enabled one and it needs to switch to
+ // next IME.
+ return false;
+ }
+ final InputMethodSubtype nextSubtype = enabledSubtypes.get(nextIndex);
+ setInputMethodAndSubtype(token, nextSubtype);
+ return true;
+ }
+
+ private boolean switchToNextInputMethodAndSubtype(final IBinder token) {
+ final InputMethodManager imm = mImmWrapper.mImm;
+ final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
+ final int currentIndex = getImiIndexInList(getInputMethodInfoOfThisIme(), enabledImis);
+ if (currentIndex == INDEX_NOT_FOUND) {
+ Log.w(TAG, "Can't find current IME in enabled IMEs: IME package="
+ + getInputMethodInfoOfThisIme().getPackageName());
+ return false;
+ }
+ final InputMethodInfo nextImi = getNextNonAuxiliaryIme(currentIndex, enabledImis);
+ final List<InputMethodSubtype> enabledSubtypes = getEnabledInputMethodSubtypeList(nextImi,
+ true /* allowsImplicitlySelectedSubtypes */);
+ if (enabledSubtypes.isEmpty()) {
+ // The next IME has no subtype.
+ imm.setInputMethod(token, nextImi.getId());
+ return true;
+ }
+ final InputMethodSubtype firstSubtype = enabledSubtypes.get(0);
+ imm.setInputMethodAndSubtype(token, nextImi.getId(), firstSubtype);
+ return true;
+ }
+
+ private static int getImiIndexInList(final InputMethodInfo inputMethodInfo,
+ final List<InputMethodInfo> imiList) {
+ final int count = imiList.size();
+ for (int index = 0; index < count; index++) {
+ final InputMethodInfo imi = imiList.get(index);
+ if (imi.equals(inputMethodInfo)) {
+ return index;
+ }
+ }
+ return INDEX_NOT_FOUND;
+ }
+
+ // This method mimics {@link InputMethodManager#switchToNextInputMethod(IBinder,boolean)}.
+ private static InputMethodInfo getNextNonAuxiliaryIme(final int currentIndex,
+ final List<InputMethodInfo> imiList) {
+ final int count = imiList.size();
+ for (int i = 1; i < count; i++) {
+ final int nextIndex = (currentIndex + i) % count;
+ final InputMethodInfo nextImi = imiList.get(nextIndex);
+ if (!isAuxiliaryIme(nextImi)) {
+ return nextImi;
+ }
+ }
+ return imiList.get(currentIndex);
+ }
+
+ // Copied from {@link InputMethodInfo}. See how auxiliary of IME is determined.
+ private static boolean isAuxiliaryIme(final InputMethodInfo imi) {
+ final int count = imi.getSubtypeCount();
+ if (count == 0) {
+ return false;
+ }
+ for (int index = 0; index < count; index++) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+ if (!subtype.isAuxiliary()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static class InputMethodInfoCache {
+ private final InputMethodManager mImm;
+ private final String mImePackageName;
+
+ private InputMethodInfo mCachedThisImeInfo;
+ private final HashMap<InputMethodInfo, List<InputMethodSubtype>>
+ mCachedSubtypeListWithImplicitlySelected;
+ private final HashMap<InputMethodInfo, List<InputMethodSubtype>>
+ mCachedSubtypeListOnlyExplicitlySelected;
+
+ public InputMethodInfoCache(final InputMethodManager imm, final String imePackageName) {
+ mImm = imm;
+ mImePackageName = imePackageName;
+ mCachedSubtypeListWithImplicitlySelected = new HashMap<>();
+ mCachedSubtypeListOnlyExplicitlySelected = new HashMap<>();
+ }
+
+ public synchronized InputMethodInfo getInputMethodOfThisIme() {
+ if (mCachedThisImeInfo != null) {
+ return mCachedThisImeInfo;
+ }
+ for (final InputMethodInfo imi : mImm.getInputMethodList()) {
+ if (imi.getPackageName().equals(mImePackageName)) {
+ mCachedThisImeInfo = imi;
+ return imi;
+ }
+ }
+ throw new RuntimeException("Input method id for " + mImePackageName + " not found.");
+ }
+
+ public synchronized List<InputMethodSubtype> getEnabledInputMethodSubtypeList(
+ final InputMethodInfo imi, final boolean allowsImplicitlySelectedSubtypes) {
+ final HashMap<InputMethodInfo, List<InputMethodSubtype>> cache =
+ allowsImplicitlySelectedSubtypes
+ ? mCachedSubtypeListWithImplicitlySelected
+ : mCachedSubtypeListOnlyExplicitlySelected;
+ final List<InputMethodSubtype> cachedList = cache.get(imi);
+ if (cachedList != null) {
+ return cachedList;
+ }
+ final List<InputMethodSubtype> result = mImm.getEnabledInputMethodSubtypeList(
+ imi, allowsImplicitlySelectedSubtypes);
+ cache.put(imi, result);
+ return result;
+ }
+
+ public synchronized void clear() {
+ mCachedThisImeInfo = null;
+ mCachedSubtypeListWithImplicitlySelected.clear();
+ mCachedSubtypeListOnlyExplicitlySelected.clear();
+ }
+ }
+
+ public InputMethodInfo getInputMethodInfoOfThisIme() {
+ return mInputMethodInfoCache.getInputMethodOfThisIme();
+ }
+
+ public String getInputMethodIdOfThisIme() {
+ return getInputMethodInfoOfThisIme().getId();
+ }
+
+ public boolean checkIfSubtypeBelongsToThisImeAndEnabled(final InputMethodSubtype subtype) {
+ return checkIfSubtypeBelongsToList(subtype,
+ getEnabledInputMethodSubtypeList(
+ getInputMethodInfoOfThisIme(),
+ true /* allowsImplicitlySelectedSubtypes */));
+ }
+
+ public boolean checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(
+ final InputMethodSubtype subtype) {
+ final boolean subtypeEnabled = checkIfSubtypeBelongsToThisImeAndEnabled(subtype);
+ final boolean subtypeExplicitlyEnabled = checkIfSubtypeBelongsToList(subtype,
+ getMyEnabledInputMethodSubtypeList(false /* allowsImplicitlySelectedSubtypes */));
+ return subtypeEnabled && !subtypeExplicitlyEnabled;
+ }
+
+ private static boolean checkIfSubtypeBelongsToList(final InputMethodSubtype subtype,
+ final List<InputMethodSubtype> subtypes) {
+ return getSubtypeIndexInList(subtype, subtypes) != INDEX_NOT_FOUND;
+ }
+
+ private static int getSubtypeIndexInList(final InputMethodSubtype subtype,
+ final List<InputMethodSubtype> subtypes) {
+ final int count = subtypes.size();
+ for (int index = 0; index < count; index++) {
+ final InputMethodSubtype ims = subtypes.get(index);
+ if (ims.equals(subtype)) {
+ return index;
+ }
+ }
+ return INDEX_NOT_FOUND;
+ }
+
+ public void onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) {
+ updateCurrentSubtype(newSubtype);
+ updateShortcutIme();
+ if (DEBUG) {
+ Log.w(TAG, "onSubtypeChanged: " + mCurrentRichInputMethodSubtype.getNameForLogging());
+ }
+ }
+
+ private static RichInputMethodSubtype sForcedSubtypeForTesting = null;
+
+ @UsedForTesting
+ static void forceSubtype(@Nonnull final InputMethodSubtype subtype) {
+ sForcedSubtypeForTesting = RichInputMethodSubtype.getRichInputMethodSubtype(subtype);
+ }
+
+ @Nonnull
+ public Locale getCurrentSubtypeLocale() {
+ if (null != sForcedSubtypeForTesting) {
+ return sForcedSubtypeForTesting.getLocale();
+ }
+ return getCurrentSubtype().getLocale();
+ }
+
+ @Nonnull
+ public RichInputMethodSubtype getCurrentSubtype() {
+ if (null != sForcedSubtypeForTesting) {
+ return sForcedSubtypeForTesting;
+ }
+ return mCurrentRichInputMethodSubtype;
+ }
+
+
+ public String getCombiningRulesExtraValueOfCurrentSubtype() {
+ return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype().getRawSubtype());
+ }
+
+ public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) {
+ final List<InputMethodInfo> enabledImis = mImmWrapper.mImm.getEnabledInputMethodList();
+ return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, enabledImis);
+ }
+
+ public boolean hasMultipleEnabledSubtypesInThisIme(
+ final boolean shouldIncludeAuxiliarySubtypes) {
+ final List<InputMethodInfo> imiList = Collections.singletonList(
+ getInputMethodInfoOfThisIme());
+ return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, imiList);
+ }
+
+ private boolean hasMultipleEnabledSubtypes(final boolean shouldIncludeAuxiliarySubtypes,
+ final List<InputMethodInfo> imiList) {
+ // Number of the filtered IMEs
+ int filteredImisCount = 0;
+
+ for (InputMethodInfo imi : imiList) {
+ // We can return true immediately after we find two or more filtered IMEs.
+ if (filteredImisCount > 1) return true;
+ final List<InputMethodSubtype> subtypes = getEnabledInputMethodSubtypeList(imi, true);
+ // IMEs that have no subtypes should be counted.
+ if (subtypes.isEmpty()) {
+ ++filteredImisCount;
+ continue;
+ }
+
+ int auxCount = 0;
+ for (InputMethodSubtype subtype : subtypes) {
+ if (subtype.isAuxiliary()) {
+ ++auxCount;
+ }
+ }
+ final int nonAuxCount = subtypes.size() - auxCount;
+
+ // IMEs that have one or more non-auxiliary subtypes should be counted.
+ // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
+ // subtypes should be counted as well.
+ if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
+ ++filteredImisCount;
+ }
+ }
+
+ if (filteredImisCount > 1) {
+ return true;
+ }
+ final List<InputMethodSubtype> subtypes = getMyEnabledInputMethodSubtypeList(true);
+ int keyboardCount = 0;
+ // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's
+ // both explicitly and implicitly enabled input method subtype.
+ // (The current IME should be LatinIME.)
+ for (InputMethodSubtype subtype : subtypes) {
+ if (KEYBOARD_MODE.equals(subtype.getMode())) {
+ ++keyboardCount;
+ }
+ }
+ return keyboardCount > 1;
+ }
+
+ public InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(final String localeString,
+ final String keyboardLayoutSetName) {
+ final InputMethodInfo myImi = getInputMethodInfoOfThisIme();
+ final int count = myImi.getSubtypeCount();
+ for (int i = 0; i < count; i++) {
+ final InputMethodSubtype subtype = myImi.getSubtypeAt(i);
+ final String layoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
+ if (localeString.equals(subtype.getLocale())
+ && keyboardLayoutSetName.equals(layoutName)) {
+ return subtype;
+ }
+ }
+ return null;
+ }
+
+ public InputMethodSubtype findSubtypeByLocale(final Locale locale) {
+ // Find the best subtype based on a straightforward matching algorithm.
+ // TODO: Use LocaleList#getFirstMatch() instead.
+ final List<InputMethodSubtype> subtypes =
+ getMyEnabledInputMethodSubtypeList(true /* allowsImplicitlySelectedSubtypes */);
+ final int count = subtypes.size();
+ for (int i = 0; i < count; ++i) {
+ final InputMethodSubtype subtype = subtypes.get(i);
+ final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
+ if (subtypeLocale.equals(locale)) {
+ return subtype;
+ }
+ }
+ for (int i = 0; i < count; ++i) {
+ final InputMethodSubtype subtype = subtypes.get(i);
+ final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
+ if (subtypeLocale.getLanguage().equals(locale.getLanguage()) &&
+ subtypeLocale.getCountry().equals(locale.getCountry()) &&
+ subtypeLocale.getVariant().equals(locale.getVariant())) {
+ return subtype;
+ }
+ }
+ for (int i = 0; i < count; ++i) {
+ final InputMethodSubtype subtype = subtypes.get(i);
+ final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
+ if (subtypeLocale.getLanguage().equals(locale.getLanguage()) &&
+ subtypeLocale.getCountry().equals(locale.getCountry())) {
+ return subtype;
+ }
+ }
+ for (int i = 0; i < count; ++i) {
+ final InputMethodSubtype subtype = subtypes.get(i);
+ final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype);
+ if (subtypeLocale.getLanguage().equals(locale.getLanguage())) {
+ return subtype;
+ }
+ }
+ return null;
+ }
+
+ public void setInputMethodAndSubtype(final IBinder token, final InputMethodSubtype subtype) {
+ mImmWrapper.mImm.setInputMethodAndSubtype(
+ token, getInputMethodIdOfThisIme(), subtype);
+ }
+
+ public void setAdditionalInputMethodSubtypes(final InputMethodSubtype[] subtypes) {
+ mImmWrapper.mImm.setAdditionalInputMethodSubtypes(
+ getInputMethodIdOfThisIme(), subtypes);
+ // Clear the cache so that we go read the {@link InputMethodInfo} of this IME and list of
+ // subtypes again next time.
+ refreshSubtypeCaches();
+ }
+
+ private List<InputMethodSubtype> getEnabledInputMethodSubtypeList(final InputMethodInfo imi,
+ final boolean allowsImplicitlySelectedSubtypes) {
+ return mInputMethodInfoCache.getEnabledInputMethodSubtypeList(
+ imi, allowsImplicitlySelectedSubtypes);
+ }
+
+ public void refreshSubtypeCaches() {
+ mInputMethodInfoCache.clear();
+ updateCurrentSubtype(mImmWrapper.mImm.getCurrentInputMethodSubtype());
+ updateShortcutIme();
+ }
+
+ public boolean shouldOfferSwitchingToNextInputMethod(final IBinder binder,
+ boolean defaultValue) {
+ // Use the default value instead on Jelly Bean MR2 and previous where
+ // {@link InputMethodManager#shouldOfferSwitchingToNextInputMethod} isn't yet available
+ // and on KitKat where the API is still just a stub to return true always.
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
+ return defaultValue;
+ }
+ return mImmWrapper.shouldOfferSwitchingToNextInputMethod(binder);
+ }
+
+ public boolean isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes() {
+ final Locale systemLocale = mContext.getResources().getConfiguration().locale;
+ final Set<InputMethodSubtype> enabledSubtypesOfEnabledImes = new HashSet<>();
+ final InputMethodManager inputMethodManager = getInputMethodManager();
+ final List<InputMethodInfo> enabledInputMethodInfoList =
+ inputMethodManager.getEnabledInputMethodList();
+ for (final InputMethodInfo info : enabledInputMethodInfoList) {
+ final List<InputMethodSubtype> enabledSubtypes =
+ inputMethodManager.getEnabledInputMethodSubtypeList(
+ info, true /* allowsImplicitlySelectedSubtypes */);
+ if (enabledSubtypes.isEmpty()) {
+ // An IME with no subtypes is found.
+ return false;
+ }
+ enabledSubtypesOfEnabledImes.addAll(enabledSubtypes);
+ }
+ for (final InputMethodSubtype subtype : enabledSubtypesOfEnabledImes) {
+ if (!subtype.isAuxiliary() && !subtype.getLocale().isEmpty()
+ && !systemLocale.equals(SubtypeLocaleUtils.getSubtypeLocale(subtype))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void updateCurrentSubtype(@Nullable final InputMethodSubtype subtype) {
+ mCurrentRichInputMethodSubtype = RichInputMethodSubtype.getRichInputMethodSubtype(subtype);
+ }
+
+ private void updateShortcutIme() {
+ if (DEBUG) {
+ Log.d(TAG, "Update shortcut IME from : "
+ + (mShortcutInputMethodInfo == null
+ ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
+ + (mShortcutSubtype == null ? "<null>" : (
+ mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
+ }
+ final RichInputMethodSubtype richSubtype = mCurrentRichInputMethodSubtype;
+ final boolean implicitlyEnabledSubtype = checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(
+ richSubtype.getRawSubtype());
+ final Locale systemLocale = mContext.getResources().getConfiguration().locale;
+ LanguageOnSpacebarUtils.onSubtypeChanged(
+ richSubtype, implicitlyEnabledSubtype, systemLocale);
+ LanguageOnSpacebarUtils.setEnabledSubtypes(getMyEnabledInputMethodSubtypeList(
+ true /* allowsImplicitlySelectedSubtypes */));
+
+ // TODO: Update an icon for shortcut IME
+ final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
+ getInputMethodManager().getShortcutInputMethodsAndSubtypes();
+ mShortcutInputMethodInfo = null;
+ mShortcutSubtype = null;
+ for (final InputMethodInfo imi : shortcuts.keySet()) {
+ final List<InputMethodSubtype> subtypes = shortcuts.get(imi);
+ // TODO: Returns the first found IMI for now. Should handle all shortcuts as
+ // appropriate.
+ mShortcutInputMethodInfo = imi;
+ // TODO: Pick up the first found subtype for now. Should handle all subtypes
+ // as appropriate.
+ mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null;
+ break;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Update shortcut IME to : "
+ + (mShortcutInputMethodInfo == null
+ ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
+ + (mShortcutSubtype == null ? "<null>" : (
+ mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
+ }
+ }
+
+ public void switchToShortcutIme(final InputMethodService context) {
+ if (mShortcutInputMethodInfo == null) {
+ return;
+ }
+
+ final String imiId = mShortcutInputMethodInfo.getId();
+ switchToTargetIME(imiId, mShortcutSubtype, context);
+ }
+
+ private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype,
+ final InputMethodService context) {
+ final IBinder token = context.getWindow().getWindow().getAttributes().token;
+ if (token == null) {
+ return;
+ }
+ final InputMethodManager imm = getInputMethodManager();
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ imm.setInputMethodAndSubtype(token, imiId, subtype);
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ public boolean isShortcutImeReady() {
+ if (mShortcutInputMethodInfo == null) {
+ return false;
+ }
+ if (mShortcutSubtype == null) {
+ return true;
+ }
+ return true;
+ }
+}