| /* |
| * Copyright (C) 2010 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.ExtraValue.REQ_NETWORK_CONNECTIVITY; |
| |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.res.Resources; |
| import android.inputmethodservice.InputMethodService; |
| import android.net.ConnectivityManager; |
| import android.net.NetworkInfo; |
| import android.os.AsyncTask; |
| import android.os.IBinder; |
| import android.util.Log; |
| 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.keyboard.KeyboardSwitcher; |
| |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| public final class SubtypeSwitcher { |
| private static boolean DBG = LatinImeLogger.sDBG; |
| private static final String TAG = SubtypeSwitcher.class.getSimpleName(); |
| |
| private static final SubtypeSwitcher sInstance = new SubtypeSwitcher(); |
| private /* final */ RichInputMethodManager mRichImm; |
| private /* final */ Resources mResources; |
| private /* final */ ConnectivityManager mConnectivityManager; |
| |
| /*-----------------------------------------------------------*/ |
| // Variants which should be changed only by reload functions. |
| private NeedsToDisplayLanguage mNeedsToDisplayLanguage = new NeedsToDisplayLanguage(); |
| private InputMethodInfo mShortcutInputMethodInfo; |
| private InputMethodSubtype mShortcutSubtype; |
| private InputMethodSubtype mNoLanguageSubtype; |
| /*-----------------------------------------------------------*/ |
| |
| private boolean mIsNetworkConnected; |
| |
| static final class NeedsToDisplayLanguage { |
| private int mEnabledSubtypeCount; |
| private boolean mIsSystemLanguageSameAsInputLanguage; |
| |
| public boolean getValue() { |
| return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage; |
| } |
| |
| public void updateEnabledSubtypeCount(final int count) { |
| mEnabledSubtypeCount = count; |
| } |
| |
| public void updateIsSystemLanguageSameAsInputLanguage(final boolean isSame) { |
| mIsSystemLanguageSameAsInputLanguage = isSame; |
| } |
| } |
| |
| public static SubtypeSwitcher getInstance() { |
| return sInstance; |
| } |
| |
| public static void init(final Context context) { |
| SubtypeLocale.init(context); |
| RichInputMethodManager.init(context); |
| sInstance.initialize(context); |
| } |
| |
| private SubtypeSwitcher() { |
| // Intentional empty constructor for singleton. |
| } |
| |
| private void initialize(final Context context) { |
| if (mResources != null) { |
| return; |
| } |
| mResources = context.getResources(); |
| mRichImm = RichInputMethodManager.getInstance(); |
| mConnectivityManager = (ConnectivityManager) context.getSystemService( |
| Context.CONNECTIVITY_SERVICE); |
| mNoLanguageSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( |
| SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY); |
| if (mNoLanguageSubtype == null) { |
| throw new RuntimeException("Can't find no lanugage with QWERTY subtype"); |
| } |
| |
| final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); |
| mIsNetworkConnected = (info != null && info.isConnected()); |
| |
| onSubtypeChanged(getCurrentSubtype()); |
| updateParametersOnStartInputView(); |
| } |
| |
| /** |
| * Update parameters which are changed outside LatinIME. This parameters affect UI so that they |
| * should be updated every time onStartInputView is called. |
| */ |
| public void updateParametersOnStartInputView() { |
| final List<InputMethodSubtype> enabledSubtypesOfThisIme = |
| mRichImm.getMyEnabledInputMethodSubtypeList(true); |
| mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size()); |
| updateShortcutIME(); |
| } |
| |
| private void updateShortcutIME() { |
| if (DBG) { |
| Log.d(TAG, "Update shortcut IME from : " |
| + (mShortcutInputMethodInfo == null |
| ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " |
| + (mShortcutSubtype == null ? "<null>" : ( |
| mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); |
| } |
| // TODO: Update an icon for shortcut IME |
| final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts = |
| mRichImm.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 (DBG) { |
| Log.d(TAG, "Update shortcut IME to : " |
| + (mShortcutInputMethodInfo == null |
| ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " |
| + (mShortcutSubtype == null ? "<null>" : ( |
| mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); |
| } |
| } |
| |
| // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function. |
| public void onSubtypeChanged(final InputMethodSubtype newSubtype) { |
| if (DBG) { |
| Log.w(TAG, "onSubtypeChanged: " + SubtypeLocale.getSubtypeDisplayName(newSubtype)); |
| } |
| |
| final Locale newLocale = SubtypeLocale.getSubtypeLocale(newSubtype); |
| final Locale systemLocale = mResources.getConfiguration().locale; |
| final boolean sameLocale = systemLocale.equals(newLocale); |
| final boolean sameLanguage = systemLocale.getLanguage().equals(newLocale.getLanguage()); |
| final boolean implicitlyEnabled = |
| mRichImm.checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype); |
| mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage( |
| sameLocale || (sameLanguage && implicitlyEnabled)); |
| |
| updateShortcutIME(); |
| } |
| |
| //////////////////////////// |
| // Shortcut IME functions // |
| //////////////////////////// |
| |
| 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 = mRichImm.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 isShortcutImeEnabled() { |
| if (mShortcutInputMethodInfo == null) { |
| return false; |
| } |
| if (mShortcutSubtype == null) { |
| return true; |
| } |
| return mRichImm.checkIfSubtypeBelongsToImeAndEnabled( |
| mShortcutInputMethodInfo, mShortcutSubtype); |
| } |
| |
| public boolean isShortcutImeReady() { |
| if (mShortcutInputMethodInfo == null) |
| return false; |
| if (mShortcutSubtype == null) |
| return true; |
| if (mShortcutSubtype.containsExtraValueKey(REQ_NETWORK_CONNECTIVITY)) { |
| return mIsNetworkConnected; |
| } |
| return true; |
| } |
| |
| public void onNetworkStateChanged(final Intent intent) { |
| final boolean noConnection = intent.getBooleanExtra( |
| ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); |
| mIsNetworkConnected = !noConnection; |
| |
| KeyboardSwitcher.getInstance().onNetworkStateChanged(); |
| } |
| |
| ////////////////////////////////// |
| // Subtype Switching functions // |
| ////////////////////////////////// |
| |
| public boolean needsToDisplayLanguage(final Locale keyboardLocale) { |
| if (keyboardLocale.toString().equals(SubtypeLocale.NO_LANGUAGE)) { |
| return true; |
| } |
| if (!keyboardLocale.equals(getCurrentSubtypeLocale())) { |
| return false; |
| } |
| return mNeedsToDisplayLanguage.getValue(); |
| } |
| |
| private static Locale sForcedLocaleForTesting = null; |
| @UsedForTesting |
| void forceLocale(final Locale locale) { |
| sForcedLocaleForTesting = locale; |
| } |
| |
| public Locale getCurrentSubtypeLocale() { |
| if (null != sForcedLocaleForTesting) return sForcedLocaleForTesting; |
| return SubtypeLocale.getSubtypeLocale(getCurrentSubtype()); |
| } |
| |
| public InputMethodSubtype getCurrentSubtype() { |
| return mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype); |
| } |
| |
| public InputMethodSubtype getNoLanguageSubtype() { |
| return mNoLanguageSubtype; |
| } |
| } |