SubtypeSwitcher.java revision a410cb48eab0cd75aa27e20f60e47a29a59fb9ff
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.inputmethod.latin; 18 19import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY; 20 21import android.content.Context; 22import android.content.Intent; 23import android.content.res.Resources; 24import android.inputmethodservice.InputMethodService; 25import android.net.ConnectivityManager; 26import android.net.NetworkInfo; 27import android.os.AsyncTask; 28import android.os.IBinder; 29import android.util.Log; 30import android.view.inputmethod.InputMethodInfo; 31import android.view.inputmethod.InputMethodManager; 32import android.view.inputmethod.InputMethodSubtype; 33 34import com.android.inputmethod.annotations.UsedForTesting; 35import com.android.inputmethod.keyboard.KeyboardSwitcher; 36import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; 37 38import java.util.List; 39import java.util.Locale; 40import java.util.Map; 41 42public final class SubtypeSwitcher { 43 private static boolean DBG = LatinImeLogger.sDBG; 44 private static final String TAG = SubtypeSwitcher.class.getSimpleName(); 45 46 private static final SubtypeSwitcher sInstance = new SubtypeSwitcher(); 47 48 private /* final */ RichInputMethodManager mRichImm; 49 private /* final */ Resources mResources; 50 private /* final */ ConnectivityManager mConnectivityManager; 51 52 private final NeedsToDisplayLanguage mNeedsToDisplayLanguage = new NeedsToDisplayLanguage(); 53 private InputMethodInfo mShortcutInputMethodInfo; 54 private InputMethodSubtype mShortcutSubtype; 55 private InputMethodSubtype mNoLanguageSubtype; 56 private boolean mIsNetworkConnected; 57 58 // Dummy no language QWERTY subtype. See {@link R.xml.method}. 59 private static final InputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE = new InputMethodSubtype( 60 R.string.subtype_no_language_qwerty, R.drawable.ic_subtype_keyboard, "zz", "keyboard", 61 "KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable", 62 false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */); 63 64 static final class NeedsToDisplayLanguage { 65 private int mEnabledSubtypeCount; 66 private boolean mIsSystemLanguageSameAsInputLanguage; 67 68 public boolean getValue() { 69 return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage; 70 } 71 72 public void updateEnabledSubtypeCount(final int count) { 73 mEnabledSubtypeCount = count; 74 } 75 76 public void updateIsSystemLanguageSameAsInputLanguage(final boolean isSame) { 77 mIsSystemLanguageSameAsInputLanguage = isSame; 78 } 79 } 80 81 public static SubtypeSwitcher getInstance() { 82 return sInstance; 83 } 84 85 public static void init(final Context context) { 86 SubtypeLocaleUtils.init(context); 87 RichInputMethodManager.init(context); 88 sInstance.initialize(context); 89 } 90 91 private SubtypeSwitcher() { 92 // Intentional empty constructor for singleton. 93 } 94 95 private void initialize(final Context context) { 96 if (mResources != null) { 97 return; 98 } 99 mResources = context.getResources(); 100 mRichImm = RichInputMethodManager.getInstance(); 101 mConnectivityManager = (ConnectivityManager) context.getSystemService( 102 Context.CONNECTIVITY_SERVICE); 103 104 final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); 105 mIsNetworkConnected = (info != null && info.isConnected()); 106 107 onSubtypeChanged(getCurrentSubtype()); 108 updateParametersOnStartInputView(); 109 } 110 111 /** 112 * Update parameters which are changed outside LatinIME. This parameters affect UI so that they 113 * should be updated every time onStartInputView is called. 114 */ 115 public void updateParametersOnStartInputView() { 116 final List<InputMethodSubtype> enabledSubtypesOfThisIme = 117 mRichImm.getMyEnabledInputMethodSubtypeList(true); 118 mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size()); 119 updateShortcutIME(); 120 } 121 122 private void updateShortcutIME() { 123 if (DBG) { 124 Log.d(TAG, "Update shortcut IME from : " 125 + (mShortcutInputMethodInfo == null 126 ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " 127 + (mShortcutSubtype == null ? "<null>" : ( 128 mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); 129 } 130 // TODO: Update an icon for shortcut IME 131 final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts = 132 mRichImm.getInputMethodManager().getShortcutInputMethodsAndSubtypes(); 133 mShortcutInputMethodInfo = null; 134 mShortcutSubtype = null; 135 for (final InputMethodInfo imi : shortcuts.keySet()) { 136 final List<InputMethodSubtype> subtypes = shortcuts.get(imi); 137 // TODO: Returns the first found IMI for now. Should handle all shortcuts as 138 // appropriate. 139 mShortcutInputMethodInfo = imi; 140 // TODO: Pick up the first found subtype for now. Should handle all subtypes 141 // as appropriate. 142 mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null; 143 break; 144 } 145 if (DBG) { 146 Log.d(TAG, "Update shortcut IME to : " 147 + (mShortcutInputMethodInfo == null 148 ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " 149 + (mShortcutSubtype == null ? "<null>" : ( 150 mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); 151 } 152 } 153 154 // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function. 155 public void onSubtypeChanged(final InputMethodSubtype newSubtype) { 156 if (DBG) { 157 Log.w(TAG, "onSubtypeChanged: " 158 + SubtypeLocaleUtils.getSubtypeNameForLogging(newSubtype)); 159 } 160 161 final Locale newLocale = SubtypeLocaleUtils.getSubtypeLocale(newSubtype); 162 final Locale systemLocale = mResources.getConfiguration().locale; 163 final boolean sameLocale = systemLocale.equals(newLocale); 164 final boolean sameLanguage = systemLocale.getLanguage().equals(newLocale.getLanguage()); 165 final boolean implicitlyEnabled = 166 mRichImm.checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype); 167 mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage( 168 sameLocale || (sameLanguage && implicitlyEnabled)); 169 170 updateShortcutIME(); 171 } 172 173 //////////////////////////// 174 // Shortcut IME functions // 175 //////////////////////////// 176 177 public void switchToShortcutIME(final InputMethodService context) { 178 if (mShortcutInputMethodInfo == null) { 179 return; 180 } 181 182 final String imiId = mShortcutInputMethodInfo.getId(); 183 switchToTargetIME(imiId, mShortcutSubtype, context); 184 } 185 186 private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype, 187 final InputMethodService context) { 188 final IBinder token = context.getWindow().getWindow().getAttributes().token; 189 if (token == null) { 190 return; 191 } 192 final InputMethodManager imm = mRichImm.getInputMethodManager(); 193 new AsyncTask<Void, Void, Void>() { 194 @Override 195 protected Void doInBackground(Void... params) { 196 imm.setInputMethodAndSubtype(token, imiId, subtype); 197 return null; 198 } 199 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 200 } 201 202 public boolean isShortcutImeEnabled() { 203 if (mShortcutInputMethodInfo == null) { 204 return false; 205 } 206 if (mShortcutSubtype == null) { 207 return true; 208 } 209 return mRichImm.checkIfSubtypeBelongsToImeAndEnabled( 210 mShortcutInputMethodInfo, mShortcutSubtype); 211 } 212 213 public boolean isShortcutImeReady() { 214 if (mShortcutInputMethodInfo == null) 215 return false; 216 if (mShortcutSubtype == null) 217 return true; 218 if (mShortcutSubtype.containsExtraValueKey(REQ_NETWORK_CONNECTIVITY)) { 219 return mIsNetworkConnected; 220 } 221 return true; 222 } 223 224 public void onNetworkStateChanged(final Intent intent) { 225 final boolean noConnection = intent.getBooleanExtra( 226 ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); 227 mIsNetworkConnected = !noConnection; 228 229 KeyboardSwitcher.getInstance().onNetworkStateChanged(); 230 } 231 232 ////////////////////////////////// 233 // Subtype Switching functions // 234 ////////////////////////////////// 235 236 public boolean needsToDisplayLanguage(final Locale keyboardLocale) { 237 if (keyboardLocale.toString().equals(SubtypeLocaleUtils.NO_LANGUAGE)) { 238 return true; 239 } 240 if (!keyboardLocale.equals(getCurrentSubtypeLocale())) { 241 return false; 242 } 243 return mNeedsToDisplayLanguage.getValue(); 244 } 245 246 private static Locale sForcedLocaleForTesting = null; 247 @UsedForTesting 248 void forceLocale(final Locale locale) { 249 sForcedLocaleForTesting = locale; 250 } 251 252 public Locale getCurrentSubtypeLocale() { 253 if (null != sForcedLocaleForTesting) return sForcedLocaleForTesting; 254 return SubtypeLocaleUtils.getSubtypeLocale(getCurrentSubtype()); 255 } 256 257 public InputMethodSubtype getCurrentSubtype() { 258 return mRichImm.getCurrentInputMethodSubtype(getNoLanguageSubtype()); 259 } 260 261 public InputMethodSubtype getNoLanguageSubtype() { 262 if (mNoLanguageSubtype == null) { 263 mNoLanguageSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( 264 SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.QWERTY); 265 } 266 if (mNoLanguageSubtype != null) { 267 return mNoLanguageSubtype; 268 } 269 Log.w(TAG, "Can't find no lanugage with QWERTY subtype"); 270 Log.w(TAG, "No input method subtype found; return dummy subtype: " 271 + DUMMY_NO_LANGUAGE_SUBTYPE); 272 return DUMMY_NO_LANGUAGE_SUBTYPE; 273 } 274} 275