Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(811)

Unified Diff: content/public/android/java/src/org/chromium/content/browser/input/ChromiumInputConnection.java

Issue 1278593004: Introduce ThreadedInputConnection behind a switch (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..bad967608b16f67febaed3f327fe24a84f61f6bf
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ChromiumInputConnection.java
@@ -0,0 +1,735 @@
+// 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.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * TODO(changwan): add a description
+ */
+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;
+
+ // TODO(changwan): this value is used in two threads.
+ private final AtomicBoolean mSingleLine = new AtomicBoolean(true);
aelias_OOO_until_Jul13 2015/09/30 00:10:04 How about making this one of the TextInputState fi
Changwan Ryu 2016/01/19 07:31:53 Good idea! Done.
+ private final BlockingQueue<TextInputState> mQueue = new LinkedBlockingQueue<>();
+ private TextInputState mLastTextInputState;
+
+ @VisibleForTesting
+ ChromiumInputConnection(View view, ImeAdapter imeAdapter) {
+ ImeUtils.assertOnUiThread();
+
+ mHandler = InputConnectionHandlerFactory.getInputConnectionHandler();
+ mInternalView = view;
+ mImeAdapter = imeAdapter;
+ mImeAdapter.setInputConnection(ChromiumInputConnection.this);
+
+ // Make sure the previous composition is finished. There is no proper way to
+ // tell IME about the previous composition by the time this call ends, anyways.
+ // finishComposingText();
aelias_OOO_until_Jul13 2015/09/30 00:10:04 Can this be deleted? Was this useful for anything
Changwan Ryu 2016/01/19 07:31:53 Done.
+
+ Log.d(TAG, "%d Constructor called", System.identityHashCode(this));
+ }
+
+ private void updateToInputMethodManager(TextInputState textInputState) {
+ ImeUtils.assertNotOnUiThread();
+ if (mNumNestedBatchEdits != 0) {
+ mLastTextInputState = textInputState;
+ return;
+ }
+ ImeUtils.assertReally(textInputState != null);
+ if (textInputState.equals(mLastTextInputState)) return;
+ Range selection = textInputState.selection();
+ Range composition = textInputState.composition();
+ getInputMethodManagerWrapper().updateSelection(mInternalView, selection.start(),
+ selection.end(), composition.start(), composition.end());
+ mLastTextInputState = textInputState;
+ }
+
+ public void updateEditorInfo(EditorInfo outAttrs, int inputType, int inputFlags) {
aelias_OOO_until_Jul13 2015/09/30 00:10:04 Please rename this to updateEditorInfoOnUiThread a
Changwan Ryu 2016/01/19 07:31:53 Done.
+ Log.d(TAG, "updateEditorInfo");
+ mSingleLine.set(true);
+ 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) {
aelias_OOO_until_Jul13 2015/09/30 00:10:04 Please move most of this logic into a private stat
Changwan Ryu 2016/01/19 07:31:53 Done.
+ // 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.set(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;
+ }
+
+ Range selection = getSelection();
+ outAttrs.initialSelStart = selection.start();
+ outAttrs.initialSelEnd = selection.end();
+ }
+
+ @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);
+ }
+ Log.d(TAG, "updateState finished");
+ }
+
+ private TextInputState updateTextInputState() {
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ boolean result = mImeAdapter.requestTextInputStateUpdate();
+ if (!result) unblock();
+ }
+ });
+ return blockAndGetStateUpdate();
+ }
+
+ private Range getSelection() {
+ Log.d(TAG, "getSelection");
+ return new Range(0, 0);
aelias_OOO_until_Jul13 2015/09/30 00:10:04 Looks like this needs to be fixed to work properly
Changwan Ryu 2016/01/19 07:31:53 Applied the logic from AdapterInputConnection. BT
+ // TextInputState textInputState = updateTextInputState();
+ // if (textInputState == null) return new Range(0, 0);
+ // return textInputState.selection();
+ }
+
+ private void onStateUpdateOriginatingFromIme(TextInputState textInputState) {
+ ImeUtils.assertOnUiThread();
+ try {
+ mQueue.put(textInputState);
+ } catch (InterruptedException e) {
+ Log.w(TAG, "onStateUpdateOriginatingFromIme interrupted", e);
+ }
+ Log.d(TAG, "%d onStateUpdateOriginatingFromIme finished: %d", System.identityHashCode(this),
+ mQueue.size());
+ }
+
+ /**
+ * TODO(changwan): simplify calls using this.
+ * @param c
+ * @return
+ */
+ private TextInputState runAndGetStateUpdate(final Callable<Boolean> c) {
+ ImeUtils.assertNotOnUiThread();
+ mQueue.clear();
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // TODO(changwan): remove this.
+ Boolean result = null;
+ try {
+ result = c.call();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (result == null || !result.booleanValue()) {
+ // Now we know that updateState() will never be called.
+ unblock();
+ }
+ }
+ });
+
+ return blockAndGetStateUpdate();
+ }
+
+ /**
+ * TODO(changwan): simplify calls using this.
+ * @param c
+ * @return
+ */
+ private TextInputState runAndGetStateUpdate2(final Callable<Boolean> c) {
aelias_OOO_until_Jul13 2015/09/30 00:10:04 This has no callers, can it be deleted?
Changwan Ryu 2016/01/19 07:31:53 Done.
+ mQueue.clear();
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Boolean result = null;
+ try {
+ result = c.call();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (result == null || !result.booleanValue()) {
+ // Now we know that updateState() will never be called.
+ unblock();
+ }
+ }
+ });
+ FutureTask<TextInputState> task = new FutureTask<>(new Callable<TextInputState>() {
+ @Override
+ public TextInputState call() throws Exception {
+ TextInputState result = blockAndGetStateUpdate();
+ // if (result != null) updateToInputMethodManager(result);
+ return result;
+ }
+ });
+ mHandler.post(task);
+ if (!ThreadUtils.runningOnUiThread()) {
+ try {
+ return task.get();
+ } catch (InterruptedException | ExecutionException e) {
+ // TODO(changwan): Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Unblock the blockAndGetStateUpdate() function if we found that we will
+ * never get state update.
+ */
+ private void unblock() {
+ ImeUtils.assertOnUiThread();
+ onStateUpdateOriginatingFromIme(new TextInputState.Dummy());
+ }
+
+ private void postConsumeStateUpdate() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ blockAndGetStateUpdate();
+ }
+ });
+ }
+
+ /**
+ * Block until we get the expected state update.
+ * @return TextInputState if we get it successfully. null otherwise.
+ */
+ private TextInputState blockAndGetStateUpdate() {
+ ImeUtils.assertNotOnUiThread();
+ long timeout = MAX_TIMEOUT;
+ try {
+ while (true) {
+ long startTime = System.currentTimeMillis();
+ TextInputState result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
+
+ if (result != null) {
+ if (result.isDummy()) {
+ Log.d(TAG, "blockAndGetStateUpdate: failed, got dummy");
+ return null;
+ }
+ updateToInputMethodManager(result);
+ return result;
+ }
+
+ long polledTime = System.currentTimeMillis() - startTime;
+ timeout -= polledTime + 10;
+ if (timeout < MIN_TIMEOUT) {
aelias_OOO_until_Jul13 2015/09/30 00:10:04 This doesn't seem useful, how about simply having
Changwan Ryu 2016/01/19 07:31:53 Done.
+ Log.e(TAG, "%d blockAndGetStateUpdate: failed %d",
+ System.identityHashCode(this), mQueue.size());
+ ImeUtils.assertReally(false);
+ return null;
+ }
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ Log.d(TAG, "blockAndGetStateUpdate: 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);
+ ImeUtils.assertNotOnUiThread();
+ mQueue.clear(); // TODO(changwan): remove
aelias_OOO_until_Jul13 2015/09/30 00:10:04 Can all these be removed yet?
Changwan Ryu 2016/01/19 07:31:53 Done.
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ 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() {
+ boolean result = mImeAdapter.checkCompositionQueueAndCallNative(
+ text, newCursorPosition, text.length() > 0);
+ if (!result) unblock();
+ }
+ });
+ return blockAndGetStateUpdate() != null;
+ }
+
+ @Override
+ public void restartInput() {
+ Log.d(TAG, "restartInput");
+ if (ThreadUtils.runningOnUiThread()) {
aelias_OOO_until_Jul13 2015/09/30 00:10:03 After this is renamed restartInputOnUiThread, this
Changwan Ryu 2016/01/19 07:31:53 The logic here has been refactored into ImeAdapter
+ unblock();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ getInputMethodManagerWrapper().restartInput(mInternalView);
+ }
+ });
+ } else {
+ getInputMethodManagerWrapper().restartInput(mInternalView);
+ }
+ }
+
+ /**
+ * @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();
aelias_OOO_until_Jul13 2015/09/30 00:10:04 This is the only IME-thread caller of restartInput
Changwan Ryu 2016/01/19 07:31:53 Thanks for pointing that out. It's already been mo
+ mQueue.clear(); // TODO(changwan): remove
+ // Send TAB key event
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ 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() {
+ 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.get() ? 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 && mLastTextInputState != null) {
+ updateToInputMethodManager(mLastTextInputState);
aelias_OOO_until_Jul13 2015/09/30 00:10:04 I don't think this works as you intended, this is
Changwan Ryu 2016/01/19 07:31:53 You're correct. Now we have mLastUpdatedTextInputS
+ }
+ 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() {
+ boolean result = mImeAdapter.deleteSurroundingText(beforeLength, afterLength);
+ if (!result) unblock();
+ }
+ });
+ return blockAndGetStateUpdate() != null;
+ }
+
+ /**
+ * @see ChromiumBaseInputConnection#dispatchKeyEvent(KeyEvent)
+ */
+ @Override
+ public boolean dispatchKeyEvent(final KeyEvent event) {
+ // TODO(changwan): check if we need to post to IME handler first.
+ ImeUtils.assertOnUiThread();
+ // This should not happen.
+ if (!mImeAdapter.translateAndSendNativeEvents(event)) return false;
+ postConsumeStateUpdate();
+ return true;
+ }
+
+ /**
+ * @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();
+ mQueue.clear(); // TODO(changwan): remove
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ boolean result = mImeAdapter.translateAndSendNativeEvents(event);
+ if (!result) unblock();
+ }
+ });
+ return blockAndGetStateUpdate() != null;
+ }
+
+ /**
+ * @see InputConnection#finishComposingText()
+ */
+ @Override
+ public boolean finishComposingText() {
+ Log.d(TAG, "%d finishComposingText", System.identityHashCode(this));
+ mQueue.clear(); // TODO(changwan): remove
+ // This is the only function that may be called on UI thread because
+ // of direct calls from InputMethodManager.
+
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ boolean result = mImeAdapter.finishComposingText();
+ if (!result) unblock();
+ }
+ });
+
+ if (ThreadUtils.runningOnUiThread()) {
aelias_OOO_until_Jul13 2015/09/30 00:10:04 There doesn't appear to be any (non-unit-test) cod
Changwan Ryu 2016/01/19 07:31:53 Hmm.. How about InputMethodManager#reportFinishInp
+ postConsumeStateUpdate();
+ return true;
+ } else {
+ 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() {
+ 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() {
+ 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);
+ ImeUtils.assertNotOnUiThread();
+ mQueue.clear(); // TODO(changwan): remove
+ 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
aelias_OOO_until_Jul13 2015/09/30 00:10:04 Looks like you have several rarely called methods
Changwan Ryu 2016/01/19 07:31:53 Since InputConnection is an interface, we still ne
+ 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;
+ }
+
+ /**
+ * TODO(changwan): add description
+ */
+ public void reset() {
+ ImeUtils.assertOnUiThread();
+ finishComposingText();
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698