From 612cce92ad96eda1146c3abd2afa7aaa4d4f2b3f Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Thu, 11 Feb 2016 17:47:33 -0800 Subject: [PATCH] Introduce InputConnection#getHandler(). Currently there is an internal hidden class named ControlledInputConnectionWrapper which works as a proxy in the application process to receive incoming binder calls from input method and dispatch those method calls again on an appropriate thread. Although this is a kind of implementation details, basically you can see the same design everywhere in the Android. Currently ControlledInputConnectionWrapper is initialized with view.getHandler(), where the view here is the View which was used to call View#onCreateInputConnection(). This is actually a reasonable behavior because we have generally assumed that there the only reasonable way to implement InputConnection is to extend BaseInputConnection, which is designed to be able to work only on the UI-thread associated with the target view. However, on Android N and onward, we are going to ensure that BaseInputConnection can be re-implemented on top of public APIs [1]. Although most of applications should not try to do that, for certain applications such as web browsers and WebView it may make sense to let custom InputConnection implementation run with a custom Handler so that the application can respond to the IME without blocking the UI thread. To do that, this CL introduces a new method InputConnection#getHandler(), which changes nothing as long as it returns null, but if it returns non-null Handler, InputMethodManager will use it to initialize ControlledInputConnectionWrapper. Note that InputConnection#getHandler() is not for IME developers. It just returns null when called in the IME process. [1] See Bug 24688781 for details. Bug: 26945674 Change-Id: Id9e579bb3e2966986cdcb1c34bc8cacfeca2e1a9 --- api/current.txt | 3 +++ api/system-current.txt | 3 +++ api/test-current.txt | 3 +++ .../view/inputmethod/BaseInputConnection.java | 5 +++++ .../android/view/inputmethod/InputConnection.java | 12 ++++++++++++ .../view/inputmethod/InputConnectionWrapper.java | 5 +++++ .../android/view/inputmethod/InputMethodManager.java | 8 +++++--- core/java/android/widget/AbsListView.java | 6 ++++++ .../internal/view/InputConnectionWrapper.java | 6 ++++++ 9 files changed, 48 insertions(+), 3 deletions(-) diff --git a/api/current.txt b/api/current.txt index 71568004d7606..a0ee6ec89d8cb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -44315,6 +44315,7 @@ package android.view.inputmethod { method public int getCursorCapsMode(int); method public android.text.Editable getEditable(); method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); + method public android.os.Handler getHandler(); method public java.lang.CharSequence getSelectedText(int); method public java.lang.CharSequence getTextAfterCursor(int, int); method public java.lang.CharSequence getTextBeforeCursor(int, int); @@ -44478,6 +44479,7 @@ package android.view.inputmethod { method public abstract boolean finishComposingText(); method public abstract int getCursorCapsMode(int); method public abstract android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); + method public abstract android.os.Handler getHandler(); method public abstract java.lang.CharSequence getSelectedText(int); method public abstract java.lang.CharSequence getTextAfterCursor(int, int); method public abstract java.lang.CharSequence getTextBeforeCursor(int, int); @@ -44509,6 +44511,7 @@ package android.view.inputmethod { method public boolean finishComposingText(); method public int getCursorCapsMode(int); method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); + method public android.os.Handler getHandler(); method public java.lang.CharSequence getSelectedText(int); method public java.lang.CharSequence getTextAfterCursor(int, int); method public java.lang.CharSequence getTextBeforeCursor(int, int); diff --git a/api/system-current.txt b/api/system-current.txt index 1c8967281cb0e..15c9d82587e54 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -47070,6 +47070,7 @@ package android.view.inputmethod { method public int getCursorCapsMode(int); method public android.text.Editable getEditable(); method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); + method public android.os.Handler getHandler(); method public java.lang.CharSequence getSelectedText(int); method public java.lang.CharSequence getTextAfterCursor(int, int); method public java.lang.CharSequence getTextBeforeCursor(int, int); @@ -47233,6 +47234,7 @@ package android.view.inputmethod { method public abstract boolean finishComposingText(); method public abstract int getCursorCapsMode(int); method public abstract android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); + method public abstract android.os.Handler getHandler(); method public abstract java.lang.CharSequence getSelectedText(int); method public abstract java.lang.CharSequence getTextAfterCursor(int, int); method public abstract java.lang.CharSequence getTextBeforeCursor(int, int); @@ -47264,6 +47266,7 @@ package android.view.inputmethod { method public boolean finishComposingText(); method public int getCursorCapsMode(int); method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); + method public android.os.Handler getHandler(); method public java.lang.CharSequence getSelectedText(int); method public java.lang.CharSequence getTextAfterCursor(int, int); method public java.lang.CharSequence getTextBeforeCursor(int, int); diff --git a/api/test-current.txt b/api/test-current.txt index 929b7cb72f864..bf3cf93a45751 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -44332,6 +44332,7 @@ package android.view.inputmethod { method public int getCursorCapsMode(int); method public android.text.Editable getEditable(); method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); + method public android.os.Handler getHandler(); method public java.lang.CharSequence getSelectedText(int); method public java.lang.CharSequence getTextAfterCursor(int, int); method public java.lang.CharSequence getTextBeforeCursor(int, int); @@ -44495,6 +44496,7 @@ package android.view.inputmethod { method public abstract boolean finishComposingText(); method public abstract int getCursorCapsMode(int); method public abstract android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); + method public abstract android.os.Handler getHandler(); method public abstract java.lang.CharSequence getSelectedText(int); method public abstract java.lang.CharSequence getTextAfterCursor(int, int); method public abstract java.lang.CharSequence getTextBeforeCursor(int, int); @@ -44526,6 +44528,7 @@ package android.view.inputmethod { method public boolean finishComposingText(); method public int getCursorCapsMode(int); method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); + method public android.os.Handler getHandler(); method public java.lang.CharSequence getSelectedText(int); method public java.lang.CharSequence getTextAfterCursor(int, int); method public java.lang.CharSequence getTextBeforeCursor(int, int); diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index a10f792b006bf..6a830f87de1f1 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -19,6 +19,7 @@ package android.view.inputmethod; import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; +import android.os.Handler; import android.os.SystemClock; import android.text.Editable; import android.text.NoCopySpan; @@ -602,6 +603,10 @@ public class BaseInputConnection implements InputConnection { return false; } + public Handler getHandler() { + return null; + } + /** * The default implementation places the given text into the editable, * replacing any existing composing text. The new text is marked as diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index eb773e23fa623..2a9706dc87d54 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -17,6 +17,7 @@ package android.view.inputmethod; import android.os.Bundle; +import android.os.Handler; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -786,4 +787,15 @@ public interface InputConnection { * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}. */ public boolean requestCursorUpdates(int cursorUpdateMode); + + /** + * Called by the {@link InputMethodManager} to enable application developers to specify a + * dedicated {@link Handler} on which incoming IPC method calls from input methods will be + * dispatched. + * + *

