diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java index a01c301ee..8bd1e5208 100644 --- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java +++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java @@ -19,53 +19,21 @@ package com.android.inputmethod.compat; import android.content.Context; import android.os.IBinder; import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.InputMethodSubtype; - -import com.android.inputmethod.latin.ImfUtils; import java.lang.reflect.Method; -// TODO: Override this class with the concrete implementation if we need to take care of the -// performance. public final class InputMethodManagerCompatWrapper { - private static final String TAG = InputMethodManagerCompatWrapper.class.getSimpleName(); private static final Method METHOD_switchToNextInputMethod = CompatUtils.getMethod( InputMethodManager.class, "switchToNextInputMethod", IBinder.class, Boolean.TYPE); - private static final InputMethodManagerCompatWrapper sInstance = - new InputMethodManagerCompatWrapper(); + public final InputMethodManager mImm; - private InputMethodManager mImm; - - private InputMethodManagerCompatWrapper() { - // This wrapper class is not publicly instantiable. + public InputMethodManagerCompatWrapper(final Context context) { + mImm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE); } - public static InputMethodManagerCompatWrapper getInstance() { - if (sInstance.mImm == null) { - throw new RuntimeException(TAG + ".getInstance() is called before initialization"); - } - return sInstance; - } - - public static void init(Context context) { - sInstance.mImm = ImfUtils.getInputMethodManager(context); - } - - public InputMethodSubtype getLastInputMethodSubtype() { - return mImm.getLastInputMethodSubtype(); - } - - public boolean switchToLastInputMethod(IBinder token) { - return mImm.switchToLastInputMethod(token); - } - - public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) { + public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) { return (Boolean)CompatUtils.invoke(mImm, false, METHOD_switchToNextInputMethod, token, onlyCurrentIme); } - - public void showInputMethodPicker() { - mImm.showInputMethodPicker(); - } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 926f3ec28..c2e31ee1e 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -30,11 +30,11 @@ import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetExcep import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; import com.android.inputmethod.keyboard.internal.KeyboardState; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; -import com.android.inputmethod.latin.ImfUtils; import com.android.inputmethod.latin.InputView; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.SettingsValues; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.WordComposer; @@ -142,7 +142,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { builder.setOptions( settingsValues.isVoiceKeyEnabled(editorInfo), settingsValues.isVoiceKeyOnMain(), - settingsValues.isLanguageSwitchKeyEnabled(mThemeContext)); + settingsValues.isLanguageSwitchKeyEnabled()); mKeyboardLayoutSet = builder.build(); try { mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols)); @@ -187,7 +187,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage( keyboard.mId.mLocale); keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage, - ImfUtils.hasMultipleEnabledIMEsOrSubtypes(mLatinIME, true)); + RichInputMethodManager.getInstance().hasMultipleEnabledIMEsOrSubtypes(true)); } public Keyboard getKeyboard() { diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java index d12607721..a071bc9fb 100644 --- a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java +++ b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java @@ -50,6 +50,7 @@ import java.util.ArrayList; import java.util.TreeSet; public final class AdditionalSubtypeSettings extends PreferenceFragment { + private RichInputMethodManager mRichImm; private SharedPreferences mPrefs; private SubtypeLocaleAdapter mSubtypeLocaleAdapter; private KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter; @@ -63,6 +64,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { private static final String KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN = "is_subtype_enabler_notification_dialog_open"; private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler"; + static final class SubtypeLocaleItem extends Pair implements Comparable { public SubtypeLocaleItem(final String localeString, final String displayName) { @@ -90,7 +92,8 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); final TreeSet items = CollectionUtils.newTreeSet(); - final InputMethodInfo imi = ImfUtils.getInputMethodInfoOfThisIme(context); + final InputMethodInfo imi = RichInputMethodManager.getInstance() + .getInputMethodInfoOfThisIme(); final int count = imi.getSubtypeCount(); for (int i = 0; i < count; i++) { final InputMethodSubtype subtype = imi.getSubtypeAt(i); @@ -375,6 +378,8 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); + RichInputMethodManager.init(getActivity()); + mRichImm = RichInputMethodManager.getInstance(); addPreferencesFromResource(R.xml.additional_subtype_settings); setHasOptionsMenu(true); @@ -431,7 +436,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { mIsAddingNewSubtype = false; final PreferenceGroup group = getPreferenceScreen(); group.removePreference(subtypePref); - ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), getSubtypes()); + mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); } @Override @@ -441,7 +446,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { return; } if (findDuplicatedSubtype(subtype) == null) { - ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), getSubtypes()); + mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); return; } @@ -458,7 +463,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { mIsAddingNewSubtype = false; final InputMethodSubtype subtype = subtypePref.getSubtype(); if (findDuplicatedSubtype(subtype) == null) { - ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), getSubtypes()); + mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey(); mSubtypeEnablerNotificationDialog = createDialog(subtypePref); mSubtypeEnablerNotificationDialog.show(); @@ -493,8 +498,8 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { private InputMethodSubtype findDuplicatedSubtype(final InputMethodSubtype subtype) { final String localeString = subtype.getLocale(); final String keyboardLayoutSetName = SubtypeLocale.getKeyboardLayoutSetName(subtype); - return ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - getActivity(), localeString, keyboardLayoutSetName); + return mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + localeString, keyboardLayoutSetName); } private AlertDialog createDialog( @@ -507,7 +512,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { @Override public void onClick(DialogInterface dialog, int which) { final Intent intent = CompatUtils.getInputLanguageSelectionIntent( - ImfUtils.getInputMethodIdOfThisIme(getActivity()), + mRichImm.getInputMethodIdOfThisIme(), Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -565,7 +570,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { } finally { editor.apply(); } - ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), subtypes); + mRichImm.setAdditionalInputMethodSubtypes(subtypes); } @Override diff --git a/java/src/com/android/inputmethod/latin/ImfUtils.java b/java/src/com/android/inputmethod/latin/ImfUtils.java deleted file mode 100644 index 2674e4575..000000000 --- a/java/src/com/android/inputmethod/latin/ImfUtils.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * 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 com.android.inputmethod.latin; - -import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; - -import android.content.Context; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.InputMethodSubtype; - -import java.util.Collections; -import java.util.List; - -/** - * Utility class for Input Method Framework - */ -public final class ImfUtils { - private ImfUtils() { - // This utility class is not publicly instantiable. - } - - private static InputMethodManager sInputMethodManager; - - public static InputMethodManager getInputMethodManager(Context context) { - if (sInputMethodManager == null) { - sInputMethodManager = (InputMethodManager)context.getSystemService( - Context.INPUT_METHOD_SERVICE); - } - return sInputMethodManager; - } - - private static InputMethodInfo sInputMethodInfoOfThisIme; - - public static InputMethodInfo getInputMethodInfoOfThisIme(Context context) { - if (sInputMethodInfoOfThisIme == null) { - final InputMethodManager imm = getInputMethodManager(context); - final String packageName = context.getPackageName(); - for (final InputMethodInfo imi : imm.getInputMethodList()) { - if (imi.getPackageName().equals(packageName)) - return imi; - } - throw new RuntimeException("Can not find input method id for " + packageName); - } - return sInputMethodInfoOfThisIme; - } - - public static String getInputMethodIdOfThisIme(Context context) { - return getInputMethodInfoOfThisIme(context).getId(); - } - - public static boolean checkIfSubtypeBelongsToThisImeAndEnabled(Context context, - InputMethodSubtype ims) { - final InputMethodInfo myImi = getInputMethodInfoOfThisIme(context); - final InputMethodManager imm = getInputMethodManager(context); - // TODO: Cache all subtypes of this IME for optimization - final List subtypes = imm.getEnabledInputMethodSubtypeList(myImi, true); - for (final InputMethodSubtype subtype : subtypes) { - if (subtype.equals(ims)) { - return true; - } - } - return false; - } - - public static boolean checkIfSubtypeBelongsToThisIme(Context context, - InputMethodSubtype ims) { - final InputMethodInfo myImi = getInputMethodInfoOfThisIme(context); - final int count = myImi.getSubtypeCount(); - for (int i = 0; i < count; i++) { - final InputMethodSubtype subtype = myImi.getSubtypeAt(i); - if (subtype.equals(ims)) { - return true; - } - } - return false; - } - - public static InputMethodSubtype getCurrentInputMethodSubtype(Context context, - InputMethodSubtype defaultSubtype) { - final InputMethodManager imm = getInputMethodManager(context); - final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype(); - return (currentSubtype != null) ? currentSubtype : defaultSubtype; - } - - public static boolean hasMultipleEnabledIMEsOrSubtypes(Context context, - final boolean shouldIncludeAuxiliarySubtypes) { - final InputMethodManager imm = getInputMethodManager(context); - final List enabledImis = imm.getEnabledInputMethodList(); - return hasMultipleEnabledSubtypes(context, shouldIncludeAuxiliarySubtypes, enabledImis); - } - - public static boolean hasMultipleEnabledSubtypesInThisIme(Context context, - final boolean shouldIncludeAuxiliarySubtypes) { - final InputMethodInfo myImi = getInputMethodInfoOfThisIme(context); - final List imiList = Collections.singletonList(myImi); - return hasMultipleEnabledSubtypes(context, shouldIncludeAuxiliarySubtypes, imiList); - } - - private static boolean hasMultipleEnabledSubtypes(Context context, - final boolean shouldIncludeAuxiliarySubtypes, List imiList) { - final InputMethodManager imm = getInputMethodManager(context); - - // 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 subtypes = - imm.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; - continue; - } - } - - if (filteredImisCount > 1) { - return true; - } - final List subtypes = imm.getEnabledInputMethodSubtypeList(null, 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 static InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet( - Context context, String localeString, String keyboardLayoutSetName) { - final InputMethodInfo imi = getInputMethodInfoOfThisIme(context); - final int count = imi.getSubtypeCount(); - for (int i = 0; i < count; i++) { - final InputMethodSubtype subtype = imi.getSubtypeAt(i); - final String layoutName = SubtypeLocale.getKeyboardLayoutSetName(subtype); - if (localeString.equals(subtype.getLocale()) - && keyboardLayoutSetName.equals(layoutName)) { - return subtype; - } - } - return null; - } - - public static void setAdditionalInputMethodSubtypes(Context context, - InputMethodSubtype[] subtypes) { - final InputMethodManager imm = getInputMethodManager(context); - final String imiId = getInputMethodIdOfThisIme(context); - imm.setAdditionalInputMethodSubtypes(imiId, subtypes); - } -} diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 5f87c8c91..049d13472 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -56,13 +56,13 @@ import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.CompatUtils; -import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; import com.android.inputmethod.compat.InputMethodServiceCompatUtils; import com.android.inputmethod.compat.SuggestionSpanUtils; import com.android.inputmethod.keyboard.KeyDetector; @@ -137,7 +137,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private CompletionInfo[] mApplicationSpecifiedCompletions; private ApplicationInfo mTargetApplicationInfo; - private InputMethodManagerCompatWrapper mImm; + private RichInputMethodManager mRichImm; private Resources mResources; private SharedPreferences mPrefs; @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher; @@ -381,14 +381,14 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.getInstance().init(this, prefs); } - InputMethodManagerCompatWrapper.init(this); + RichInputMethodManager.init(this); SubtypeSwitcher.init(this); KeyboardSwitcher.init(this, prefs); AccessibilityUtils.init(this); super.onCreate(); - mImm = InputMethodManagerCompatWrapper.getInstance(); + mRichImm = RichInputMethodManager.getInstance(); mHandler.onCreate(); DEBUG = LatinImeLogger.sDBG; @@ -397,7 +397,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction loadSettings(); - ImfUtils.setAdditionalInputMethodSubtypes(this, mCurrentSettings.getAdditionalSubtypes()); + mRichImm.setAdditionalInputMethodSubtypes(mCurrentSettings.getAdditionalSubtypes()); initSuggest(); @@ -548,7 +548,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction @Override public void onConfigurationChanged(final Configuration conf) { // System locale has been changed. Needs to reload keyboard. - if (mSubtypeSwitcher.onConfigurationChanged(conf, this)) { + if (mSubtypeSwitcher.onConfigurationChanged(conf)) { loadKeyboard(); } // If orientation changed while predicting, commit the change @@ -690,8 +690,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction .updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled(); if (!currentSubtypeEnabled) { // Current subtype is disabled. Needs to update subtype and keyboard. - final InputMethodSubtype newSubtype = ImfUtils.getCurrentInputMethodSubtype( - this, mSubtypeSwitcher.getNoLanguageSubtype()); + final InputMethodSubtype newSubtype = mRichImm.getCurrentInputMethodSubtype( + mSubtypeSwitcher.getNoLanguageSubtype()); mSubtypeSwitcher.updateSubtype(newSubtype); loadKeyboard(); } @@ -1208,9 +1208,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (isShowingOptionDialog()) return false; switch (requestCode) { case CODE_SHOW_INPUT_METHOD_PICKER: - if (ImfUtils.hasMultipleEnabledIMEsOrSubtypes( - this, true /* include aux subtypes */)) { - mImm.showInputMethodPicker(); + if (mRichImm.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) { + mRichImm.getInputMethodManager().showInputMethodPicker(); return true; } return false; @@ -1234,21 +1233,22 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private void handleLanguageSwitchKey() { final IBinder token = getWindow().getWindow().getAttributes().token; if (mCurrentSettings.mIncludesOtherImesInLanguageSwitchList) { - mImm.switchToNextInputMethod(token, false /* onlyCurrentIme */); + mRichImm.switchToNextInputMethod(token, false /* onlyCurrentIme */); return; } if (mShouldSwitchToLastSubtype) { - final InputMethodSubtype lastSubtype = mImm.getLastInputMethodSubtype(); + final InputMethodManager imm = mRichImm.getInputMethodManager(); + final InputMethodSubtype lastSubtype = imm.getLastInputMethodSubtype(); final boolean lastSubtypeBelongsToThisIme = - ImfUtils.checkIfSubtypeBelongsToThisImeAndEnabled(this, lastSubtype); - if (lastSubtypeBelongsToThisIme && mImm.switchToLastInputMethod(token)) { + mRichImm.checkIfSubtypeBelongsToThisImeAndEnabled(lastSubtype); + if (lastSubtypeBelongsToThisIme && imm.switchToLastInputMethod(token)) { mShouldSwitchToLastSubtype = false; } else { - mImm.switchToNextInputMethod(token, true /* onlyCurrentIme */); + mRichImm.switchToNextInputMethod(token, true /* onlyCurrentIme */); mShouldSwitchToLastSubtype = true; } } else { - mImm.switchToNextInputMethod(token, true /* onlyCurrentIme */); + mRichImm.switchToNextInputMethod(token, true /* onlyCurrentIme */); } } @@ -2345,7 +2345,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction getString(R.string.language_selection_title), getString(R.string.english_ime_settings), }; - final Context context = this; final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface di, int position) { @@ -2353,7 +2352,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction switch (position) { case 0: Intent intent = CompatUtils.getInputLanguageSelectionIntent( - ImfUtils.getInputMethodIdOfThisIme(context), + mRichImm.getInputMethodIdOfThisIme(), Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java new file mode 100644 index 000000000..d7055f747 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java @@ -0,0 +1,213 @@ +/* + * 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 com.android.inputmethod.latin; + +import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; + +import android.content.Context; +import android.os.IBinder; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; + +import java.util.Collections; +import java.util.List; + +/** + * Enrichment class for InputMethodManager to simplify interaction and add functionality. + */ +public final class RichInputMethodManager { + private static final String TAG = RichInputMethodManager.class.getSimpleName(); + + private RichInputMethodManager() { + // This utility class is not publicly instantiable. + } + + private static final RichInputMethodManager sInstance = new RichInputMethodManager(); + + private InputMethodManagerCompatWrapper mImmWrapper; + private InputMethodInfo mInputMethodInfoOfThisIme; + + public static RichInputMethodManager getInstance() { + sInstance.checkInitialized(); + return sInstance; + } + + public static void init(final Context context) { + sInstance.initInternal(context); + } + + private void checkInitialized() { + if (mImmWrapper == null) { + throw new RuntimeException(TAG + " is used before initialization"); + } + } + + private void initInternal(final Context context) { + mImmWrapper = new InputMethodManagerCompatWrapper(context); + mInputMethodInfoOfThisIme = getInputMethodInfoOfThisIme(context); + } + + public InputMethodManager getInputMethodManager() { + checkInitialized(); + return mImmWrapper.mImm; + } + + private InputMethodInfo getInputMethodInfoOfThisIme(final Context context) { + final String packageName = context.getPackageName(); + for (final InputMethodInfo imi : mImmWrapper.mImm.getInputMethodList()) { + if (imi.getPackageName().equals(packageName)) { + return imi; + } + } + throw new RuntimeException("Input method id for " + packageName + " not found."); + } + + public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) { + final boolean result = mImmWrapper.switchToNextInputMethod(token, onlyCurrentIme); + if (!result) { + mImmWrapper.mImm.switchToLastInputMethod(token); + return false; + } + return true; + } + + public InputMethodInfo getInputMethodInfoOfThisIme() { + return mInputMethodInfoOfThisIme; + } + + public String getInputMethodIdOfThisIme() { + return mInputMethodInfoOfThisIme.getId(); + } + + public boolean checkIfSubtypeBelongsToThisImeAndEnabled(final InputMethodSubtype ims) { + return checkIfSubtypeBelongsToImeAndEnabled(mInputMethodInfoOfThisIme, ims); + } + + public boolean checkIfSubtypeBelongsToImeAndEnabled(final InputMethodInfo imi, + final InputMethodSubtype ims) { + final List subtypes = mImmWrapper.mImm.getEnabledInputMethodSubtypeList( + imi, true /* allowsImplicitlySelectedSubtypes */); + for (final InputMethodSubtype subtype : subtypes) { + if (subtype.equals(ims)) { + return true; + } + } + return false; + } + + public boolean checkIfSubtypeBelongsToThisIme(final InputMethodSubtype ims) { + final InputMethodInfo myImi = mInputMethodInfoOfThisIme; + final int count = myImi.getSubtypeCount(); + for (int i = 0; i < count; i++) { + final InputMethodSubtype subtype = myImi.getSubtypeAt(i); + if (subtype.equals(ims)) { + return true; + } + } + return false; + } + + public InputMethodSubtype getCurrentInputMethodSubtype( + final InputMethodSubtype defaultSubtype) { + final InputMethodSubtype currentSubtype = mImmWrapper.mImm.getCurrentInputMethodSubtype(); + return (currentSubtype != null) ? currentSubtype : defaultSubtype; + } + + public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) { + final List enabledImis = mImmWrapper.mImm.getEnabledInputMethodList(); + return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, enabledImis); + } + + public boolean hasMultipleEnabledSubtypesInThisIme( + final boolean shouldIncludeAuxiliarySubtypes) { + final List imiList = Collections.singletonList(mInputMethodInfoOfThisIme); + return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, imiList); + } + + private boolean hasMultipleEnabledSubtypes(final boolean shouldIncludeAuxiliarySubtypes, + final List 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 subtypes = + mImmWrapper.mImm.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; + continue; + } + } + + if (filteredImisCount > 1) { + return true; + } + final List subtypes = + mImmWrapper.mImm.getEnabledInputMethodSubtypeList(null, 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 = mInputMethodInfoOfThisIme; + final int count = myImi.getSubtypeCount(); + for (int i = 0; i < count; i++) { + final InputMethodSubtype subtype = myImi.getSubtypeAt(i); + final String layoutName = SubtypeLocale.getKeyboardLayoutSetName(subtype); + if (localeString.equals(subtype.getLocale()) + && keyboardLayoutSetName.equals(layoutName)) { + return subtype; + } + } + return null; + } + + public void setAdditionalInputMethodSubtypes(final InputMethodSubtype[] subtypes) { + mImmWrapper.mImm.setAdditionalInputMethodSubtypes( + mInputMethodInfoOfThisIme.getId(), subtypes); + } +} diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 2a778aa0d..61536b573 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -355,16 +355,15 @@ public final class SettingsValues { return prefs.getBoolean(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, true); } - public boolean isLanguageSwitchKeyEnabled(final Context context) { + public boolean isLanguageSwitchKeyEnabled() { if (!mShowsLanguageSwitchKey) { return false; } + final RichInputMethodManager imm = RichInputMethodManager.getInstance(); if (mIncludesOtherImesInLanguageSwitchList) { - return ImfUtils.hasMultipleEnabledIMEsOrSubtypes( - context, /* include aux subtypes */false); + return imm.hasMultipleEnabledIMEsOrSubtypes(false /* include aux subtypes */); } else { - return ImfUtils.hasMultipleEnabledSubtypesInThisIme( - context, /* include aux subtypes */false); + return imm.hasMultipleEnabledSubtypesInThisIme(false /* include aux subtypes */); } } diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 8e51a372b..0f339eb3b 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -43,7 +43,7 @@ public final class SubtypeSwitcher { private static final String TAG = SubtypeSwitcher.class.getSimpleName(); private static final SubtypeSwitcher sInstance = new SubtypeSwitcher(); - private /* final */ InputMethodManager mImm; + private /* final */ RichInputMethodManager mRichImm; private /* final */ Resources mResources; private /* final */ ConnectivityManager mConnectivityManager; @@ -84,7 +84,7 @@ public final class SubtypeSwitcher { public static void init(final Context context) { SubtypeLocale.init(context); sInstance.initialize(context); - sInstance.updateAllParameters(context); + sInstance.updateAllParameters(); } private SubtypeSwitcher() { @@ -93,13 +93,13 @@ public final class SubtypeSwitcher { private void initialize(final Context service) { mResources = service.getResources(); - mImm = ImfUtils.getInputMethodManager(service); + mRichImm = RichInputMethodManager.getInstance(); mConnectivityManager = (ConnectivityManager) service.getSystemService( Context.CONNECTIVITY_SERVICE); mCurrentSystemLocale = mResources.getConfiguration().locale; - mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY); - mCurrentSubtype = ImfUtils.getCurrentInputMethodSubtype(service, mNoLanguageSubtype); + mNoLanguageSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY); + mCurrentSubtype = mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype); if (mNoLanguageSubtype == null) { throw new RuntimeException("Can't find no lanugage with QWERTY subtype"); } @@ -110,9 +110,9 @@ public final class SubtypeSwitcher { // Update all parameters stored in SubtypeSwitcher. // Only configuration changed event is allowed to call this because this is heavy. - private void updateAllParameters(final Context context) { + private void updateAllParameters() { mCurrentSystemLocale = mResources.getConfiguration().locale; - updateSubtype(ImfUtils.getCurrentInputMethodSubtype(context, mNoLanguageSubtype)); + updateSubtype(mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype)); updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled(); } @@ -137,7 +137,7 @@ public final class SubtypeSwitcher { */ private boolean updateEnabledSubtypesAndReturnIfEnabled(final InputMethodSubtype subtype) { final List enabledSubtypesOfThisIme = - mImm.getEnabledInputMethodSubtypeList(null, true); + mRichImm.getInputMethodManager().getEnabledInputMethodSubtypeList(null, true); mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size()); for (final InputMethodSubtype ims : enabledSubtypesOfThisIme) { @@ -162,7 +162,7 @@ public final class SubtypeSwitcher { } // TODO: Update an icon for shortcut IME final Map> shortcuts = - mImm.getShortcutInputMethodsAndSubtypes(); + mRichImm.getInputMethodManager().getShortcutInputMethodsAndSubtypes(); mShortcutInputMethodInfo = null; mShortcutSubtype = null; for (final InputMethodInfo imi : shortcuts.keySet()) { @@ -221,7 +221,7 @@ public final class SubtypeSwitcher { if (token == null) { return; } - final InputMethodManager imm = mImm; + final InputMethodManager imm = mRichImm.getInputMethodManager(); new AsyncTask() { @Override protected Void doInBackground(Void... params) { @@ -238,14 +238,8 @@ public final class SubtypeSwitcher { if (mShortcutSubtype == null) { return true; } - final boolean allowsImplicitlySelectedSubtypes = true; - for (final InputMethodSubtype enabledSubtype : mImm.getEnabledInputMethodSubtypeList( - mShortcutInputMethodInfo, allowsImplicitlySelectedSubtypes)) { - if (enabledSubtype.equals(mShortcutSubtype)) { - return true; - } - } - return false; + return mRichImm.checkIfSubtypeBelongsToImeAndEnabled( + mShortcutInputMethodInfo, mShortcutSubtype); } public boolean isShortcutImeReady() { @@ -285,12 +279,12 @@ public final class SubtypeSwitcher { return SubtypeLocale.getSubtypeLocale(mCurrentSubtype); } - public boolean onConfigurationChanged(final Configuration conf, final Context context) { + public boolean onConfigurationChanged(final Configuration conf) { final Locale systemLocale = conf.locale; final boolean systemLocaleChanged = !systemLocale.equals(mCurrentSystemLocale); // If system configuration was changed, update all parameters. if (systemLocaleChanged) { - updateAllParameters(context); + updateAllParameters(); } return systemLocaleChanged; } diff --git a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java index bc5043911..015c2711c 100644 --- a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java +++ b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java @@ -23,7 +23,7 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.latin.AdditionalSubtype; import com.android.inputmethod.latin.CollectionUtils; -import com.android.inputmethod.latin.ImfUtils; +import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.StringUtils; import com.android.inputmethod.latin.SubtypeLocale; @@ -34,12 +34,15 @@ public class SpacebarTextTests extends AndroidTestCase { // Locale to subtypes list. private final ArrayList mSubtypesList = CollectionUtils.newArrayList(); + private RichInputMethodManager mRichImm; private Resources mRes; @Override protected void setUp() throws Exception { super.setUp(); final Context context = getContext(); + RichInputMethodManager.init(context); + mRichImm = RichInputMethodManager.getInstance(); mRes = context.getResources(); SubtypeLocale.init(context); } @@ -103,19 +106,18 @@ public class SpacebarTextTests extends AndroidTestCase { // zz azerty T AZERTY AZERTY public void testPredefinedSubtypes() { - final Context context = getContext(); - final InputMethodSubtype EN_US = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.US.toString(), "qwerty"); - final InputMethodSubtype EN_GB = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.UK.toString(), "qwerty"); - final InputMethodSubtype FR = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.FRENCH.toString(), "azerty"); - final InputMethodSubtype FR_CA = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.CANADA_FRENCH.toString(), "qwerty"); - final InputMethodSubtype DE = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.GERMAN.toString(), "qwertz"); - final InputMethodSubtype ZZ = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, SubtypeLocale.NO_LANGUAGE, "qwerty"); + final InputMethodSubtype EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.US.toString(), "qwerty"); + final InputMethodSubtype EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.UK.toString(), "qwerty"); + final InputMethodSubtype FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.FRENCH.toString(), "azerty"); + final InputMethodSubtype FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.CANADA_FRENCH.toString(), "qwerty"); + final InputMethodSubtype DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.GERMAN.toString(), "qwertz"); + final InputMethodSubtype ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + SubtypeLocale.NO_LANGUAGE, "qwerty"); assertEquals("en_US", "English (US)", MainKeyboardView.getFullDisplayName(EN_US, mRes)); diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java index 52a3745fa..0a48f0d8b 100644 --- a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java +++ b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java @@ -30,12 +30,15 @@ public class SubtypeLocaleTests extends AndroidTestCase { // Locale to subtypes list. private final ArrayList mSubtypesList = CollectionUtils.newArrayList(); + private RichInputMethodManager mRichImm; private Resources mRes; @Override protected void setUp() throws Exception { super.setUp(); final Context context = getContext(); + RichInputMethodManager.init(context); + mRichImm = RichInputMethodManager.getInstance(); mRes = context.getResources(); SubtypeLocale.init(context); } @@ -70,19 +73,18 @@ public class SubtypeLocaleTests extends AndroidTestCase { // zz azerty T No language (AZERTY) in system locale public void testPredefinedSubtypesInEnglish() { - final Context context = getContext(); - final InputMethodSubtype EN_US = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.US.toString(), "qwerty"); - final InputMethodSubtype EN_GB = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.UK.toString(), "qwerty"); - final InputMethodSubtype FR = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.FRENCH.toString(), "azerty"); - final InputMethodSubtype FR_CA = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.CANADA_FRENCH.toString(), "qwerty"); - final InputMethodSubtype DE = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.GERMAN.toString(), "qwertz"); - final InputMethodSubtype ZZ = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, SubtypeLocale.NO_LANGUAGE, "qwerty"); + final InputMethodSubtype EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.US.toString(), "qwerty"); + final InputMethodSubtype EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.UK.toString(), "qwerty"); + final InputMethodSubtype FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.FRENCH.toString(), "azerty"); + final InputMethodSubtype FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.CANADA_FRENCH.toString(), "qwerty"); + final InputMethodSubtype DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.GERMAN.toString(), "qwertz"); + final InputMethodSubtype ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + SubtypeLocale.NO_LANGUAGE, "qwerty"); assertEquals("en_US", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(EN_US)); assertEquals("en_GB", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(EN_GB)); @@ -140,19 +142,18 @@ public class SubtypeLocaleTests extends AndroidTestCase { } public void testPredefinedSubtypesInFrench() { - final Context context = getContext(); - final InputMethodSubtype EN_US = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.US.toString(), "qwerty"); - final InputMethodSubtype EN_GB = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.UK.toString(), "qwerty"); - final InputMethodSubtype FR = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.FRENCH.toString(), "azerty"); - final InputMethodSubtype FR_CA = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.CANADA_FRENCH.toString(), "qwerty"); - final InputMethodSubtype DE = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, Locale.GERMAN.toString(), "qwertz"); - final InputMethodSubtype ZZ = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - context, SubtypeLocale.NO_LANGUAGE, "qwerty"); + final InputMethodSubtype EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.US.toString(), "qwerty"); + final InputMethodSubtype EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.UK.toString(), "qwerty"); + final InputMethodSubtype FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.FRENCH.toString(), "azerty"); + final InputMethodSubtype FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.CANADA_FRENCH.toString(), "qwerty"); + final InputMethodSubtype DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.GERMAN.toString(), "qwertz"); + final InputMethodSubtype ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + SubtypeLocale.NO_LANGUAGE, "qwerty"); final RunInLocale tests = new RunInLocale() { @Override