blob: bef8a3cf1ab8a63ac406d97c493cf570d9d2a6ec [file] [log] [blame]
/*
* 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.getInputMethodManager().getEnabledInputMethodSubtypeList(null, 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;
}
}