Note: This does nothing when called from input methods.

+ * + * @return {@code null} to use the default {@link Handler}. + */ + public Handler getHandler(); } diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index e5ae42299dbac..65c7654c671b4 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -17,6 +17,7 @@ package android.view.inputmethod; import android.os.Bundle; +import android.os.Handler; import android.view.KeyEvent; /** @@ -133,4 +134,8 @@ public class InputConnectionWrapper implements InputConnection { public boolean requestCursorUpdates(int cursorUpdateMode) { return mTarget.requestCursorUpdates(cursorUpdateMode); } + + public Handler getHandler() { + return mTarget.getHandler(); + } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 0ed22994585b9..2de9897904d60 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1215,7 +1215,7 @@ public final class InputMethodManager { if (mCurrentTextBoxAttribute == null) { controlFlags |= CONTROL_START_INITIAL; } - + // Hook 'em up and let 'er rip. mCurrentTextBoxAttribute = tba; mServedConnecting = false; @@ -1230,7 +1230,9 @@ public final class InputMethodManager { mCursorCandEnd = -1; mCursorRect.setEmpty(); mCursorAnchorInfo = null; - servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this); + final Handler icHandler = ic.getHandler(); + servedContext = new ControlledInputConnectionWrapper( + icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this); } else { servedContext = null; } @@ -1238,7 +1240,7 @@ public final class InputMethodManager { mServedInputConnectionWrapper.deactivate(); } mServedInputConnectionWrapper = servedContext; - + try { if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic=" + ic + " tba=" + tba + " controlFlags=#" diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index b689564cfe589..496f7eee27f79 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -28,6 +28,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.os.Bundle; import android.os.Debug; +import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; import android.os.StrictMode; @@ -5930,6 +5931,11 @@ public abstract class AbsListView extends AdapterView implements Te public boolean requestCursorUpdates(int cursorUpdateMode) { return getTarget().requestCursorUpdates(cursorUpdateMode); } + + @Override + public Handler getHandler() { + return getTarget().getHandler(); + } } /** diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 94790c1585597..fc672454a0a08 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -17,6 +17,7 @@ package com.android.internal.view; import android.os.Bundle; +import android.os.Handler; import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; @@ -454,4 +455,9 @@ public class InputConnectionWrapper implements InputConnection { } return result; } + + public Handler getHandler() { + // Nothing should happen when called from input method. + return null; + } }