Chromium Code Reviews| Index: content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java |
| diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..fd7c3372e45ebc5af0301740b7b635086531aa0b |
| --- /dev/null |
| +++ b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java |
| @@ -0,0 +1,629 @@ |
| +// Copyright 2016 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.Looper; |
| +import android.view.KeyCharacterMap; |
| +import android.view.KeyEvent; |
| +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 java.util.concurrent.BlockingQueue; |
| +import java.util.concurrent.LinkedBlockingQueue; |
| + |
| +/** |
| + * An implementation of {@link InputConnection} to communicate with external input method |
| + * apps. Note that it is running on IME thread (except for constructor and calls from ImeAdapter) |
| + * such that it does not block UI thread and returns text values immediately after any change |
| + * to them. |
| + */ |
| +public class ThreadedInputConnection implements ChromiumBaseInputConnection { |
| + private static final String TAG = "cr_Ime"; |
| + private static final boolean DEBUG_LOGS = false; |
| + |
| + private static final TextInputState UNBLOCKER = new TextInputState( |
| + "", new Range(0, 0), new Range(-1, -1), false, false /* notFromIme */) { |
| + |
| + @Override |
| + public boolean shouldUnblock() { |
| + return true; |
| + } |
| + }; |
| + |
| + private final ImeAdapter mImeAdapter; |
| + private final Handler mHandler; |
| + private int mNumNestedBatchEdits; |
| + |
| + // TODO(changwan): check if we can keep a pool of TextInputState to avoid creating |
| + // a bunch of new objects for each key stroke. |
| + private final BlockingQueue<TextInputState> mQueue = new LinkedBlockingQueue<>(); |
| + private int mPendingAccent; |
| + // This should be accessed on UI thread. |
| + private TextInputState mLastUpdatedTextInputState; |
| + |
| + ThreadedInputConnection(ImeAdapter imeAdapter, Handler handler) { |
| + if (DEBUG_LOGS) Log.w(TAG, "constructor"); |
| + ImeUtils.checkOnUiThread(); |
| + mImeAdapter = imeAdapter; |
| + mHandler = handler; |
|
Ted C
2016/02/17 19:09:34
should we add an assert that handler.getLooper() !
Changwan Ryu
2016/02/18 06:03:27
Done.
Changwan Ryu
2016/02/18 10:38:42
Hmmm.. Actually ThreadedInputConnectionTest is usi
|
| + } |
| + |
| + void initializeOutAttrsOnUiThread(int inputType, int inputFlags, EditorInfo outAttrs) { |
| + if (DEBUG_LOGS) Log.w(TAG, "initializeOutAttrs"); |
| + ImeUtils.checkOnUiThread(); |
| + int initialSelStart = 0; |
| + int initialSelEnd = 0; |
| + mNumNestedBatchEdits = 0; |
| + mPendingAccent = 0; |
| + if (mLastUpdatedTextInputState != null) { |
| + initialSelStart = mLastUpdatedTextInputState.selection().start(); |
| + initialSelEnd = mLastUpdatedTextInputState.selection().end(); |
| + } |
| + ImeUtils.computeEditorInfo(inputType, inputFlags, initialSelStart, initialSelEnd, outAttrs); |
| + } |
| + |
| + @Override |
| + public void updateStateOnUiThread(final String text, final int selectionStart, |
| + final int selectionEnd, final int compositionStart, final int compositionEnd, |
| + boolean singleLine, final boolean isNonImeChange) { |
| + ImeUtils.checkOnUiThread(); |
| + |
| + final TextInputState newState = |
| + new TextInputState(text, new Range(selectionStart, selectionEnd), |
| + new Range(compositionStart, compositionEnd), singleLine, !isNonImeChange); |
| + if (DEBUG_LOGS) Log.w(TAG, "updateState: %s", newState); |
| + mLastUpdatedTextInputState = newState; |
| + |
| + addToQueueOnUiThread(newState); |
| + if (isNonImeChange) { |
| + mHandler.post(new Runnable() { |
|
Ted C
2016/02/17 19:09:34
You can probably keep a single one of these runnab
Changwan Ryu
2016/02/18 06:03:26
Done.
|
| + @Override |
| + public void run() { |
| + checkQueue(); |
| + } |
| + }); |
| + } |
| + } |
| + |
| + /** |
| + * @see ChromiumBaseInputConnection#getHandler() |
| + */ |
| + @Override |
| + public Handler getHandler() { |
| + return mHandler; |
| + } |
| + |
| + /** |
| + * @see ChromiumBaseInputConnection#onRestartInputOnUiThread() |
| + */ |
| + @Override |
| + public void onRestartInputOnUiThread() {} |
| + |
| + /** |
| + * @see ChromiumBaseInputConnection#sendKeyEventOnUiThread(KeyEvent) |
| + */ |
| + @Override |
| + public boolean sendKeyEventOnUiThread(final KeyEvent event) { |
| + ImeUtils.checkOnUiThread(); |
| + mHandler.post(new Runnable() { |
| + @Override |
| + public void run() { |
| + sendKeyEvent(event); |
| + } |
| + }); |
| + return true; |
| + } |
| + |
| + /** |
| + * @see ChromiumBaseInputConnection#moveCursorToSelectionEndOnUiThread() |
| + */ |
| + @Override |
| + public void moveCursorToSelectionEndOnUiThread() { |
| + mHandler.post(new Runnable() { |
| + @Override |
| + public void run() { |
| + TextInputState textInputState = requestTextInputState(); |
| + if (textInputState == null) return; |
| + Range selection = textInputState.selection(); |
| + setSelection(selection.end(), selection.end()); |
| + } |
| + }); |
| + } |
| + |
| + @Override |
| + @VisibleForTesting |
| + public void unblockOnUiThread() { |
| + if (DEBUG_LOGS) Log.w(TAG, "unblockOnUiThread"); |
| + ImeUtils.checkOnUiThread(); |
| + addToQueueOnUiThread(UNBLOCKER); |
| + mHandler.post(new Runnable() { |
| + @Override |
| + public void run() { |
| + checkQueue(); |
| + } |
| + }); |
| + } |
| + |
| + private void checkQueue() { |
|
Ted C
2016/02/17 19:09:34
this is more about emptying the queue than checkin
Changwan Ryu
2016/02/18 06:03:27
Done.
|
| + if (DEBUG_LOGS) Log.w(TAG, "checkQueue"); |
| + assertOnImeThread(); |
| + // Handle all the remaining states in the queue. |
| + while (true) { |
| + TextInputState state = mQueue.peek(); |
|
Ted C
2016/02/17 19:09:34
just use poll() here and delete the line below. p
Changwan Ryu
2016/02/18 06:03:26
Done.
|
| + if (state == null) { |
| + if (DEBUG_LOGS) Log.w(TAG, "checkQueue - finished"); |
| + return; |
| + } |
| + mQueue.poll(); |
| + if (DEBUG_LOGS) Log.w(TAG, "checkQueue: " + state); |
| + // Unblocker was not used. Ignore. |
| + if (state.shouldUnblock()) { |
| + if (DEBUG_LOGS) Log.w(TAG, "checkQueue - ignoring one unblocker"); |
| + continue; |
| + } |
| + ImeUtils.checkCondition(!state.fromIme()); |
| + updateSelection(state); |
| + } |
| + } |
| + |
| + private void updateSelection(TextInputState textInputState) { |
| + if (textInputState == null) return; |
| + assertOnImeThread(); |
| + if (mNumNestedBatchEdits != 0) return; |
| + ImeUtils.checkCondition(textInputState != null); |
|
Ted C
2016/02/17 19:09:34
this seems unnecessary w/ regards to line 182
Changwan Ryu
2016/02/18 06:03:26
Done.
|
| + Range selection = textInputState.selection(); |
| + Range composition = textInputState.composition(); |
| + mImeAdapter.updateSelection( |
| + selection.start(), selection.end(), composition.start(), composition.end()); |
| + } |
| + |
| + private TextInputState requestTextInputState() { |
|
Ted C
2016/02/17 19:09:34
I "might" suffix this with Blocking as it will wai
Changwan Ryu
2016/02/18 06:03:26
Changed name as requestAndWaitForTextInputState.
|
| + if (DEBUG_LOGS) Log.w(TAG, "requestTextInputState"); |
| + ThreadUtils.postOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + boolean result = mImeAdapter.requestTextInputStateUpdate(); |
| + if (!result) unblockOnUiThread(); |
| + } |
| + }); |
| + return blockAndGetStateUpdate(); |
| + } |
| + |
| + private void addToQueueOnUiThread(TextInputState textInputState) { |
| + ImeUtils.checkOnUiThread(); |
| + try { |
| + mQueue.put(textInputState); |
| + } catch (InterruptedException e) { |
| + Log.e(TAG, "addToQueueOnUiThread interrupted", e); |
| + } |
| + if (DEBUG_LOGS) Log.w(TAG, "addToQueueOnUiThread finished: %d", mQueue.size()); |
| + } |
| + |
| + /** |
| + * @return BlockingQueue for white box unit testing. |
| + */ |
| + BlockingQueue<TextInputState> getQueueForTest() { |
| + return mQueue; |
| + } |
| + |
| + private void assertOnImeThread() { |
| + ImeUtils.checkCondition(mHandler.getLooper() == Looper.myLooper()); |
| + } |
| + |
| + /** |
| + * Block until we get the expected state update. |
| + * @return TextInputState if we get it successfully. null otherwise. |
| + */ |
| + private TextInputState blockAndGetStateUpdate() { |
| + if (DEBUG_LOGS) Log.w(TAG, "blockAndGetStateUpdate"); |
| + assertOnImeThread(); |
| + boolean shouldUpdateSelection = false; |
| + while (true) { |
| + TextInputState state; |
| + try { |
| + state = mQueue.take(); |
| + } catch (InterruptedException e) { |
| + e.printStackTrace(); |
| + return null; |
|
Ted C
2016/02/17 19:09:34
I don't think you should quietly ignore this. If
Changwan Ryu
2016/02/18 06:03:26
Hmmm... IME thread is an artificial thread and cur
|
| + } |
| + if (state == null) return null; |
|
Ted C
2016/02/17 19:09:34
can this be null? "seems" like take take() doesn'
Changwan Ryu
2016/02/18 06:03:26
No, it can't. Removed the line.
|
| + if (state.shouldUnblock()) { |
| + if (DEBUG_LOGS) Log.w(TAG, "blockAndGetStateUpdate: unblocked"); |
| + return null; |
| + } else if (state.fromIme()) { |
| + if (shouldUpdateSelection) updateSelection(state); |
| + if (DEBUG_LOGS) Log.w(TAG, "blockAndGetStateUpdate done: %d", mQueue.size()); |
| + return state; |
| + } |
| + // Ignore when state is not from IME, but make sure to update state when we handle |
| + // state from IME. |
| + shouldUpdateSelection = true; |
|
Ted C
2016/02/17 19:09:34
What happens if there are no pending events from t
Changwan Ryu
2016/02/18 06:03:27
This should never happen in theory, but when it ha
|
| + } |
| + } |
| + |
| + private void notifyUserAction() { |
| + ThreadUtils.postOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + mImeAdapter.notifyUserAction(); |
| + } |
| + }); |
| + } |
| + |
| + /** |
| + * @see InputConnection#setComposingText(java.lang.CharSequence, int) |
| + */ |
| + @Override |
| + public boolean setComposingText(final CharSequence text, final int newCursorPosition) { |
| + if (DEBUG_LOGS) Log.w(TAG, "setComposingText [%s] [%d]", text, newCursorPosition); |
| + assertOnImeThread(); |
| + cancelCombiningAccent(); |
| + ThreadUtils.postOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + mImeAdapter.sendCompositionToNative(text, newCursorPosition, false); |
| + } |
| + }); |
| + notifyUserAction(); |
| + return true; |
| + } |
| + |
| + /** |
| + * @see InputConnection#commitText(java.lang.CharSequence, int) |
| + */ |
| + @Override |
| + public boolean commitText(final CharSequence text, final int newCursorPosition) { |
| + if (DEBUG_LOGS) Log.w(TAG, "commitText [%s] [%d]", text, newCursorPosition); |
| + assertOnImeThread(); |
| + cancelCombiningAccent(); |
| + ThreadUtils.postOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + mImeAdapter.sendCompositionToNative(text, newCursorPosition, text.length() > 0); |
| + } |
| + }); |
| + notifyUserAction(); |
| + return true; |
| + } |
| + |
| + /** |
| + * @see InputConnection#performEditorAction(int) |
| + */ |
| + @Override |
| + public boolean performEditorAction(final int actionCode) { |
| + if (DEBUG_LOGS) Log.w(TAG, "performEditorAction [%d]", actionCode); |
| + assertOnImeThread(); |
| + ThreadUtils.postOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + mImeAdapter.performEditorAction(actionCode); |
| + } |
| + }); |
| + return true; |
| + } |
| + |
| + /** |
| + * @see InputConnection#performContextMenuAction(int) |
| + */ |
| + @Override |
| + public boolean performContextMenuAction(final int id) { |
| + if (DEBUG_LOGS) Log.w(TAG, "performContextMenuAction [%d]", id); |
| + assertOnImeThread(); |
| + ThreadUtils.postOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + mImeAdapter.performContextMenuAction(id); |
| + } |
| + }); |
| + return true; |
| + } |
| + |
| + /** |
| + * @see InputConnection#getExtractedText(android.view.inputmethod.ExtractedTextRequest, |
| + * int) |
|
Ted C
2016/02/17 19:09:34
this indenting looks wonky
Changwan Ryu
2016/02/18 06:03:27
Fixed
|
| + */ |
| + @Override |
| + public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { |
| + if (DEBUG_LOGS) Log.w(TAG, "getExtractedText"); |
| + assertOnImeThread(); |
| + TextInputState textInputState = requestTextInputState(); |
| + 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 = textInputState.singleLine() ? ExtractedText.FLAG_SINGLE_LINE : 0; |
| + return extractedText; |
| + } |
| + |
| + /** |
| + * @see InputConnection#beginBatchEdit() |
| + */ |
| + @Override |
| + public boolean beginBatchEdit() { |
| + if (DEBUG_LOGS) Log.w(TAG, "beginBatchEdit [%b]", (mNumNestedBatchEdits == 0)); |
| + assertOnImeThread(); |
| + mNumNestedBatchEdits++; |
| + return true; |
| + } |
| + |
| + /** |
| + * @see InputConnection#endBatchEdit() |
| + */ |
| + @Override |
| + public boolean endBatchEdit() { |
| + assertOnImeThread(); |
| + if (mNumNestedBatchEdits == 0) return false; |
| + --mNumNestedBatchEdits; |
| + if (DEBUG_LOGS) Log.w(TAG, "endBatchEdit [%b]", (mNumNestedBatchEdits == 0)); |
| + if (mNumNestedBatchEdits == 0) { |
| + updateSelection(requestTextInputState()); |
| + } |
| + return mNumNestedBatchEdits != 0; |
| + } |
| + |
| + /** |
| + * @see InputConnection#deleteSurroundingText(int, int) |
| + */ |
| + @Override |
| + public boolean deleteSurroundingText(final int beforeLength, final int afterLength) { |
| + if (DEBUG_LOGS) Log.w(TAG, "deleteSurroundingText [%d %d]", beforeLength, afterLength); |
| + assertOnImeThread(); |
| + if (mPendingAccent != 0) { |
| + finishComposingText(); |
| + } |
| + ThreadUtils.postOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + mImeAdapter.deleteSurroundingText(beforeLength, afterLength); |
| + } |
| + }); |
| + return true; |
| + } |
| + |
| + /** |
| + * @see InputConnection#sendKeyEvent(android.view.KeyEvent) |
| + */ |
| + @Override |
| + public boolean sendKeyEvent(final KeyEvent event) { |
| + if (DEBUG_LOGS) Log.w(TAG, "sendKeyEvent [%d %d]", event.getAction(), event.getKeyCode()); |
| + assertOnImeThread(); |
| + |
| + if (handleCombiningAccent(event)) return true; |
| + |
| + ThreadUtils.postOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + mImeAdapter.sendKeyEvent(event); |
| + } |
| + }); |
| + notifyUserAction(); |
| + return true; |
| + } |
| + |
| + private boolean handleCombiningAccent(final KeyEvent event) { |
| + // TODO(changwan): this will break the current composition. check if we can |
| + // implement it in the renderer instead. |
| + int action = event.getAction(); |
| + int unicodeChar = event.getUnicodeChar(); |
| + |
| + if (action != KeyEvent.ACTION_DOWN) return false; |
| + if ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) != 0) { |
| + 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(); |
| + } |
| + return false; |
| + } |
| + |
| + private void cancelCombiningAccent() { |
| + mPendingAccent = 0; |
| + } |
| + |
| + /** |
| + * Call finishComposingText on UI thread. |
| + */ |
| + public void finishComposingTextOnUiThread() { |
| + if (DEBUG_LOGS) Log.w(TAG, "finishComposingTextOnUiThread"); |
| + ThreadUtils.postOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + finishComposingText(); |
| + } |
| + }); |
| + } |
| + |
| + /** |
| + * @see InputConnection#finishComposingText() |
| + */ |
| + @Override |
| + public boolean finishComposingText() { |
| + if (DEBUG_LOGS) Log.w(TAG, "finishComposingText"); |
| + Log.w(TAG, "called from: " + Thread.currentThread().getStackTrace()[3].getMethodName()); |
|
Ted C
2016/02/17 19:09:34
remove?
Changwan Ryu
2016/02/18 06:03:27
Done.
|
| + cancelCombiningAccent(); |
| + // 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() { |
| + mImeAdapter.finishComposingText(); |
| + } |
| + }); |
| + return true; |
| + } |
| + |
| + /** |
| + * @see InputConnection#setSelection(int, int) |
| + */ |
| + @Override |
| + public boolean setSelection(final int start, final int end) { |
| + if (DEBUG_LOGS) Log.w(TAG, "setSelection [%d %d]", start, end); |
| + assertOnImeThread(); |
| + ThreadUtils.postOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + mImeAdapter.setEditableSelectionOffsets(start, end); |
| + } |
| + }); |
| + return true; |
| + } |
| + |
| + /** |
| + * @see InputConnection#setComposingRegion(int, int) |
| + */ |
| + @Override |
| + public boolean setComposingRegion(final int start, final int end) { |
| + if (DEBUG_LOGS) Log.w(TAG, "setComposingRegion [%d %d]", start, end); |
| + assertOnImeThread(); |
| + ThreadUtils.postOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + mImeAdapter.setComposingRegion(start, end); |
| + } |
| + }); |
| + return true; |
| + } |
| + |
| + /** |
| + * @see InputConnection#getTextBeforeCursor(int, int) |
| + */ |
| + @Override |
| + public CharSequence getTextBeforeCursor(int maxChars, int flags) { |
| + if (DEBUG_LOGS) Log.w(TAG, "getTextBeforeCursor [%d %x]", maxChars, flags); |
| + assertOnImeThread(); |
| + TextInputState textInputState = requestTextInputState(); |
| + if (textInputState == null) return null; |
| + return textInputState.getTextBeforeSelection(maxChars); |
| + } |
| + |
| + /** |
| + * @see InputConnection#getTextAfterCursor(int, int) |
| + */ |
| + @Override |
| + public CharSequence getTextAfterCursor(int maxChars, int flags) { |
| + if (DEBUG_LOGS) Log.w(TAG, "getTextAfterCursor [%d %x]", maxChars, flags); |
| + assertOnImeThread(); |
| + TextInputState textInputState = requestTextInputState(); |
| + if (textInputState == null) return null; |
| + return textInputState.getTextAfterSelection(maxChars); |
| + } |
| + |
| + /** |
| + * @see InputConnection#getSelectedText(int) |
| + */ |
| + @Override |
| + public CharSequence getSelectedText(int flags) { |
| + if (DEBUG_LOGS) Log.w(TAG, "getSelectedText [%x]", flags); |
| + assertOnImeThread(); |
| + TextInputState textInputState = requestTextInputState(); |
| + if (textInputState == null) return null; |
| + return textInputState.getSelectedText(); |
| + } |
| + |
| + /** |
| + * @see InputConnection#getCursorCapsMode(int) |
| + */ |
| + @Override |
| + public int getCursorCapsMode(int reqModes) { |
| + if (DEBUG_LOGS) Log.w(TAG, "getCursorCapsMode [%x]", reqModes); |
| + assertOnImeThread(); |
| + // TODO(changwan): Auto-generated method stub |
|
Ted C
2016/02/17 19:09:34
remvoe these TODOs
Changwan Ryu
2016/02/18 06:03:26
Removed them and added a real TODO for getCursorCa
|
| + return 0; |
| + } |
| + |
| + /** |
| + * @see InputConnection#commitCompletion(android.view.inputmethod.CompletionInfo) |
| + */ |
| + @Override |
| + public boolean commitCompletion(CompletionInfo text) { |
| + if (DEBUG_LOGS) Log.w(TAG, "commitCompletion [%s]", text); |
| + assertOnImeThread(); |
| + // TODO(changwan): Auto-generated method stub |
| + return false; |
| + } |
| + |
| + /** |
| + * @see InputConnection#commitCorrection(android.view.inputmethod.CorrectionInfo) |
| + */ |
| + @Override |
| + public boolean commitCorrection(CorrectionInfo correctionInfo) { |
| + if (DEBUG_LOGS) { |
| + Log.w(TAG, "commitCorrection [%s]", ImeUtils.getCorrectInfoDebugString(correctionInfo)); |
| + } |
| + assertOnImeThread(); |
| + // TODO(changwan): Auto-generated method stub |
| + return false; |
| + } |
| + |
| + /** |
| + * @see InputConnection#clearMetaKeyStates(int) |
| + */ |
| + @Override |
| + public boolean clearMetaKeyStates(int states) { |
| + if (DEBUG_LOGS) Log.w(TAG, "clearMetaKeyStates [%x]", states); |
| + assertOnImeThread(); |
| + // TODO(changwan): Auto-generated method stub |
| + return false; |
| + } |
| + |
| + /** |
| + * @see InputConnection#reportFullscreenMode(boolean) |
| + */ |
| + @Override |
| + public boolean reportFullscreenMode(boolean enabled) { |
| + if (DEBUG_LOGS) Log.w(TAG, "reportFullscreenMode [%b]", enabled); |
| + // We ignore fullscreen mode for now. That's why we set |
| + // EditorInfo.IME_FLAG_NO_FULLSCREEN in constructor. |
| + // Note that this may be called on UI thread. |
| + return false; |
| + } |
| + |
| + /** |
| + * @see InputConnection#performPrivateCommand(java.lang.String, android.os.Bundle) |
| + */ |
| + @Override |
| + public boolean performPrivateCommand(String action, Bundle data) { |
| + if (DEBUG_LOGS) Log.w(TAG, "performPrivateCommand [%s]", action); |
| + assertOnImeThread(); |
| + // TODO(changwan): Auto-generated method stub |
| + return false; |
| + } |
| + |
| + /** |
| + * @see InputConnection#requestCursorUpdates(int) |
| + */ |
| + @Override |
| + public boolean requestCursorUpdates(int cursorUpdateMode) { |
| + if (DEBUG_LOGS) Log.w(TAG, "requestCursorUpdates [%x]", cursorUpdateMode); |
| + assertOnImeThread(); |
| + // TODO(changwan): Auto-generated method stub |
| + return false; |
| + } |
| +} |