| Index: content/public/android/java/src/org/chromium/content/browser/input/ChromiumInputConnection.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ChromiumInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/ChromiumInputConnection.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..79762a2dff42c1df3dff4b1d766d53c0958032b6
|
| --- /dev/null
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/input/ChromiumInputConnection.java
|
| @@ -0,0 +1,656 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.content.browser.input;
|
| +
|
| +import android.os.Bundle;
|
| +import android.os.Handler;
|
| +import android.os.SystemClock;
|
| +import android.text.InputType;
|
| +import android.view.KeyEvent;
|
| +import android.view.View;
|
| +import android.view.inputmethod.CompletionInfo;
|
| +import android.view.inputmethod.CorrectionInfo;
|
| +import android.view.inputmethod.EditorInfo;
|
| +import android.view.inputmethod.ExtractedText;
|
| +import android.view.inputmethod.ExtractedTextRequest;
|
| +import android.view.inputmethod.InputConnection;
|
| +
|
| +import org.chromium.base.Log;
|
| +import org.chromium.base.ThreadUtils;
|
| +import org.chromium.base.VisibleForTesting;
|
| +import org.chromium.blink_public.web.WebInputEventType;
|
| +import org.chromium.blink_public.web.WebTextInputFlags;
|
| +import org.chromium.ui.base.ime.TextInputType;
|
| +
|
| +import java.util.concurrent.BlockingQueue;
|
| +import java.util.concurrent.Callable;
|
| +import java.util.concurrent.FutureTask;
|
| +import java.util.concurrent.LinkedBlockingQueue;
|
| +import java.util.concurrent.TimeUnit;
|
| +
|
| +/**
|
| + * InputConnection is created by ContentView.onCreateInputConnection.
|
| + * It then adapts android's IME to chrome's RenderWidgetHostView using the
|
| + * native ImeAdapterAndroid via the class ImeAdapter.
|
| + */
|
| +public class ChromiumInputConnection implements ChromiumBaseInputConnection {
|
| + private static final String TAG = "cr.Ime";
|
| +
|
| + // Android will raise TimeoutException to IME if bound call takes 2 seconds.
|
| + private static final long MAX_TIMEOUT = 1900;
|
| + private static final long MIN_TIMEOUT = 15;
|
| +
|
| + private final Handler mHandler;
|
| + private final View mInternalView;
|
| + private final ImeAdapter mImeAdapter;
|
| +
|
| + private int mNumNestedBatchEdits = 0;
|
| +
|
| + private boolean mSingleLine = true;
|
| + private final BlockingQueue<TextInputState> mQueue = new LinkedBlockingQueue<>();
|
| + private TextInputState mTextInputState;
|
| +
|
| + @VisibleForTesting
|
| + ChromiumInputConnection(View view, ImeAdapter imeAdapter, EditorInfo outAttrs) {
|
| + ImeUtils.assertNotOnUiThread();
|
| +
|
| + mHandler = InputConnectionHandlerFactory.getInputConnectionHandler();
|
| + mInternalView = view;
|
| + mImeAdapter = imeAdapter;
|
| + ThreadUtils.runOnUiThreadBlocking(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mImeAdapter.setInputConnection(ChromiumInputConnection.this);
|
| + }
|
| + });
|
| +
|
| + // The editable passed in might have been in use by a prior keyboard and could have had
|
| + // prior composition spans set. To avoid keyboard conflicts, remove all composing spans
|
| + // when taking ownership of an existing Editable.
|
| + finishComposingText();
|
| +
|
| + updateEditorInfo(outAttrs, imeAdapter.getTextInputType(), imeAdapter.getTextInputFlags(),
|
| + getSelection());
|
| +
|
| + Log.d(TAG, "Constructor called with outAttrs: " + ImeUtils.dumpEditorInfo(outAttrs));
|
| + }
|
| +
|
| + private void updateToInputMethodManager(TextInputState textInputState) {
|
| + ImeUtils.assertNotOnUiThread();
|
| + if (mNumNestedBatchEdits != 0) return;
|
| + ImeUtils.assertReally(textInputState != null);
|
| + if (textInputState.equals(mTextInputState)) return;
|
| + Range selection = textInputState.selection();
|
| + Range composition = textInputState.composition();
|
| + getInputMethodManagerWrapper().updateSelection(mInternalView, selection.start(),
|
| + selection.end(), composition.start(), composition.end());
|
| + mTextInputState = textInputState;
|
| + }
|
| +
|
| + private void updateEditorInfo(
|
| + EditorInfo outAttrs, int inputType, int inputFlags, Range selection) {
|
| + outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
|
| + outAttrs.inputType =
|
| + EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
|
| +
|
| + if ((inputFlags & WebTextInputFlags.AutocompleteOff) != 0) {
|
| + outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
|
| + }
|
| +
|
| + if (inputType == TextInputType.TEXT) {
|
| + // Normal text field
|
| + outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO;
|
| + if ((inputFlags & WebTextInputFlags.AutocorrectOff) == 0) {
|
| + outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT;
|
| + }
|
| + } else if (inputType == TextInputType.TEXT_AREA
|
| + || inputType == TextInputType.CONTENT_EDITABLE) {
|
| + outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
|
| + mSingleLine = false;
|
| + if ((inputFlags & WebTextInputFlags.AutocorrectOff) == 0) {
|
| + outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT;
|
| + }
|
| + outAttrs.imeOptions |= EditorInfo.IME_ACTION_NONE;
|
| + } else if (inputType == TextInputType.PASSWORD) {
|
| + // Password
|
| + outAttrs.inputType =
|
| + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD;
|
| + outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO;
|
| + } else if (inputType == TextInputType.SEARCH) {
|
| + // Search
|
| + outAttrs.imeOptions |= EditorInfo.IME_ACTION_SEARCH;
|
| + } else if (inputType == TextInputType.URL) {
|
| + // Url
|
| + outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI;
|
| + outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO;
|
| + } else if (inputType == TextInputType.EMAIL) {
|
| + // Email
|
| + outAttrs.inputType =
|
| + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
|
| + outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO;
|
| + } else if (inputType == TextInputType.TELEPHONE) {
|
| + // Telephone
|
| + // Number and telephone do not have both a Tab key and an
|
| + // action in default OSK, so set the action to NEXT
|
| + outAttrs.inputType = InputType.TYPE_CLASS_PHONE;
|
| + outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
|
| + } else if (inputType == TextInputType.NUMBER) {
|
| + // Number
|
| + outAttrs.inputType = InputType.TYPE_CLASS_NUMBER
|
| + | InputType.TYPE_NUMBER_VARIATION_NORMAL | InputType.TYPE_NUMBER_FLAG_DECIMAL;
|
| + outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
|
| + }
|
| +
|
| + // Handling of autocapitalize. Blink will send the flag taking into account the element's
|
| + // type. This is not using AutocapitalizeNone because Android does not autocapitalize by
|
| + // default and there is no way to express no capitalization.
|
| + // Autocapitalize is meant as a hint to the virtual keyboard.
|
| + if ((inputFlags & WebTextInputFlags.AutocapitalizeCharacters) != 0) {
|
| + outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
|
| + } else if ((inputFlags & WebTextInputFlags.AutocapitalizeWords) != 0) {
|
| + outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
|
| + } else if ((inputFlags & WebTextInputFlags.AutocapitalizeSentences) != 0) {
|
| + outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
|
| + }
|
| + // Content editable doesn't use autocapitalize so we need to set it manually.
|
| + if (inputType == TextInputType.CONTENT_EDITABLE) {
|
| + outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
|
| + }
|
| +
|
| + outAttrs.initialSelStart = selection.start();
|
| + outAttrs.initialSelEnd = selection.end();
|
| + }
|
| +
|
| + private Range getSelection() {
|
| + Log.d(TAG, "getSelection");
|
| + return new Range(0, 0);
|
| + // TextInputState textInputState = updateTextInputState();
|
| + // if (textInputState == null) return new Range(0, 0);
|
| + // return textInputState.selection();
|
| + }
|
| +
|
| + private TextInputState updateTextInputState() {
|
| + ThreadUtils.postOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mImeAdapter.setInputConnection(ChromiumInputConnection.this);
|
| + boolean result = mImeAdapter.requestTextInputStateUpdate();
|
| + if (!result) unblock();
|
| + }
|
| + });
|
| + return blockAndGetStateUpdate();
|
| + }
|
| +
|
| + @Override
|
| + public void updateState(final String text, final int selectionStart, final int selectionEnd,
|
| + final int compositionStart, final int compositionEnd, final boolean isNonImeChange) {
|
| + Log.d(TAG, "updateState [%s] [%s %s] [%s %s] [%b]", text, selectionStart, selectionEnd,
|
| + compositionStart, compositionEnd, isNonImeChange);
|
| + ImeUtils.assertOnUiThread();
|
| +
|
| + // TODO(changwan): this should be called at the end of each call, not after it.
|
| + final TextInputState newState =
|
| + new TextInputState(text, new Range(selectionStart, selectionEnd),
|
| + new Range(compositionStart, compositionEnd));
|
| +
|
| + if (isNonImeChange) {
|
| + mHandler.post(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + updateToInputMethodManager(newState);
|
| + }
|
| + });
|
| + } else {
|
| + onStateUpdateOriginatingFromIme(newState);
|
| + }
|
| + }
|
| +
|
| + private void onStateUpdateOriginatingFromIme(TextInputState textInputState) {
|
| + ImeUtils.assertOnUiThread();
|
| + try {
|
| + mQueue.put(textInputState);
|
| + } catch (InterruptedException e) {
|
| + ImeUtils.assertReally(false);
|
| + }
|
| + Log.d(TAG, "%d onStateUpdateOriginatingFromIme finished: %d", System.identityHashCode(this),
|
| + mQueue.size());
|
| + }
|
| +
|
| + private void unblock() {
|
| + ImeUtils.assertOnUiThread();
|
| + onStateUpdateOriginatingFromIme(new TextInputState.Dummy());
|
| + }
|
| +
|
| + /**
|
| + * Block until we get the expected state update.
|
| + * @return True if we get state update successfully or we are on UI thread.
|
| + */
|
| + private TextInputState blockAndGetStateUpdate() {
|
| + if (ThreadUtils.runningOnUiThread()) {
|
| + // There will be a deadlock if we block here.
|
| + FutureTask<TextInputState> task =
|
| + new FutureTask<TextInputState>(new Callable<TextInputState>() {
|
| + @Override
|
| + public TextInputState call() throws Exception {
|
| + return blockAndGetStateUpdateInternal();
|
| + }
|
| + });
|
| + mHandler.post(task);
|
| + try {
|
| + return task.get(MAX_TIMEOUT, TimeUnit.MILLISECONDS);
|
| + } catch (Exception e) {
|
| + e.printStackTrace();
|
| + return null;
|
| + }
|
| + }
|
| + return blockAndGetStateUpdateInternal();
|
| + }
|
| +
|
| + private TextInputState blockAndGetStateUpdateInternal() {
|
| + Log.d(TAG, "blockAndGetStateUpdateInternal: started");
|
| + ImeUtils.assertNotOnUiThread();
|
| + long timeout = MAX_TIMEOUT;
|
| + try {
|
| + while (true) {
|
| + long startTime = System.currentTimeMillis();
|
| + TextInputState result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
|
| + Log.d(TAG, "%d blockAndGetStateUpdateInternal: got result: %s",
|
| + System.identityHashCode(this), result);
|
| +
|
| + if (result != null) {
|
| + if (result.isDummy()) {
|
| + Log.d(TAG, "blockAndGetStateUpdateInternal: failed, got dummy");
|
| + return null;
|
| + }
|
| + Log.d(TAG, "blockAndGetStateUpdateInternal: success");
|
| + updateToInputMethodManager(result);
|
| + return result;
|
| + }
|
| +
|
| + long polledTime = System.currentTimeMillis() - startTime;
|
| + timeout -= polledTime + 10;
|
| + if (timeout < MIN_TIMEOUT) {
|
| + Log.d(TAG, "blockAndGetStateUpdateInternal: failed %d", mQueue.size());
|
| + return null;
|
| + }
|
| + Log.d(TAG, "blockAndGetStateUpdateInternal: trying a bit more");
|
| + }
|
| + } catch (InterruptedException e) {
|
| + e.printStackTrace();
|
| + Log.d(TAG, "blockAndGetStateUpdateInternal: failed due to interruption");
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#setComposingText(java.lang.CharSequence, int)
|
| + */
|
| + @Override
|
| + public boolean setComposingText(final CharSequence text, final int newCursorPosition) {
|
| + Log.d(TAG, "setComposingText [%s] [%d]", text, newCursorPosition);
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + ImeUtils.assertNotOnUiThread();
|
| +
|
| + ThreadUtils.postOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mImeAdapter.setInputConnection(ChromiumInputConnection.this);
|
| + boolean result = mImeAdapter.checkCompositionQueueAndCallNative(
|
| + text, newCursorPosition, false);
|
| + if (!result) unblock();
|
| + }
|
| + });
|
| +
|
| + return blockAndGetStateUpdate() != null;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#commitText(java.lang.CharSequence, int)
|
| + */
|
| + @Override
|
| + public boolean commitText(final CharSequence text, final int newCursorPosition) {
|
| + Log.d(TAG, "commitText [%s] [%d]", text, newCursorPosition);
|
| + ImeUtils.assertNotOnUiThread();
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + ThreadUtils.postOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mImeAdapter.setInputConnection(ChromiumInputConnection.this);
|
| + boolean result = mImeAdapter.checkCompositionQueueAndCallNative(
|
| + text, newCursorPosition, text.length() > 0);
|
| + if (!result) unblock();
|
| + }
|
| + });
|
| + return blockAndGetStateUpdate() != null;
|
| + }
|
| +
|
| + @Override
|
| + public void restartInput() {
|
| + Log.d(TAG, "restartInput");
|
| + getInputMethodManagerWrapper().restartInput(mInternalView);
|
| + if (ThreadUtils.runningOnUiThread()) {
|
| + unblock();
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#performEditorAction(int)
|
| + */
|
| + @Override
|
| + public boolean performEditorAction(int actionCode) {
|
| + Log.d(TAG, "performEditorAction [%d]", actionCode);
|
| + ImeUtils.assertNotOnUiThread();
|
| + if (actionCode == EditorInfo.IME_ACTION_NEXT) {
|
| + restartInput();
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + // Send TAB key event
|
| + ThreadUtils.postOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mImeAdapter.setInputConnection(ChromiumInputConnection.this);
|
| + long timeStampMs = SystemClock.uptimeMillis();
|
| + boolean result = mImeAdapter.sendSyntheticKeyEvent(
|
| + WebInputEventType.RawKeyDown, timeStampMs, KeyEvent.KEYCODE_TAB, 0, 0);
|
| + if (!result) unblock();
|
| + }
|
| + });
|
| + return blockAndGetStateUpdate() != null;
|
| + } else {
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + ThreadUtils.postOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mImeAdapter.setInputConnection(ChromiumInputConnection.this);
|
| + boolean result = mImeAdapter.sendKeyEventWithKeyCode(
|
| + KeyEvent.KEYCODE_ENTER, KeyEvent.FLAG_SOFT_KEYBOARD
|
| + | KeyEvent.FLAG_KEEP_TOUCH_MODE | KeyEvent.FLAG_EDITOR_ACTION);
|
| + if (!result) unblock();
|
| + }
|
| + });
|
| + return blockAndGetStateUpdate() != null;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#performContextMenuAction(int)
|
| + */
|
| + @Override
|
| + public boolean performContextMenuAction(final int id) {
|
| + Log.d(TAG, "performContextMenuAction [%d]", id);
|
| + ImeUtils.assertNotOnUiThread();
|
| + ThreadUtils.postOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mImeAdapter.performContextMenuAction(id);
|
| + }
|
| + });
|
| + // TODO(changwan): return correct value
|
| + return true;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#getExtractedText(android.view.inputmethod.ExtractedTextRequest,
|
| + * int)
|
| + */
|
| + @Override
|
| + public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
|
| + Log.d(TAG, "getExtractedText");
|
| + ImeUtils.assertNotOnUiThread();
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + TextInputState textInputState = updateTextInputState();
|
| + if (textInputState == null) return null;
|
| + ExtractedText extractedText = new ExtractedText();
|
| + extractedText.text = textInputState.text();
|
| + extractedText.partialEndOffset = textInputState.text().length();
|
| + extractedText.selectionStart = textInputState.selection().start();
|
| + extractedText.selectionEnd = textInputState.selection().end();
|
| + extractedText.flags = mSingleLine ? ExtractedText.FLAG_SINGLE_LINE : 0;
|
| + return extractedText;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#beginBatchEdit()
|
| + */
|
| + @Override
|
| + public boolean beginBatchEdit() {
|
| + Log.d(TAG, "beginBatchEdit [%b]", (mNumNestedBatchEdits == 0));
|
| + ImeUtils.assertNotOnUiThread();
|
| + mNumNestedBatchEdits++;
|
| + return true;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#endBatchEdit()
|
| + */
|
| + @Override
|
| + public boolean endBatchEdit() {
|
| + ImeUtils.assertNotOnUiThread();
|
| + if (mNumNestedBatchEdits == 0) return false;
|
| + --mNumNestedBatchEdits;
|
| + Log.d(TAG, "endBatchEdit [%b]", (mNumNestedBatchEdits == 0));
|
| + if (mNumNestedBatchEdits == 0 && mTextInputState != null) {
|
| + updateToInputMethodManager(mTextInputState);
|
| + }
|
| + return mNumNestedBatchEdits != 0;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#deleteSurroundingText(int, int)
|
| + */
|
| + @Override
|
| + public boolean deleteSurroundingText(final int beforeLength, final int afterLength) {
|
| + Log.d(TAG, "deleteSurroundingText [%d %d]", beforeLength, afterLength);
|
| + ImeUtils.assertNotOnUiThread();
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + ThreadUtils.postOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mImeAdapter.setInputConnection(ChromiumInputConnection.this);
|
| + boolean result = mImeAdapter.deleteSurroundingText(beforeLength, afterLength);
|
| + if (!result) unblock();
|
| + }
|
| + });
|
| + return blockAndGetStateUpdate() != null;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#sendKeyEvent(android.view.KeyEvent)
|
| + */
|
| + @Override
|
| + public boolean sendKeyEvent(final KeyEvent event) {
|
| + Log.d(TAG, "sendKeyEvent [%d %d]", event.getAction(), event.getKeyCode());
|
| + // ImeUtils.assertNotOnUiThread();
|
| + // ImeAdapter#dispatchKeyEvent() may call this on UI thread.
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + ThreadUtils.postOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mImeAdapter.setInputConnection(ChromiumInputConnection.this);
|
| + boolean result = mImeAdapter.translateAndSendNativeEvents(event);
|
| + if (!result) unblock();
|
| + }
|
| + });
|
| + return blockAndGetStateUpdate() != null;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#finishComposingText()
|
| + */
|
| + @Override
|
| + public boolean finishComposingText() {
|
| + Log.d(TAG, "finishComposingText");
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + // From UI thread: InputMethodManager#checkFocusNoStartInput().
|
| + // ImeUtils.assertNotOnUiThread();
|
| + ThreadUtils.postOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mImeAdapter.setInputConnection(ChromiumInputConnection.this);
|
| + boolean result = mImeAdapter.finishComposingText();
|
| + if (!result) unblock();
|
| + }
|
| + });
|
| + return blockAndGetStateUpdate() != null;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#setSelection(int, int)
|
| + */
|
| + @Override
|
| + public boolean setSelection(final int start, final int end) {
|
| + Log.d(TAG, "setSelection [%d %d]", start, end);
|
| + ImeUtils.assertNotOnUiThread();
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + ThreadUtils.postOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mImeAdapter.setInputConnection(ChromiumInputConnection.this);
|
| + boolean result = mImeAdapter.setEditableSelectionOffsets(start, end);
|
| + if (!result) unblock();
|
| + }
|
| + });
|
| + return blockAndGetStateUpdate() != null;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#setComposingRegion(int, int)
|
| + */
|
| + @Override
|
| + public boolean setComposingRegion(final int start, final int end) {
|
| + Log.d(TAG, "setComposingRegion [%d %d]", start, end);
|
| + ImeUtils.assertNotOnUiThread();
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + ThreadUtils.postOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mImeAdapter.setInputConnection(ChromiumInputConnection.this);
|
| + boolean result = mImeAdapter.setComposingRegion("", start, end);
|
| + if (!result) unblock();
|
| + }
|
| + });
|
| + return blockAndGetStateUpdate() != null;
|
| + }
|
| +
|
| + private InputMethodManagerWrapper getInputMethodManagerWrapper() {
|
| + return mImeAdapter.getInputMethodManagerWrapper();
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#getTextBeforeCursor(int, int)
|
| + */
|
| + @Override
|
| + public CharSequence getTextBeforeCursor(int maxChars, int flags) {
|
| + Log.d(TAG, "getTextBeforeCursor [%d %x]", maxChars, flags);
|
| + ImeUtils.assertNotOnUiThread();
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + TextInputState textInputState = updateTextInputState();
|
| + if (textInputState == null) return null;
|
| + return textInputState.getTextBeforeSelection(maxChars);
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#getTextAfterCursor(int, int)
|
| + */
|
| + @Override
|
| + public CharSequence getTextAfterCursor(int maxChars, int flags) {
|
| + Log.d(TAG, "getTextAfterCursor [%d %x]", maxChars, flags);
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + ImeUtils.assertNotOnUiThread();
|
| + TextInputState textInputState = updateTextInputState();
|
| + if (textInputState == null) return null;
|
| + return textInputState.getTextAfterSelection(maxChars);
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#getSelectedText(int)
|
| + */
|
| + @Override
|
| + public CharSequence getSelectedText(int flags) {
|
| + Log.d(TAG, "getSelectedText [%x]", flags);
|
| + mQueue.clear(); // TODO(changwan): remove
|
| + ImeUtils.assertNotOnUiThread();
|
| + TextInputState textInputState = updateTextInputState();
|
| + if (textInputState == null) return null;
|
| + return textInputState.getSelectedText();
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#getCursorCapsMode(int)
|
| + */
|
| + @Override
|
| + public int getCursorCapsMode(int reqModes) {
|
| + Log.d(TAG, "getCursorCapsMode [%x]", reqModes);
|
| + ImeUtils.assertNotOnUiThread();
|
| + // TODO(changwan): Auto-generated method stub
|
| + return 0;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#commitCompletion(android.view.inputmethod.CompletionInfo)
|
| + */
|
| + @Override
|
| + public boolean commitCompletion(CompletionInfo text) {
|
| + Log.d(TAG, "commitCompletion [%s]", text);
|
| + ImeUtils.assertNotOnUiThread();
|
| + // TODO(changwan): Auto-generated method stub
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#commitCorrection(android.view.inputmethod.CorrectionInfo)
|
| + */
|
| + @Override
|
| + public boolean commitCorrection(CorrectionInfo correctionInfo) {
|
| + Log.d(TAG, "commitCorrection [%s]", ImeUtils.dumpCorrectionInfo(correctionInfo));
|
| + ImeUtils.assertNotOnUiThread();
|
| + // TODO(changwan): Auto-generated method stub
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#clearMetaKeyStates(int)
|
| + */
|
| + @Override
|
| + public boolean clearMetaKeyStates(int states) {
|
| + Log.d(TAG, "clearMetaKeyStates [%x]", states);
|
| + ImeUtils.assertNotOnUiThread();
|
| + // TODO(changwan): Auto-generated method stub
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#reportFullscreenMode(boolean)
|
| + */
|
| + @Override
|
| + public boolean reportFullscreenMode(boolean enabled) {
|
| + Log.d(TAG, "reportFullscreenMode [%b]", enabled);
|
| + ImeUtils.assertNotOnUiThread();
|
| + // We ignore fullscreen mode for now. That's why we set
|
| + // EditorInfo.IME_FLAG_NO_FULLSCREEN in constructor.
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#performPrivateCommand(java.lang.String, android.os.Bundle)
|
| + */
|
| + @Override
|
| + public boolean performPrivateCommand(String action, Bundle data) {
|
| + Log.d(TAG, "performPrivateCommand [%s]", action);
|
| + ImeUtils.assertNotOnUiThread();
|
| + // TODO(changwan): Auto-generated method stub
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * @see InputConnection#requestCursorUpdates(int)
|
| + */
|
| + @Override
|
| + public boolean requestCursorUpdates(int cursorUpdateMode) {
|
| + Log.d(TAG, "requestCursorUpdates [%x]", cursorUpdateMode);
|
| + ImeUtils.assertNotOnUiThread();
|
| + // TODO(changwan): Auto-generated method stub
|
| + return false;
|
| + }
|
| +}
|
|
|