// Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
  public void updateSubtype(InputMethodSubtypeCompatWrapper newSubtype) {
    final String newLocale;
    final String newMode;
    final String oldMode = getCurrentSubtypeMode();
    if (newSubtype == null) {
      // Normally, newSubtype shouldn't be null. But just in case newSubtype was null,
      // fallback to the default locale.
      Log.w(TAG, "Couldn't get the current subtype.");
      newLocale = "en_US";
      newMode = KEYBOARD_MODE;
    } else {
      newLocale = getSubtypeLocale(newSubtype);
      newMode = newSubtype.getMode();
    }
    if (DBG) {
      Log.w(
          TAG,
          "Update subtype to:"
              + newLocale
              + ","
              + newMode
              + ", from: "
              + mInputLocaleStr
              + ", "
              + oldMode);
    }
    boolean languageChanged = false;
    if (!newLocale.equals(mInputLocaleStr)) {
      if (mInputLocaleStr != null) {
        languageChanged = true;
      }
      updateInputLocale(newLocale);
    }
    boolean modeChanged = false;
    if (!newMode.equals(oldMode)) {
      if (oldMode != null) {
        modeChanged = true;
      }
    }
    mCurrentSubtype = newSubtype;

    // If the old mode is voice input, we need to reset or cancel its status.
    // We cancel its status when we change mode, while we reset otherwise.
    if (isKeyboardMode()) {
      if (modeChanged) {
        if (VOICE_MODE.equals(oldMode) && mVoiceInputWrapper != null) {
          mVoiceInputWrapper.cancel();
        }
      }
      if (modeChanged || languageChanged) {
        updateShortcutIME();
        mService.onRefreshKeyboard();
      }
    } else if (isVoiceMode() && mVoiceInputWrapper != null) {
      if (VOICE_MODE.equals(oldMode)) {
        mVoiceInputWrapper.reset();
      }
      // If needsToShowWarningDialog is true, voice input need to show warning before
      // show recognition view.
      if (languageChanged || modeChanged || VoiceProxy.getInstance().needsToShowWarningDialog()) {
        triggerVoiceIME();
      }
    } else {
      if (VOICE_MODE.equals(oldMode) && mVoiceInputWrapper != null) {
        // We need to reset the voice input to release the resources and to reset its status
        // as it is not the current input mode.
        mVoiceInputWrapper.reset();
      }
      final String packageName = mService.getPackageName();
      int version = -1;
      try {
        version = mService.getPackageManager().getPackageInfo(packageName, 0).versionCode;
      } catch (NameNotFoundException e) {
      }
      Log.w(
          TAG,
          "Unknown subtype mode: "
              + newMode
              + ","
              + version
              + ", "
              + packageName
              + ", "
              + mVoiceInputWrapper
              + ". IME is already changed to other IME.");
      if (newSubtype != null) {
        Log.w(TAG, "Subtype mode:" + newSubtype.getMode());
        Log.w(TAG, "Subtype locale:" + newSubtype.getLocale());
        Log.w(TAG, "Subtype extra value:" + newSubtype.getExtraValue());
        Log.w(TAG, "Subtype is auxiliary:" + newSubtype.isAuxiliary());
      }
    }
  }
 private static String getSubtypeLocale(InputMethodSubtypeCompatWrapper subtype) {
   final String keyboardLocale =
       subtype.getExtraValueOf(LatinIME.SUBTYPE_EXTRA_VALUE_KEYBOARD_LOCALE);
   return keyboardLocale != null ? keyboardLocale : subtype.getLocale();
 }