/* * 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.hareime.latin; import static com.android.hareime.latin.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; 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.hareime.keyboard.KeyboardSwitcher; import java.util.List; import java.util.Locale; import java.util.Map; public 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 */ LatinIME mService; private /* final */ InputMethodManager mImm; 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; // Note: This variable is always non-null after {@link #initialize(LatinIME)}. private InputMethodSubtype mCurrentSubtype; private Locale mCurrentSystemLocale; /*-----------------------------------------------------------*/ private boolean mIsNetworkConnected; static class NeedsToDisplayLanguage { private int mEnabledSubtypeCount; private boolean mIsSystemLanguageSameAsInputLanguage; public boolean getValue() { return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage; } public void updateEnabledSubtypeCount(int count) { mEnabledSubtypeCount = count; } public void updateIsSystemLanguageSameAsInputLanguage(boolean isSame) { mIsSystemLanguageSameAsInputLanguage = isSame; } } public static SubtypeSwitcher getInstance() { return sInstance; } public static void init(LatinIME service) { SubtypeLocale.init(service); sInstance.initialize(service); sInstance.updateAllParameters(); } private SubtypeSwitcher() { // Intentional empty constructor for singleton. } private void initialize(LatinIME service) { mService = service; mResources = service.getResources(); mImm = ImfUtils.getInputMethodManager(service); mConnectivityManager = (ConnectivityManager) service.getSystemService( Context.CONNECTIVITY_SERVICE); mCurrentSystemLocale = mResources.getConfiguration().locale; mCurrentSubtype = mImm.getCurrentInputMethodSubtype(); mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( service, 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()); } // Update all parameters stored in SubtypeSwitcher. // Only configuration changed event is allowed to call this because this is heavy. private void updateAllParameters() { mCurrentSystemLocale = mResources.getConfiguration().locale; updateSubtype(mImm.getCurrentInputMethodSubtype()); updateParametersOnStartInputView(); } // Update parameters which are changed outside LatinIME. This parameters affect UI so they // should be updated every time onStartInputview. public void updateParametersOnStartInputView() { updateEnabledSubtypes(); updateShortcutIME(); } // Reload enabledSubtypes from the framework. private void updateEnabledSubtypes() { final InputMethodSubtype currentSubtype = mCurrentSubtype; boolean foundCurrentSubtypeBecameDisabled = true; final List<InputMethodSubtype> enabledSubtypesOfThisIme = mImm.getEnabledInputMethodSubtypeList(null, true); for (InputMethodSubtype ims : enabledSubtypesOfThisIme) { if (ims.equals(currentSubtype)) { foundCurrentSubtypeBecameDisabled = false; } } mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size()); if (foundCurrentSubtypeBecameDisabled) { if (DBG) { Log.w(TAG, "Last subtype: " + currentSubtype.getLocale() + "/" + currentSubtype.getExtraValue()); Log.w(TAG, "Last subtype was disabled. Update to the current one."); } updateSubtype(mImm.getCurrentInputMethodSubtype()); } } 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 = mImm.getShortcutInputMethodsAndSubtypes(); mShortcutInputMethodInfo = null; mShortcutSubtype = null; for (InputMethodInfo imi : shortcuts.keySet()) { 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 updateSubtype(InputMethodSubtype newSubtype) { if (DBG) { Log.w(TAG, "onCurrentInputMethodSubtypeChanged: to: " + newSubtype.getLocale() + "/" + newSubtype.getExtraValue() + ", from: " + mCurrentSubtype.getLocale() + "/" + mCurrentSubtype.getExtraValue()); } final Locale newLocale = SubtypeLocale.getSubtypeLocale(newSubtype); mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage( mCurrentSystemLocale.equals(newLocale)); if (newSubtype.equals(mCurrentSubtype)) return; mCurrentSubtype = newSubtype; updateShortcutIME(); mService.onRefreshKeyboard(); } //////////////////////////// // Shortcut IME functions // //////////////////////////// public void switchToShortcutIME() { if (mShortcutInputMethodInfo == null) { return; } final String imiId = mShortcutInputMethodInfo.getId(); switchToTargetIME(imiId, mShortcutSubtype); } private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype) { final IBinder token = mService.getWindow().getWindow().getAttributes().token; if (token == null) { return; } final InputMethodManager imm = mImm; 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; } final boolean allowsImplicitlySelectedSubtypes = true; for (final InputMethodSubtype enabledSubtype : mImm.getEnabledInputMethodSubtypeList( mShortcutInputMethodInfo, allowsImplicitlySelectedSubtypes)) { if (enabledSubtype.equals(mShortcutSubtype)) { return true; } } return false; } 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(Intent intent) { final boolean noConnection = intent.getBooleanExtra( ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); mIsNetworkConnected = !noConnection; KeyboardSwitcher.getInstance().onNetworkStateChanged(); } ////////////////////////////////// // Subtype Switching functions // ////////////////////////////////// public boolean needsToDisplayLanguage(Locale keyboardLocale) { if (keyboardLocale.toString().equals(SubtypeLocale.NO_LANGUAGE)) { return true; } if (!keyboardLocale.equals(getCurrentSubtypeLocale())) { return false; } return mNeedsToDisplayLanguage.getValue(); } public Locale getCurrentSubtypeLocale() { return SubtypeLocale.getSubtypeLocale(mCurrentSubtype); } public void onConfigurationChanged(Configuration conf) { final Locale systemLocale = conf.locale; // If system configuration was changed, update all parameters. if (!systemLocale.equals(mCurrentSystemLocale)) { updateAllParameters(); } } public InputMethodSubtype getCurrentSubtype() { return mCurrentSubtype; } public InputMethodSubtype getNoLanguageSubtype() { return mNoLanguageSubtype; } }