@Override
  public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);

    // The Awesome Bar will receive focus when the Awesome Screen first opens or after the user
    // closes the "Select Input Method" window. If the input method changes to or from Swype,
    // then toggle the URL mode flag. Swype's URL mode disables the automatic word spacing that
    // Swype users expect when entering search queries, but does not add any special VKB keys
    // like ".com" or "/" that would be useful for entering URLs.

    if (!hasFocus) return;

    boolean wasUsingSwype = mIsUsingSwype;
    mIsUsingSwype = sSwypeInputMethods.contains(InputMethods.getCurrentInputMethod(this));

    if (mIsUsingSwype == wasUsingSwype) return;

    int currentInputType = mText.getInputType();
    int newInputType =
        mIsUsingSwype
            ? (currentInputType & ~InputType.TYPE_TEXT_VARIATION_URI) // URL=OFF
            : (currentInputType | InputType.TYPE_TEXT_VARIATION_URI); // URL=ON

    mText.setRawInputType(newInputType);
  }
  private void restartInput() {

    mLastRestartInputTime = SystemClock.uptimeMillis();

    final InputMethodManager imm = getInputMethodManager();
    if (imm == null) {
      return;
    }
    final View v = getView();
    // InputMethodManager has internal logic to detect if we are restarting input
    // in an already focused View, which is the case here because all content text
    // fields are inside one LayerView. When this happens, InputMethodManager will
    // tell the input method to soft reset instead of hard reset. Stock latin IME
    // on Android 4.2+ has a quirk that when it soft resets, it does not clear the
    // composition. The following workaround tricks the IME into clearing the
    // composition when soft resetting.
    if (InputMethods.needsSoftResetWorkaround(mCurrentInputMethod)) {
      // Fake a selection change, because the IME clears the composition when
      // the selection changes, even if soft-resetting. Offsets here must be
      // different from the previous selection offsets, and -1 seems to be a
      // reasonable, deterministic value
      notifySelectionChange(-1, -1);
    }
    imm.restartInput(v);
  }
 private static InputMethodManager getInputMethodManager() {
   View view = getView();
   if (view == null) {
     return null;
   }
   Context context = view.getContext();
   return InputMethods.getInputMethodManager(context);
 }
 @Override
 public boolean commitText(CharSequence text, int newCursorPosition) {
   if (InputMethods.shouldCommitCharAsKey(mCurrentInputMethod)
       && text.length() == 1
       && newCursorPosition > 0) {
     if (DEBUG) {
       Log.d(LOGTAG, "committing \"" + text + "\" as key");
     }
     // mKeyInputConnection is a BaseInputConnection that commits text as keys;
     // but we first need to replace any composing span with a selection,
     // so that the new key events will generate characters to replace
     // text from the old composing span
     return replaceComposingSpanWithSelection()
         && mKeyInputConnection.commitText(text, newCursorPosition);
   }
   return super.commitText(text, newCursorPosition);
 }
  @Override
  public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
    if (mIMEState == IME_STATE_DISABLED) {
      return null;
    }

    outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
    outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
    outAttrs.actionLabel = null;

    if (mIMEState == IME_STATE_PASSWORD || "password".equalsIgnoreCase(mIMETypeHint))
      outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
    else if (mIMEState == IME_STATE_PLUGIN)
      outAttrs.inputType = InputType.TYPE_NULL; // "send key events" mode
    else if (mIMETypeHint.equalsIgnoreCase("url"))
      outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_URI;
    else if (mIMETypeHint.equalsIgnoreCase("email"))
      outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
    else if (mIMETypeHint.equalsIgnoreCase("search"))
      outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
    else if (mIMETypeHint.equalsIgnoreCase("tel")) outAttrs.inputType = InputType.TYPE_CLASS_PHONE;
    else if (mIMETypeHint.equalsIgnoreCase("number") || mIMETypeHint.equalsIgnoreCase("range"))
      outAttrs.inputType =
          InputType.TYPE_CLASS_NUMBER
              | InputType.TYPE_NUMBER_FLAG_SIGNED
              | InputType.TYPE_NUMBER_FLAG_DECIMAL;
    else if (mIMETypeHint.equalsIgnoreCase("week") || mIMETypeHint.equalsIgnoreCase("month"))
      outAttrs.inputType = InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_DATE;
    else if (mIMEModeHint.equalsIgnoreCase("numeric"))
      outAttrs.inputType =
          InputType.TYPE_CLASS_NUMBER
              | InputType.TYPE_NUMBER_FLAG_SIGNED
              | InputType.TYPE_NUMBER_FLAG_DECIMAL;
    else if (mIMEModeHint.equalsIgnoreCase("digit"))
      outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
    else {
      // TYPE_TEXT_FLAG_IME_MULTI_LINE flag makes the fullscreen IME line wrap
      outAttrs.inputType |=
          InputType.TYPE_TEXT_FLAG_AUTO_CORRECT | InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE;
      if (mIMETypeHint.equalsIgnoreCase("textarea") || mIMETypeHint.length() == 0) {
        // empty mIMETypeHint indicates contentEditable/designMode documents
        outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
      }
      if (mIMEModeHint.equalsIgnoreCase("uppercase"))
        outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
      else if (mIMEModeHint.equalsIgnoreCase("titlecase"))
        outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
      else if (mIMETypeHint.equalsIgnoreCase("text")
          && !mIMEModeHint.equalsIgnoreCase("autocapitalized"))
        outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_NORMAL;
      else if (!mIMEModeHint.equalsIgnoreCase("lowercase"))
        outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
      // auto-capitalized mode is the default for types other than text
    }

    if (mIMEActionHint.equalsIgnoreCase("go")) outAttrs.imeOptions = EditorInfo.IME_ACTION_GO;
    else if (mIMEActionHint.equalsIgnoreCase("done"))
      outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
    else if (mIMEActionHint.equalsIgnoreCase("next"))
      outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
    else if (mIMEActionHint.equalsIgnoreCase("search"))
      outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
    else if (mIMEActionHint.equalsIgnoreCase("send"))
      outAttrs.imeOptions = EditorInfo.IME_ACTION_SEND;
    else if (mIMEActionHint.length() > 0) {
      if (DEBUG) Log.w(LOGTAG, "Unexpected mIMEActionHint=\"" + mIMEActionHint + "\"");
      outAttrs.actionLabel = mIMEActionHint;
    }

    Context context = GoannaAppShell.getContext();
    DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    if (Math.min(metrics.widthPixels, metrics.heightPixels) > INLINE_IME_MIN_DISPLAY_SIZE) {
      // prevent showing full-screen keyboard only when the screen is tall enough
      // to show some reasonable amount of the page (see bug 752709)
      outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN;
    }

    if (DEBUG) {
      Log.d(
          LOGTAG,
          "mapped IME states to: inputType = "
              + Integer.toHexString(outAttrs.inputType)
              + ", imeOptions = "
              + Integer.toHexString(outAttrs.imeOptions));
    }

    String prevInputMethod = mCurrentInputMethod;
    mCurrentInputMethod = InputMethods.getCurrentInputMethod(context);
    if (DEBUG) {
      Log.d(LOGTAG, "IME: CurrentInputMethod=" + mCurrentInputMethod);
    }

    // If the user has changed IMEs, then notify input method observers.
    if (!mCurrentInputMethod.equals(prevInputMethod)
        && GoannaAppShell.getGoannaInterface() != null) {
      FormAssistPopup popup = GoannaAppShell.getGoannaInterface().getFormAssistPopup();
      if (popup != null) {
        popup.onInputMethodChanged(mCurrentInputMethod);
      }
    }

    if (mIMEState == IME_STATE_PLUGIN) {
      // Since we are using a temporary string as the editable, the selection is at 0
      outAttrs.initialSelStart = 0;
      outAttrs.initialSelEnd = 0;
      return mKeyInputConnection;
    }
    Editable editable = getEditable();
    outAttrs.initialSelStart = Selection.getSelectionStart(editable);
    outAttrs.initialSelEnd = Selection.getSelectionEnd(editable);
    return this;
  }