/** * 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; }