diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/RichInputMethodManager.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/RichInputMethodManager.java | 262 |
1 files changed, 204 insertions, 58 deletions
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java index 7cf4eff92..c8e0b93bf 100644 --- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java +++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java @@ -16,10 +16,12 @@ package com.android.inputmethod.latin; -import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; +import static com.android.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; @@ -28,20 +30,30 @@ import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; +import com.android.inputmethod.latin.utils.LanguageOnSpacebarUtils; import com.android.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; /** * Enrichment class for InputMethodManager to simplify interaction and add functionality. */ -public final class RichInputMethodManager { +// 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. @@ -49,12 +61,12 @@ public final class RichInputMethodManager { private static final RichInputMethodManager sInstance = new RichInputMethodManager(); + private Context mContext; private InputMethodManagerCompatWrapper mImmWrapper; private InputMethodInfoCache mInputMethodInfoCache; - final HashMap<InputMethodInfo, List<InputMethodSubtype>> - mSubtypeListCacheWithImplicitlySelectedSubtypes = new HashMap<>(); - final HashMap<InputMethodInfo, List<InputMethodSubtype>> - mSubtypeListCacheWithoutImplicitlySelectedSubtypes = new HashMap<>(); + private RichInputMethodSubtype mCurrentRichInputMethodSubtype; + private InputMethodInfo mShortcutInputMethodInfo; + private InputMethodSubtype mShortcutSubtype; private static final int INDEX_NOT_FOUND = -1; @@ -82,20 +94,24 @@ public final class RichInputMethodManager { return; } mImmWrapper = new InputMethodManagerCompatWrapper(context); + mContext = context; mInputMethodInfoCache = new InputMethodInfoCache( mImmWrapper.mImm, context.getPackageName()); // Initialize additional subtypes. SubtypeLocaleUtils.init(context); - final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes(context); - setAdditionalInputMethodSubtypes(additionalSubtypes); + final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes(); + mImmWrapper.mImm.setAdditionalInputMethodSubtypes( + getInputMethodIdOfThisIme(), additionalSubtypes); + + // Initialize the current input method subtype and the shortcut IME. + refreshSubtypeCaches(); } - public InputMethodSubtype[] getAdditionalSubtypes(final Context context) { - SubtypeLocaleUtils.init(context); - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + public InputMethodSubtype[] getAdditionalSubtypes() { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); final String prefAdditionalSubtypes = Settings.readPrefAdditionalSubtypes( - prefs, context.getResources()); + prefs, mContext.getResources()); return AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefAdditionalSubtypes); } @@ -212,33 +228,57 @@ public final class RichInputMethodManager { private final InputMethodManager mImm; private final String mImePackageName; - private InputMethodInfo mCachedValue; + 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 get() { - if (mCachedValue != null) { - return mCachedValue; + public synchronized InputMethodInfo getInputMethodOfThisIme() { + if (mCachedThisImeInfo != null) { + return mCachedThisImeInfo; } for (final InputMethodInfo imi : mImm.getInputMethodList()) { if (imi.getPackageName().equals(mImePackageName)) { - mCachedValue = imi; + 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() { - mCachedValue = null; + mCachedThisImeInfo = null; + mCachedSubtypeListWithImplicitlySelected.clear(); + mCachedSubtypeListOnlyExplicitlySelected.clear(); } } public InputMethodInfo getInputMethodInfoOfThisIme() { - return mInputMethodInfoCache.get(); + return mInputMethodInfoCache.getInputMethodOfThisIme(); } public String getInputMethodIdOfThisIme() { @@ -246,24 +286,20 @@ public final class RichInputMethodManager { } public boolean checkIfSubtypeBelongsToThisImeAndEnabled(final InputMethodSubtype subtype) { - return checkIfSubtypeBelongsToImeAndEnabled(getInputMethodInfoOfThisIme(), 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 */)); + final boolean subtypeExplicitlyEnabled = checkIfSubtypeBelongsToList(subtype, + getMyEnabledInputMethodSubtypeList(false /* allowsImplicitlySelectedSubtypes */)); return subtypeEnabled && !subtypeExplicitlyEnabled; } - public boolean checkIfSubtypeBelongsToImeAndEnabled(final InputMethodInfo imi, - final InputMethodSubtype subtype) { - return checkIfSubtypeBelongsToList(subtype, getEnabledInputMethodSubtypeList(imi, - true /* allowsImplicitlySelectedSubtypes */)); - } - private static boolean checkIfSubtypeBelongsToList(final InputMethodSubtype subtype, final List<InputMethodSubtype> subtypes) { return getSubtypeIndexInList(subtype, subtypes) != INDEX_NOT_FOUND; @@ -281,26 +317,40 @@ public final class RichInputMethodManager { return INDEX_NOT_FOUND; } - public boolean checkIfSubtypeBelongsToThisIme(final InputMethodSubtype subtype) { - return getSubtypeIndexInIme(subtype, getInputMethodInfoOfThisIme()) != INDEX_NOT_FOUND; + public void onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) { + updateCurrentSubtype(newSubtype); + updateShortcutIme(); + if (DEBUG) { + Log.w(TAG, "onSubtypeChanged: " + mCurrentRichInputMethodSubtype.getNameForLogging()); + } } - private static int getSubtypeIndexInIme(final InputMethodSubtype subtype, - final InputMethodInfo imi) { - final int count = imi.getSubtypeCount(); - for (int index = 0; index < count; index++) { - final InputMethodSubtype ims = imi.getSubtypeAt(index); - if (ims.equals(subtype)) { - return index; - } + private static RichInputMethodSubtype sForcedSubtypeForTesting = null; + + @UsedForTesting + static void forceSubtype(@Nonnull final InputMethodSubtype subtype) { + sForcedSubtypeForTesting = new RichInputMethodSubtype(subtype); + } + + @Nonnull + public Locale[] getCurrentSubtypeLocales() { + if (null != sForcedSubtypeForTesting) { + return sForcedSubtypeForTesting.getLocales(); } - return INDEX_NOT_FOUND; + return getCurrentSubtype().getLocales(); } - public InputMethodSubtype getCurrentInputMethodSubtype( - final InputMethodSubtype defaultSubtype) { - final InputMethodSubtype currentSubtype = mImmWrapper.mImm.getCurrentInputMethodSubtype(); - return (currentSubtype != null) ? currentSubtype : defaultSubtype; + @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) { @@ -343,7 +393,6 @@ public final class RichInputMethodManager { // subtypes should be counted as well. if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) { ++filteredImisCount; - continue; } } @@ -388,27 +437,19 @@ public final class RichInputMethodManager { getInputMethodIdOfThisIme(), subtypes); // Clear the cache so that we go read the {@link InputMethodInfo} of this IME and list of // subtypes again next time. - clearSubtypeCaches(); + refreshSubtypeCaches(); } private List<InputMethodSubtype> getEnabledInputMethodSubtypeList(final InputMethodInfo imi, final boolean allowsImplicitlySelectedSubtypes) { - final HashMap<InputMethodInfo, List<InputMethodSubtype>> cache = - allowsImplicitlySelectedSubtypes - ? mSubtypeListCacheWithImplicitlySelectedSubtypes - : mSubtypeListCacheWithoutImplicitlySelectedSubtypes; - final List<InputMethodSubtype> cachedList = cache.get(imi); - if (null != cachedList) return cachedList; - final List<InputMethodSubtype> result = mImmWrapper.mImm.getEnabledInputMethodSubtypeList( + return mInputMethodInfoCache.getEnabledInputMethodSubtypeList( imi, allowsImplicitlySelectedSubtypes); - cache.put(imi, result); - return result; } - public void clearSubtypeCaches() { - mSubtypeListCacheWithImplicitlySelectedSubtypes.clear(); - mSubtypeListCacheWithoutImplicitlySelectedSubtypes.clear(); + public void refreshSubtypeCaches() { mInputMethodInfoCache.clear(); + updateCurrentSubtype(mImmWrapper.mImm.getCurrentInputMethodSubtype()); + updateShortcutIme(); } public boolean shouldOfferSwitchingToNextInputMethod(final IBinder binder, @@ -421,4 +462,109 @@ public final class RichInputMethodManager { } 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(@Nonnull final InputMethodSubtype subtype) { + mCurrentRichInputMethodSubtype = new RichInputMethodSubtype(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; + } } |