/**
  * This method works around the issue crbug.com/373934 where Blink does not cancel the composition
  * when we send a commit with the empty text.
  *
  * <p>TODO(aurimas) Remove this once crbug.com/373934 is fixed.
  *
  * @param text Text that software keyboard requested to commit.
  * @return Whether the workaround was performed.
  */
 private boolean maybePerformEmptyCompositionWorkaround(CharSequence text) {
   int selectionStart = Selection.getSelectionStart(mEditable);
   int selectionEnd = Selection.getSelectionEnd(mEditable);
   int compositionStart = getComposingSpanStart(mEditable);
   int compositionEnd = getComposingSpanEnd(mEditable);
   if (TextUtils.isEmpty(text)
       && (selectionStart == selectionEnd)
       && compositionStart != INVALID_COMPOSITION
       && compositionEnd != INVALID_COMPOSITION) {
     beginBatchEdit();
     finishComposingText();
     int selection = Selection.getSelectionStart(mEditable);
     deleteSurroundingText(selection - compositionStart, selection - compositionEnd);
     endBatchEdit();
     return true;
   }
   return false;
 }
  /** @see BaseInputConnection#sendKeyEvent(android.view.KeyEvent) */
  @Override
  public boolean sendKeyEvent(KeyEvent event) {
    if (DEBUG) {
      Log.w(
          TAG,
          "sendKeyEvent ["
              + event.getAction()
              + "] ["
              + event.getKeyCode()
              + "] ["
              + event.getUnicodeChar()
              + "]");
    }

    int action = event.getAction();
    int keycode = event.getKeyCode();
    int unicodeChar = event.getUnicodeChar();

    // If this isn't a KeyDown event, no need to update composition state; just pass the key
    // event through and return. But note that some keys, such as enter, may actually be
    // handled on ACTION_UP in Blink.
    if (action != KeyEvent.ACTION_DOWN) {
      mImeAdapter.translateAndSendNativeEvents(event);
      return true;
    }

    // If this is backspace/del or if the key has a character representation,
    // need to update the underlying Editable (i.e. the local representation of the text
    // being edited).  Some IMEs like Jellybean stock IME and Samsung IME mix in delete
    // KeyPress events instead of calling deleteSurroundingText.
    if (keycode == KeyEvent.KEYCODE_DEL) {
      deleteSurroundingTextImpl(1, 0, true);
    } else if (keycode == KeyEvent.KEYCODE_FORWARD_DEL) {
      deleteSurroundingTextImpl(0, 1, true);
    } else if (keycode == KeyEvent.KEYCODE_ENTER) {
      // Finish text composition when pressing enter, as that may submit a form field.
      // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed.
      finishComposingText();
    } else if ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) != 0) {
      // Store a pending accent character and make it the current composition.
      int pendingAccent = unicodeChar & KeyCharacterMap.COMBINING_ACCENT_MASK;
      StringBuilder builder = new StringBuilder();
      builder.appendCodePoint(pendingAccent);
      setComposingText(builder.toString(), 1);
      mPendingAccent = pendingAccent;
      return true;
    } else if (mPendingAccent != 0 && unicodeChar != 0) {
      int combined = KeyEvent.getDeadChar(mPendingAccent, unicodeChar);
      if (combined != 0) {
        StringBuilder builder = new StringBuilder();
        builder.appendCodePoint(combined);
        commitText(builder.toString(), 1);
        return true;
      }
      // Noncombinable character; commit the accent character and fall through to sending
      // the key event for the character afterwards.
      finishComposingText();
    }
    replaceSelectionWithUnicodeChar(unicodeChar);
    mImeAdapter.translateAndSendNativeEvents(event);
    return true;
  }
  private boolean deleteSurroundingTextImpl(
      int beforeLength, int afterLength, boolean fromPhysicalKey) {
    if (DEBUG) {
      Log.w(
          TAG,
          "deleteSurroundingText ["
              + beforeLength
              + " "
              + afterLength
              + " "
              + fromPhysicalKey
              + "]");
    }

    if (mPendingAccent != 0) {
      finishComposingText();
    }

    int originalBeforeLength = beforeLength;
    int originalAfterLength = afterLength;
    int selectionStart = Selection.getSelectionStart(mEditable);
    int selectionEnd = Selection.getSelectionEnd(mEditable);
    int availableBefore = selectionStart;
    int availableAfter = mEditable.length() - selectionEnd;
    beforeLength = Math.min(beforeLength, availableBefore);
    afterLength = Math.min(afterLength, availableAfter);

    // Adjust these values even before calling super.deleteSurroundingText() to be consistent
    // with the super class.
    if (isIndexBetweenUtf16SurrogatePair(mEditable, selectionStart - beforeLength)) {
      beforeLength += 1;
    }
    if (isIndexBetweenUtf16SurrogatePair(mEditable, selectionEnd + afterLength)) {
      afterLength += 1;
    }

    super.deleteSurroundingText(beforeLength, afterLength);
    updateSelectionIfRequired();

    // If this was called due to a physical key, no need to generate a key event here as
    // the caller will take care of forwarding the original.
    if (fromPhysicalKey) {
      return true;
    }

    // For single-char deletion calls |ImeAdapter.sendKeyEventWithKeyCode| with the real key
    // code. For multi-character deletion, executes deletion by calling
    // |ImeAdapter.deleteSurroundingText| and sends synthetic key events with a dummy key code.
    int keyCode = KeyEvent.KEYCODE_UNKNOWN;
    if (originalBeforeLength == 1 && originalAfterLength == 0) {
      keyCode = KeyEvent.KEYCODE_DEL;
    } else if (originalBeforeLength == 0 && originalAfterLength == 1) {
      keyCode = KeyEvent.KEYCODE_FORWARD_DEL;
    }

    boolean result = true;
    if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
      result =
          mImeAdapter.sendSyntheticKeyEvent(
              WebInputEventType.RawKeyDown, SystemClock.uptimeMillis(), keyCode, 0, 0);
      result &= mImeAdapter.deleteSurroundingText(beforeLength, afterLength);
      result &=
          mImeAdapter.sendSyntheticKeyEvent(
              WebInputEventType.KeyUp, SystemClock.uptimeMillis(), keyCode, 0, 0);
    } else {
      mImeAdapter.sendKeyEventWithKeyCode(
          keyCode, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE);
    }
    return result;
  }