RichInputMethodManager.java revision 498dbfbd9dcd9a03b91b6efe4d0e5b3afb1df078
1/* 2 * Copyright (C) 2012 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.common.Constants.Subtype.KEYBOARD_MODE; 20import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY; 21 22import android.content.Context; 23import android.content.SharedPreferences; 24import android.inputmethodservice.InputMethodService; 25import android.os.AsyncTask; 26import android.os.Build; 27import android.os.IBinder; 28import android.preference.PreferenceManager; 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.compat.InputMethodManagerCompatWrapper; 36import com.android.inputmethod.latin.settings.AdditionalFeaturesSettingUtils; 37import com.android.inputmethod.latin.settings.Settings; 38import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; 39import com.android.inputmethod.latin.utils.NetworkConnectivityUtils; 40import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; 41 42import java.util.Collections; 43import java.util.HashMap; 44import java.util.HashSet; 45import java.util.List; 46import java.util.Locale; 47import java.util.Map; 48import java.util.Set; 49 50import javax.annotation.Nonnull; 51 52/** 53 * Enrichment class for InputMethodManager to simplify interaction and add functionality. 54 */ 55// non final for easy mocking. 56public class RichInputMethodManager { 57 private static final String TAG = RichInputMethodManager.class.getSimpleName(); 58 private static final boolean DEBUG = false; 59 60 private RichInputMethodManager() { 61 // This utility class is not publicly instantiable. 62 } 63 64 private static final RichInputMethodManager sInstance = new RichInputMethodManager(); 65 66 private Context mContext; 67 private InputMethodManagerCompatWrapper mImmWrapper; 68 private InputMethodInfoCache mInputMethodInfoCache; 69 private RichInputMethodSubtype mCurrentRichInputMethodSubtype; 70 private InputMethodInfo mShortcutInputMethodInfo; 71 private InputMethodSubtype mShortcutSubtype; 72 final HashMap<InputMethodInfo, List<InputMethodSubtype>> 73 mSubtypeListCacheWithImplicitlySelectedSubtypes = new HashMap<>(); 74 final HashMap<InputMethodInfo, List<InputMethodSubtype>> 75 mSubtypeListCacheWithoutImplicitlySelectedSubtypes = new HashMap<>(); 76 77 private static final int INDEX_NOT_FOUND = -1; 78 79 public static RichInputMethodManager getInstance() { 80 sInstance.checkInitialized(); 81 return sInstance; 82 } 83 84 public static void init(final Context context) { 85 sInstance.initInternal(context); 86 } 87 88 private boolean isInitialized() { 89 return mImmWrapper != null; 90 } 91 92 private void checkInitialized() { 93 if (!isInitialized()) { 94 throw new RuntimeException(TAG + " is used before initialization"); 95 } 96 } 97 98 private void initInternal(final Context context) { 99 if (isInitialized()) { 100 return; 101 } 102 mImmWrapper = new InputMethodManagerCompatWrapper(context); 103 mContext = context; 104 mInputMethodInfoCache = new InputMethodInfoCache( 105 mImmWrapper.mImm, context.getPackageName()); 106 107 // Initialize additional subtypes. 108 SubtypeLocaleUtils.init(context); 109 final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes(); 110 mImmWrapper.mImm.setAdditionalInputMethodSubtypes( 111 getInputMethodIdOfThisIme(), additionalSubtypes); 112 113 // Initialize the current input method subtype and the shortcut IME. 114 refreshSubtypeCaches(); 115 } 116 117 public InputMethodSubtype[] getAdditionalSubtypes() { 118 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); 119 final String prefAdditionalSubtypes = Settings.readPrefAdditionalSubtypes( 120 prefs, mContext.getResources()); 121 return AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefAdditionalSubtypes); 122 } 123 124 public InputMethodManager getInputMethodManager() { 125 checkInitialized(); 126 return mImmWrapper.mImm; 127 } 128 129 public List<InputMethodSubtype> getMyEnabledInputMethodSubtypeList( 130 boolean allowsImplicitlySelectedSubtypes) { 131 return getEnabledInputMethodSubtypeList( 132 getInputMethodInfoOfThisIme(), allowsImplicitlySelectedSubtypes); 133 } 134 135 public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) { 136 if (mImmWrapper.switchToNextInputMethod(token, onlyCurrentIme)) { 137 return true; 138 } 139 // Was not able to call {@link InputMethodManager#switchToNextInputMethodIBinder,boolean)} 140 // because the current device is running ICS or previous and lacks the API. 141 if (switchToNextInputSubtypeInThisIme(token, onlyCurrentIme)) { 142 return true; 143 } 144 return switchToNextInputMethodAndSubtype(token); 145 } 146 147 private boolean switchToNextInputSubtypeInThisIme(final IBinder token, 148 final boolean onlyCurrentIme) { 149 final InputMethodManager imm = mImmWrapper.mImm; 150 final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype(); 151 final List<InputMethodSubtype> enabledSubtypes = getMyEnabledInputMethodSubtypeList( 152 true /* allowsImplicitlySelectedSubtypes */); 153 final int currentIndex = getSubtypeIndexInList(currentSubtype, enabledSubtypes); 154 if (currentIndex == INDEX_NOT_FOUND) { 155 Log.w(TAG, "Can't find current subtype in enabled subtypes: subtype=" 156 + SubtypeLocaleUtils.getSubtypeNameForLogging(currentSubtype)); 157 return false; 158 } 159 final int nextIndex = (currentIndex + 1) % enabledSubtypes.size(); 160 if (nextIndex <= currentIndex && !onlyCurrentIme) { 161 // The current subtype is the last or only enabled one and it needs to switch to 162 // next IME. 163 return false; 164 } 165 final InputMethodSubtype nextSubtype = enabledSubtypes.get(nextIndex); 166 setInputMethodAndSubtype(token, nextSubtype); 167 return true; 168 } 169 170 private boolean switchToNextInputMethodAndSubtype(final IBinder token) { 171 final InputMethodManager imm = mImmWrapper.mImm; 172 final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList(); 173 final int currentIndex = getImiIndexInList(getInputMethodInfoOfThisIme(), enabledImis); 174 if (currentIndex == INDEX_NOT_FOUND) { 175 Log.w(TAG, "Can't find current IME in enabled IMEs: IME package=" 176 + getInputMethodInfoOfThisIme().getPackageName()); 177 return false; 178 } 179 final InputMethodInfo nextImi = getNextNonAuxiliaryIme(currentIndex, enabledImis); 180 final List<InputMethodSubtype> enabledSubtypes = getEnabledInputMethodSubtypeList(nextImi, 181 true /* allowsImplicitlySelectedSubtypes */); 182 if (enabledSubtypes.isEmpty()) { 183 // The next IME has no subtype. 184 imm.setInputMethod(token, nextImi.getId()); 185 return true; 186 } 187 final InputMethodSubtype firstSubtype = enabledSubtypes.get(0); 188 imm.setInputMethodAndSubtype(token, nextImi.getId(), firstSubtype); 189 return true; 190 } 191 192 private static int getImiIndexInList(final InputMethodInfo inputMethodInfo, 193 final List<InputMethodInfo> imiList) { 194 final int count = imiList.size(); 195 for (int index = 0; index < count; index++) { 196 final InputMethodInfo imi = imiList.get(index); 197 if (imi.equals(inputMethodInfo)) { 198 return index; 199 } 200 } 201 return INDEX_NOT_FOUND; 202 } 203 204 // This method mimics {@link InputMethodManager#switchToNextInputMethod(IBinder,boolean)}. 205 private static InputMethodInfo getNextNonAuxiliaryIme(final int currentIndex, 206 final List<InputMethodInfo> imiList) { 207 final int count = imiList.size(); 208 for (int i = 1; i < count; i++) { 209 final int nextIndex = (currentIndex + i) % count; 210 final InputMethodInfo nextImi = imiList.get(nextIndex); 211 if (!isAuxiliaryIme(nextImi)) { 212 return nextImi; 213 } 214 } 215 return imiList.get(currentIndex); 216 } 217 218 // Copied from {@link InputMethodInfo}. See how auxiliary of IME is determined. 219 private static boolean isAuxiliaryIme(final InputMethodInfo imi) { 220 final int count = imi.getSubtypeCount(); 221 if (count == 0) { 222 return false; 223 } 224 for (int index = 0; index < count; index++) { 225 final InputMethodSubtype subtype = imi.getSubtypeAt(index); 226 if (!subtype.isAuxiliary()) { 227 return false; 228 } 229 } 230 return true; 231 } 232 233 private static class InputMethodInfoCache { 234 private final InputMethodManager mImm; 235 private final String mImePackageName; 236 237 private InputMethodInfo mCachedValue; 238 239 public InputMethodInfoCache(final InputMethodManager imm, final String imePackageName) { 240 mImm = imm; 241 mImePackageName = imePackageName; 242 } 243 244 public synchronized InputMethodInfo get() { 245 if (mCachedValue != null) { 246 return mCachedValue; 247 } 248 for (final InputMethodInfo imi : mImm.getInputMethodList()) { 249 if (imi.getPackageName().equals(mImePackageName)) { 250 mCachedValue = imi; 251 return imi; 252 } 253 } 254 throw new RuntimeException("Input method id for " + mImePackageName + " not found."); 255 } 256 257 public synchronized void clear() { 258 mCachedValue = null; 259 } 260 } 261 262 public InputMethodInfo getInputMethodInfoOfThisIme() { 263 return mInputMethodInfoCache.get(); 264 } 265 266 public String getInputMethodIdOfThisIme() { 267 return getInputMethodInfoOfThisIme().getId(); 268 } 269 270 public boolean checkIfSubtypeBelongsToThisImeAndEnabled(final InputMethodSubtype subtype) { 271 return checkIfSubtypeBelongsToImeAndEnabled(getInputMethodInfoOfThisIme(), subtype); 272 } 273 274 public boolean checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled( 275 final InputMethodSubtype subtype) { 276 final boolean subtypeEnabled = checkIfSubtypeBelongsToThisImeAndEnabled(subtype); 277 final boolean subtypeExplicitlyEnabled = checkIfSubtypeBelongsToList( 278 subtype, getMyEnabledInputMethodSubtypeList( 279 false /* allowsImplicitlySelectedSubtypes */)); 280 return subtypeEnabled && !subtypeExplicitlyEnabled; 281 } 282 283 public boolean checkIfSubtypeBelongsToImeAndEnabled(final InputMethodInfo imi, 284 final InputMethodSubtype subtype) { 285 return checkIfSubtypeBelongsToList(subtype, getEnabledInputMethodSubtypeList(imi, 286 true /* allowsImplicitlySelectedSubtypes */)); 287 } 288 289 private static boolean checkIfSubtypeBelongsToList(final InputMethodSubtype subtype, 290 final List<InputMethodSubtype> subtypes) { 291 return getSubtypeIndexInList(subtype, subtypes) != INDEX_NOT_FOUND; 292 } 293 294 private static int getSubtypeIndexInList(final InputMethodSubtype subtype, 295 final List<InputMethodSubtype> subtypes) { 296 final int count = subtypes.size(); 297 for (int index = 0; index < count; index++) { 298 final InputMethodSubtype ims = subtypes.get(index); 299 if (ims.equals(subtype)) { 300 return index; 301 } 302 } 303 return INDEX_NOT_FOUND; 304 } 305 306 public boolean checkIfSubtypeBelongsToThisIme(final InputMethodSubtype subtype) { 307 return getSubtypeIndexInIme(subtype, getInputMethodInfoOfThisIme()) != INDEX_NOT_FOUND; 308 } 309 310 private static int getSubtypeIndexInIme(final InputMethodSubtype subtype, 311 final InputMethodInfo imi) { 312 final int count = imi.getSubtypeCount(); 313 for (int index = 0; index < count; index++) { 314 final InputMethodSubtype ims = imi.getSubtypeAt(index); 315 if (ims.equals(subtype)) { 316 return index; 317 } 318 } 319 return INDEX_NOT_FOUND; 320 } 321 322 public void onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) { 323 updateCurrentSubtype(newSubtype); 324 updateShortcutIme(); 325 if (DEBUG) { 326 Log.w(TAG, "onSubtypeChanged: " + mCurrentRichInputMethodSubtype.getNameForLogging()); 327 } 328 } 329 330 private static RichInputMethodSubtype sForcedSubtypeForTesting = null; 331 332 @UsedForTesting 333 static void forceSubtype(@Nonnull final InputMethodSubtype subtype) { 334 sForcedSubtypeForTesting = new RichInputMethodSubtype(subtype); 335 } 336 337 @Nonnull 338 public Locale[] getCurrentSubtypeLocales() { 339 if (null != sForcedSubtypeForTesting) { 340 return sForcedSubtypeForTesting.getLocales(); 341 } 342 return getCurrentSubtype().getLocales(); 343 } 344 345 @Nonnull 346 public RichInputMethodSubtype getCurrentSubtype() { 347 if (null != sForcedSubtypeForTesting) { 348 return sForcedSubtypeForTesting; 349 } 350 return mCurrentRichInputMethodSubtype; 351 } 352 353 354 public String getCombiningRulesExtraValueOfCurrentSubtype() { 355 return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype().getRawSubtype()); 356 } 357 358 public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) { 359 final List<InputMethodInfo> enabledImis = mImmWrapper.mImm.getEnabledInputMethodList(); 360 return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, enabledImis); 361 } 362 363 public boolean hasMultipleEnabledSubtypesInThisIme( 364 final boolean shouldIncludeAuxiliarySubtypes) { 365 final List<InputMethodInfo> imiList = Collections.singletonList( 366 getInputMethodInfoOfThisIme()); 367 return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, imiList); 368 } 369 370 private boolean hasMultipleEnabledSubtypes(final boolean shouldIncludeAuxiliarySubtypes, 371 final List<InputMethodInfo> imiList) { 372 // Number of the filtered IMEs 373 int filteredImisCount = 0; 374 375 for (InputMethodInfo imi : imiList) { 376 // We can return true immediately after we find two or more filtered IMEs. 377 if (filteredImisCount > 1) return true; 378 final List<InputMethodSubtype> subtypes = getEnabledInputMethodSubtypeList(imi, true); 379 // IMEs that have no subtypes should be counted. 380 if (subtypes.isEmpty()) { 381 ++filteredImisCount; 382 continue; 383 } 384 385 int auxCount = 0; 386 for (InputMethodSubtype subtype : subtypes) { 387 if (subtype.isAuxiliary()) { 388 ++auxCount; 389 } 390 } 391 final int nonAuxCount = subtypes.size() - auxCount; 392 393 // IMEs that have one or more non-auxiliary subtypes should be counted. 394 // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary 395 // subtypes should be counted as well. 396 if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) { 397 ++filteredImisCount; 398 continue; 399 } 400 } 401 402 if (filteredImisCount > 1) { 403 return true; 404 } 405 final List<InputMethodSubtype> subtypes = getMyEnabledInputMethodSubtypeList(true); 406 int keyboardCount = 0; 407 // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's 408 // both explicitly and implicitly enabled input method subtype. 409 // (The current IME should be LatinIME.) 410 for (InputMethodSubtype subtype : subtypes) { 411 if (KEYBOARD_MODE.equals(subtype.getMode())) { 412 ++keyboardCount; 413 } 414 } 415 return keyboardCount > 1; 416 } 417 418 public InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(final String localeString, 419 final String keyboardLayoutSetName) { 420 final InputMethodInfo myImi = getInputMethodInfoOfThisIme(); 421 final int count = myImi.getSubtypeCount(); 422 for (int i = 0; i < count; i++) { 423 final InputMethodSubtype subtype = myImi.getSubtypeAt(i); 424 final String layoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype); 425 if (localeString.equals(subtype.getLocale()) 426 && keyboardLayoutSetName.equals(layoutName)) { 427 return subtype; 428 } 429 } 430 return null; 431 } 432 433 public void setInputMethodAndSubtype(final IBinder token, final InputMethodSubtype subtype) { 434 mImmWrapper.mImm.setInputMethodAndSubtype( 435 token, getInputMethodIdOfThisIme(), subtype); 436 } 437 438 public void setAdditionalInputMethodSubtypes(final InputMethodSubtype[] subtypes) { 439 mImmWrapper.mImm.setAdditionalInputMethodSubtypes( 440 getInputMethodIdOfThisIme(), subtypes); 441 // Clear the cache so that we go read the {@link InputMethodInfo} of this IME and list of 442 // subtypes again next time. 443 refreshSubtypeCaches(); 444 } 445 446 private List<InputMethodSubtype> getEnabledInputMethodSubtypeList(final InputMethodInfo imi, 447 final boolean allowsImplicitlySelectedSubtypes) { 448 final HashMap<InputMethodInfo, List<InputMethodSubtype>> cache = 449 allowsImplicitlySelectedSubtypes 450 ? mSubtypeListCacheWithImplicitlySelectedSubtypes 451 : mSubtypeListCacheWithoutImplicitlySelectedSubtypes; 452 final List<InputMethodSubtype> cachedList = cache.get(imi); 453 if (null != cachedList) return cachedList; 454 final List<InputMethodSubtype> result = mImmWrapper.mImm.getEnabledInputMethodSubtypeList( 455 imi, allowsImplicitlySelectedSubtypes); 456 cache.put(imi, result); 457 return result; 458 } 459 460 public void refreshSubtypeCaches() { 461 mSubtypeListCacheWithImplicitlySelectedSubtypes.clear(); 462 mSubtypeListCacheWithoutImplicitlySelectedSubtypes.clear(); 463 mInputMethodInfoCache.clear(); 464 updateCurrentSubtype(mImmWrapper.mImm.getCurrentInputMethodSubtype()); 465 updateShortcutIme(); 466 } 467 468 public boolean shouldOfferSwitchingToNextInputMethod(final IBinder binder, 469 boolean defaultValue) { 470 // Use the default value instead on Jelly Bean MR2 and previous where 471 // {@link InputMethodManager#shouldOfferSwitchingToNextInputMethod} isn't yet available 472 // and on KitKat where the API is still just a stub to return true always. 473 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { 474 return defaultValue; 475 } 476 return mImmWrapper.shouldOfferSwitchingToNextInputMethod(binder); 477 } 478 479 public boolean isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes() { 480 final Locale systemLocale = mContext.getResources().getConfiguration().locale; 481 final Set<InputMethodSubtype> enabledSubtypesOfEnabledImes = new HashSet<>(); 482 final InputMethodManager inputMethodManager = getInputMethodManager(); 483 final List<InputMethodInfo> enabledInputMethodInfoList = 484 inputMethodManager.getEnabledInputMethodList(); 485 for (final InputMethodInfo info : enabledInputMethodInfoList) { 486 final List<InputMethodSubtype> enabledSubtypes = 487 inputMethodManager.getEnabledInputMethodSubtypeList( 488 info, true /* allowsImplicitlySelectedSubtypes */); 489 if (enabledSubtypes.isEmpty()) { 490 // An IME with no subtypes is found. 491 return false; 492 } 493 enabledSubtypesOfEnabledImes.addAll(enabledSubtypes); 494 } 495 for (final InputMethodSubtype subtype : enabledSubtypesOfEnabledImes) { 496 if (!subtype.isAuxiliary() && !subtype.getLocale().isEmpty() 497 && !systemLocale.equals(SubtypeLocaleUtils.getSubtypeLocale(subtype))) { 498 return false; 499 } 500 } 501 return true; 502 } 503 504 private void updateCurrentSubtype(@Nonnull final InputMethodSubtype subtype) { 505 mCurrentRichInputMethodSubtype = new RichInputMethodSubtype(subtype); 506 } 507 508 private void updateShortcutIme() { 509 if (DEBUG) { 510 Log.d(TAG, "Update shortcut IME from : " 511 + (mShortcutInputMethodInfo == null 512 ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " 513 + (mShortcutSubtype == null ? "<null>" : ( 514 mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); 515 } 516 // TODO: Update an icon for shortcut IME 517 final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts = 518 getInputMethodManager().getShortcutInputMethodsAndSubtypes(); 519 mShortcutInputMethodInfo = null; 520 mShortcutSubtype = null; 521 for (final InputMethodInfo imi : shortcuts.keySet()) { 522 final List<InputMethodSubtype> subtypes = shortcuts.get(imi); 523 // TODO: Returns the first found IMI for now. Should handle all shortcuts as 524 // appropriate. 525 mShortcutInputMethodInfo = imi; 526 // TODO: Pick up the first found subtype for now. Should handle all subtypes 527 // as appropriate. 528 mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null; 529 break; 530 } 531 if (DEBUG) { 532 Log.d(TAG, "Update shortcut IME to : " 533 + (mShortcutInputMethodInfo == null 534 ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " 535 + (mShortcutSubtype == null ? "<null>" : ( 536 mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); 537 } 538 } 539 540 public void switchToShortcutIme(final InputMethodService context) { 541 if (mShortcutInputMethodInfo == null) { 542 return; 543 } 544 545 final String imiId = mShortcutInputMethodInfo.getId(); 546 switchToTargetIME(imiId, mShortcutSubtype, context); 547 } 548 549 private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype, 550 final InputMethodService context) { 551 final IBinder token = context.getWindow().getWindow().getAttributes().token; 552 if (token == null) { 553 return; 554 } 555 final InputMethodManager imm = getInputMethodManager(); 556 new AsyncTask<Void, Void, Void>() { 557 @Override 558 protected Void doInBackground(Void... params) { 559 imm.setInputMethodAndSubtype(token, imiId, subtype); 560 return null; 561 } 562 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 563 } 564 565 public boolean isShortcutImeEnabled() { 566 if (mShortcutInputMethodInfo == null) { 567 return false; 568 } 569 if (mShortcutSubtype == null) { 570 return true; 571 } 572 return checkIfSubtypeBelongsToImeAndEnabled(mShortcutInputMethodInfo, mShortcutSubtype); 573 } 574 575 public boolean isShortcutImeReady() { 576 if (mShortcutInputMethodInfo == null) { 577 return false; 578 } 579 if (mShortcutSubtype == null) { 580 return true; 581 } 582 if (mShortcutSubtype.containsExtraValueKey(REQ_NETWORK_CONNECTIVITY)) { 583 return NetworkConnectivityUtils.isNetworkConnected(); 584 } 585 return true; 586 } 587} 588