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 17 package com.android.inputmethod.latin; 18 19 import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY; 20 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.res.Configuration; 24 import android.content.res.Resources; 25 import android.net.ConnectivityManager; 26 import android.net.NetworkInfo; 27 import android.os.AsyncTask; 28 import android.os.IBinder; 29 import android.util.Log; 30 import android.view.inputmethod.InputMethodInfo; 31 import android.view.inputmethod.InputMethodManager; 32 import android.view.inputmethod.InputMethodSubtype; 33 34 import com.android.inputmethod.keyboard.KeyboardSwitcher; 35 36 import java.util.List; 37 import java.util.Locale; 38 import java.util.Map; 39 40 public class SubtypeSwitcher { 41 private static boolean DBG = LatinImeLogger.sDBG; 42 private static final String TAG = SubtypeSwitcher.class.getSimpleName(); 43 44 private static final SubtypeSwitcher sInstance = new SubtypeSwitcher(); 45 private /* final */ LatinIME mService; 46 private /* final */ InputMethodManager mImm; 47 private /* final */ Resources mResources; 48 private /* final */ ConnectivityManager mConnectivityManager; 49 50 /*-----------------------------------------------------------*/ 51 // Variants which should be changed only by reload functions. 52 private NeedsToDisplayLanguage mNeedsToDisplayLanguage = new NeedsToDisplayLanguage(); 53 private InputMethodInfo mShortcutInputMethodInfo; 54 private InputMethodSubtype mShortcutSubtype; 55 private InputMethodSubtype mNoLanguageSubtype; 56 // Note: This variable is always non-null after {@link #initialize(LatinIME)}. 57 private InputMethodSubtype mCurrentSubtype; 58 private Locale mCurrentSystemLocale; 59 /*-----------------------------------------------------------*/ 60 61 private boolean mIsNetworkConnected; 62 63 static class NeedsToDisplayLanguage { 64 private int mEnabledSubtypeCount; 65 private boolean mIsSystemLanguageSameAsInputLanguage; 66 67 public boolean getValue() { 68 return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage; 69 } 70 71 public void updateEnabledSubtypeCount(int count) { 72 mEnabledSubtypeCount = count; 73 } 74 75 public void updateIsSystemLanguageSameAsInputLanguage(boolean isSame) { 76 mIsSystemLanguageSameAsInputLanguage = isSame; 77 } 78 } 79 80 public static SubtypeSwitcher getInstance() { 81 return sInstance; 82 } 83 84 public static void init(LatinIME service) { 85 SubtypeLocale.init(service); 86 sInstance.initialize(service); 87 sInstance.updateAllParameters(); 88 } 89 90 private SubtypeSwitcher() { 91 // Intentional empty constructor for singleton. 92 } 93 94 private void initialize(LatinIME service) { 95 mService = service; 96 mResources = service.getResources(); 97 mImm = ImfUtils.getInputMethodManager(service); 98 mConnectivityManager = (ConnectivityManager) service.getSystemService( 99 Context.CONNECTIVITY_SERVICE); 100 mCurrentSystemLocale = mResources.getConfiguration().locale; 101 mCurrentSubtype = mImm.getCurrentInputMethodSubtype(); 102 mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( 103 service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY); 104 if (mNoLanguageSubtype == null) { 105 throw new RuntimeException("Can't find no lanugage with QWERTY subtype"); 106 } 107 108 final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); 109 mIsNetworkConnected = (info != null && info.isConnected()); 110 } 111 112 // Update all parameters stored in SubtypeSwitcher. 113 // Only configuration changed event is allowed to call this because this is heavy. 114 private void updateAllParameters() { 115 mCurrentSystemLocale = mResources.getConfiguration().locale; 116 updateSubtype(mImm.getCurrentInputMethodSubtype()); 117 updateParametersOnStartInputView(); 118 } 119 120 // Update parameters which are changed outside LatinIME. This parameters affect UI so they 121 // should be updated every time onStartInputview. 122 public void updateParametersOnStartInputView() { 123 updateEnabledSubtypes(); 124 updateShortcutIME(); 125 } 126 127 // Reload enabledSubtypes from the framework. 128 private void updateEnabledSubtypes() { 129 final InputMethodSubtype currentSubtype = mCurrentSubtype; 130 boolean foundCurrentSubtypeBecameDisabled = true; 131 final List<InputMethodSubtype> enabledSubtypesOfThisIme = 132 mImm.getEnabledInputMethodSubtypeList(null, true); 133 for (InputMethodSubtype ims : enabledSubtypesOfThisIme) { 134 if (ims.equals(currentSubtype)) { 135 foundCurrentSubtypeBecameDisabled = false; 136 } 137 } 138 mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size()); 139 if (foundCurrentSubtypeBecameDisabled) { 140 if (DBG) { 141 Log.w(TAG, "Last subtype: " 142 + currentSubtype.getLocale() + "/" + currentSubtype.getExtraValue()); 143 Log.w(TAG, "Last subtype was disabled. Update to the current one."); 144 } 145 updateSubtype(mImm.getCurrentInputMethodSubtype()); 146 } 147 } 148 149 private void updateShortcutIME() { 150 if (DBG) { 151 Log.d(TAG, "Update shortcut IME from : " 152 + (mShortcutInputMethodInfo == null 153 ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " 154 + (mShortcutSubtype == null ? "<null>" : ( 155 mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); 156 } 157 // TODO: Update an icon for shortcut IME 158 final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts = 159 mImm.getShortcutInputMethodsAndSubtypes(); 160 mShortcutInputMethodInfo = null; 161 mShortcutSubtype = null; 162 for (InputMethodInfo imi : shortcuts.keySet()) { 163 List<InputMethodSubtype> subtypes = shortcuts.get(imi); 164 // TODO: Returns the first found IMI for now. Should handle all shortcuts as 165 // appropriate. 166 mShortcutInputMethodInfo = imi; 167 // TODO: Pick up the first found subtype for now. Should handle all subtypes 168 // as appropriate. 169 mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null; 170 break; 171 } 172 if (DBG) { 173 Log.d(TAG, "Update shortcut IME to : " 174 + (mShortcutInputMethodInfo == null 175 ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " 176 + (mShortcutSubtype == null ? "<null>" : ( 177 mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); 178 } 179 } 180 181 // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function. 182 public void updateSubtype(InputMethodSubtype newSubtype) { 183 if (DBG) { 184 Log.w(TAG, "onCurrentInputMethodSubtypeChanged: to: " 185 + newSubtype.getLocale() + "/" + newSubtype.getExtraValue() + ", from: " 186 + mCurrentSubtype.getLocale() + "/" + mCurrentSubtype.getExtraValue()); 187 } 188 189 final Locale newLocale = SubtypeLocale.getSubtypeLocale(newSubtype); 190 mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage( 191 mCurrentSystemLocale.equals(newLocale)); 192 193 if (newSubtype.equals(mCurrentSubtype)) return; 194 195 mCurrentSubtype = newSubtype; 196 updateShortcutIME(); 197 mService.onRefreshKeyboard(); 198 } 199 200 //////////////////////////// 201 // Shortcut IME functions // 202 //////////////////////////// 203 204 public void switchToShortcutIME() { 205 if (mShortcutInputMethodInfo == null) { 206 return; 207 } 208 209 final String imiId = mShortcutInputMethodInfo.getId(); 210 switchToTargetIME(imiId, mShortcutSubtype); 211 } 212 213 private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype) { 214 final IBinder token = mService.getWindow().getWindow().getAttributes().token; 215 if (token == null) { 216 return; 217 } 218 final InputMethodManager imm = mImm; 219 new AsyncTask<Void, Void, Void>() { 220 @Override 221 protected Void doInBackground(Void... params) { 222 imm.setInputMethodAndSubtype(token, imiId, subtype); 223 return null; 224 } 225 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 226 } 227 228 public boolean isShortcutImeEnabled() { 229 if (mShortcutInputMethodInfo == null) { 230 return false; 231 } 232 if (mShortcutSubtype == null) { 233 return true; 234 } 235 final boolean allowsImplicitlySelectedSubtypes = true; 236 for (final InputMethodSubtype enabledSubtype : mImm.getEnabledInputMethodSubtypeList( 237 mShortcutInputMethodInfo, allowsImplicitlySelectedSubtypes)) { 238 if (enabledSubtype.equals(mShortcutSubtype)) { 239 return true; 240 } 241 } 242 return false; 243 } 244 245 public boolean isShortcutImeReady() { 246 if (mShortcutInputMethodInfo == null) 247 return false; 248 if (mShortcutSubtype == null) 249 return true; 250 if (mShortcutSubtype.containsExtraValueKey(REQ_NETWORK_CONNECTIVITY)) { 251 return mIsNetworkConnected; 252 } 253 return true; 254 } 255 256 public void onNetworkStateChanged(Intent intent) { 257 final boolean noConnection = intent.getBooleanExtra( 258 ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); 259 mIsNetworkConnected = !noConnection; 260 261 KeyboardSwitcher.getInstance().onNetworkStateChanged(); 262 } 263 264 ////////////////////////////////// 265 // Subtype Switching functions // 266 ////////////////////////////////// 267 268 public boolean needsToDisplayLanguage(Locale keyboardLocale) { 269 if (keyboardLocale.toString().equals(SubtypeLocale.NO_LANGUAGE)) { 270 return true; 271 } 272 if (!keyboardLocale.equals(getCurrentSubtypeLocale())) { 273 return false; 274 } 275 return mNeedsToDisplayLanguage.getValue(); 276 } 277 278 public Locale getCurrentSubtypeLocale() { 279 return SubtypeLocale.getSubtypeLocale(mCurrentSubtype); 280 } 281 282 public void onConfigurationChanged(Configuration conf) { 283 final Locale systemLocale = conf.locale; 284 // If system configuration was changed, update all parameters. 285 if (!systemLocale.equals(mCurrentSystemLocale)) { 286 updateAllParameters(); 287 } 288 } 289 290 public InputMethodSubtype getCurrentSubtype() { 291 return mCurrentSubtype; 292 } 293 294 public InputMethodSubtype getNoLanguageSubtype() { 295 return mNoLanguageSubtype; 296 } 297 } 298