summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java27
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java36
-rw-r--r--core/java/android/view/inputmethod/InputConnectionInspector.java224
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java12
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java16
-rw-r--r--core/java/com/android/internal/view/IInputMethod.aidl4
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl3
-rw-r--r--core/java/com/android/internal/view/InputConnectionWrapper.java44
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java93
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java3
10 files changed, 403 insertions, 59 deletions
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 857e3357e133..cc71a9c24bfb 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -36,6 +36,7 @@ import android.view.InputChannel;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
import android.view.inputmethod.InputMethodSubtype;
@@ -164,9 +165,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
return;
case DO_START_INPUT: {
SomeArgs args = (SomeArgs)msg.obj;
+ int missingMethods = msg.arg1;
IInputContext inputContext = (IInputContext)args.arg1;
InputConnection ic = inputContext != null
- ? new InputConnectionWrapper(inputContext) : null;
+ ? new InputConnectionWrapper(inputContext, missingMethods) : null;
EditorInfo info = (EditorInfo)args.arg2;
info.makeCompatible(mTargetSdkVersion);
inputMethod.startInput(ic, info);
@@ -175,9 +177,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
case DO_RESTART_INPUT: {
SomeArgs args = (SomeArgs)msg.obj;
+ int missingMethods = msg.arg1;
IInputContext inputContext = (IInputContext)args.arg1;
InputConnection ic = inputContext != null
- ? new InputConnectionWrapper(inputContext) : null;
+ ? new InputConnectionWrapper(inputContext, missingMethods) : null;
EditorInfo info = (EditorInfo)args.arg2;
info.makeCompatible(mTargetSdkVersion);
inputMethod.restartInput(ic, info);
@@ -246,8 +249,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
@Override
public void bindInput(InputBinding binding) {
+ // This IInputContext is guaranteed to implement all the methods.
+ final int missingMethodFlags = 0;
InputConnection ic = new InputConnectionWrapper(
- IInputContext.Stub.asInterface(binding.getConnectionToken()));
+ IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags);
InputBinding nu = new InputBinding(ic, binding);
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
}
@@ -258,15 +263,19 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
@Override
- public void startInput(IInputContext inputContext, EditorInfo attribute) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT,
- inputContext, attribute));
+ public void startInput(IInputContext inputContext,
+ @InputConnectionInspector.MissingMethodFlags final int missingMethods,
+ EditorInfo attribute) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_START_INPUT,
+ missingMethods, inputContext, attribute));
}
@Override
- public void restartInput(IInputContext inputContext, EditorInfo attribute) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT,
- inputContext, attribute));
+ public void restartInput(IInputContext inputContext,
+ @InputConnectionInspector.MissingMethodFlags final int missingMethods,
+ EditorInfo attribute) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_RESTART_INPUT,
+ missingMethods, inputContext, attribute));
}
@Override
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 2a9706dc87d5..8002a8e9ba14 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -28,10 +28,24 @@ import android.view.KeyEvent;
* cursor, committing text to the text box, and sending raw key events
* to the application.
*
- * <p>Applications should never directly implement this interface, but
- * instead subclass from {@link BaseInputConnection}. This will ensure
- * that the application does not break when new methods are added to
- * the interface.</p>
+ * <p>Starting from API Level {@link android.os.Build.VERSION_CODES#N},
+ * the system can deal with the situation where the application directly
+ * implements this class but one or more of the following methods are
+ * not implemented.</p>
+ * <ul>
+ * <li>{@link #getSelectedText(int)}, which was introduced in
+ * {@link android.os.Build.VERSION_CODES#GINGERBREAD}.</li>
+ * <li>{@link #setComposingRegion(int, int)}, which was introduced
+ * in {@link android.os.Build.VERSION_CODES#GINGERBREAD}.</li>
+ * <li>{@link #commitCorrection(CorrectionInfo)}, which was introduced
+ * in {@link android.os.Build.VERSION_CODES#HONEYCOMB}.</li>
+ * <li>{@link #requestCursorUpdates(int)}, which was introduced in
+ * {@link android.os.Build.VERSION_CODES#LOLLIPOP}.</li>
+ * <li>{@link #deleteSurroundingTextInCodePoints(int, int)}}, which
+ * was introduced in {@link android.os.Build.VERSION_CODES#N}.</li>
+ * <li>{@link #getHandler()}}, which was introduced in
+ * {@link android.os.Build.VERSION_CODES#N}.</li>
+ * </ul>
*
* <h3>Implementing an IME or an editor</h3>
* <p>Text input is the result of the synergy of two essential components:
@@ -224,7 +238,9 @@ public interface InputConnection {
* @param flags Supplies additional options controlling how the text is
* returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
* @return the text that is currently selected, if any, or null if
- * no text is selected.
+ * no text is selected. In {@link android.os.Build.VERSION_CODES#N} and
+ * later, returns false when the target application does not implement
+ * this method.
*/
public CharSequence getSelectedText(int flags);
@@ -371,7 +387,8 @@ public interface InputConnection {
* If this is greater than the number of existing characters between the cursor and
* the end of the text, then this method does not fail but deletes all the characters in
* that range.
- * @return true on success, false if the input connection is no longer valid.
+ * @return true on success, false if the input connection is no longer valid. Returns
+ * {@code false} when the target application does not implement this method.
*/
public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
@@ -461,7 +478,8 @@ public interface InputConnection {
* @param start the position in the text at which the composing region begins
* @param end the position in the text at which the composing region ends
* @return true on success, false if the input connection is no longer
- * valid.
+ * valid. In {@link android.os.Build.VERSION_CODES#N} and later, false is returned when the
+ * target application does not implement this method.
*/
public boolean setComposingRegion(int start, int end);
@@ -573,6 +591,8 @@ public interface InputConnection {
*
* @param correctionInfo Detailed information about the correction.
* @return true on success, false if the input connection is no longer valid.
+ * In {@link android.os.Build.VERSION_CODES#N} and later, returns false
+ * when the target application does not implement this method.
*/
public boolean commitCorrection(CorrectionInfo correctionInfo);
@@ -785,6 +805,8 @@ public interface InputConnection {
* @return {@code true} if the request is scheduled. {@code false} to indicate that when the
* application will not call
* {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}.
+ * In {@link android.os.Build.VERSION_CODES#N} and later, returns {@code false} also when the
+ * target application does not implement this method.
*/
public boolean requestCursorUpdates(int cursorUpdateMode);
diff --git a/core/java/android/view/inputmethod/InputConnectionInspector.java b/core/java/android/view/inputmethod/InputConnectionInspector.java
new file mode 100644
index 000000000000..46b2c3e0fbea
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputConnectionInspector.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * @hide
+ */
+public final class InputConnectionInspector {
+
+ @Retention(SOURCE)
+ @IntDef({MissingMethodFlags.GET_SELECTED_TEXT,
+ MissingMethodFlags.SET_COMPOSING_REGION,
+ MissingMethodFlags.COMMIT_CORRECTION,
+ MissingMethodFlags.REQUEST_CURSOR_UPDATES,
+ MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
+ MissingMethodFlags.GET_HANDLER,
+ })
+ public @interface MissingMethodFlags {
+ /**
+ * {@link InputConnection#getSelectedText(int)} is available in
+ * {@link android.os.Build.VERSION_CODES#GINGERBREAD} and later.
+ */
+ int GET_SELECTED_TEXT = 1 << 0;
+ /**
+ * {@link InputConnection#setComposingRegion(int, int)} is available in
+ * {@link android.os.Build.VERSION_CODES#GINGERBREAD} and later.
+ */
+ int SET_COMPOSING_REGION = 1 << 1;
+ /**
+ * {@link InputConnection#commitCorrection(CorrectionInfo)} is available in
+ * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and later.
+ */
+ int COMMIT_CORRECTION = 1 << 2;
+ /**
+ * {@link InputConnection#requestCursorUpdates(int)} is available in
+ * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
+ */
+ int REQUEST_CURSOR_UPDATES = 1 << 3;
+ /**
+ * {@link InputConnection#deleteSurroundingTextInCodePoints(int, int)}} is available in
+ * {@link android.os.Build.VERSION_CODES#N} and later.
+ */
+ int DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 1 << 4;
+ /**
+ * {@link InputConnection#deleteSurroundingTextInCodePoints(int, int)}} is available in
+ * {@link android.os.Build.VERSION_CODES#N} and later.
+ */
+ int GET_HANDLER = 1 << 5;
+ }
+
+ private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
+ new WeakHashMap<>());
+
+ @MissingMethodFlags
+ public static int getMissingMethodFlags(@Nullable final InputConnection ic) {
+ if (ic == null) {
+ return 0;
+ }
+ // Optimization for a known class.
+ if (ic instanceof BaseInputConnection) {
+ return 0;
+ }
+ // Optimization for a known class.
+ if (ic instanceof InputConnectionWrapper) {
+ return ((InputConnectionWrapper) ic).getMissingMethodFlags();
+ }
+ return getMissingMethodFlagsInternal(ic.getClass());
+ }
+
+ @MissingMethodFlags
+ public static int getMissingMethodFlagsInternal(@NonNull final Class clazz) {
+ final Integer cachedFlags = sMissingMethodsMap.get(clazz);
+ if (cachedFlags != null) {
+ return cachedFlags;
+ }
+ int flags = 0;
+ if (!hasGetSelectedText(clazz)) {
+ flags |= MissingMethodFlags.GET_SELECTED_TEXT;
+ }
+ if (!hasSetComposingRegion(clazz)) {
+ flags |= MissingMethodFlags.SET_COMPOSING_REGION;
+ }
+ if (!hasCommitCorrection(clazz)) {
+ flags |= MissingMethodFlags.COMMIT_CORRECTION;
+ }
+ if (!hasRequestCursorUpdate(clazz)) {
+ flags |= MissingMethodFlags.REQUEST_CURSOR_UPDATES;
+ }
+ if (!hasDeleteSurroundingTextInCodePoints(clazz)) {
+ flags |= MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS;
+ }
+ if (!hasGetHandler(clazz)) {
+ flags |= MissingMethodFlags.GET_HANDLER;
+ }
+ sMissingMethodsMap.put(clazz, flags);
+ return flags;
+ }
+
+ private static boolean hasGetSelectedText(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("getSelectedText", int.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasSetComposingRegion(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("setComposingRegion", int.class, int.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasCommitCorrection(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("commitCorrection", CorrectionInfo.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasRequestCursorUpdate(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("requestCursorUpdates", int.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasDeleteSurroundingTextInCodePoints(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("deleteSurroundingTextInCodePoints", int.class,
+ int.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasGetHandler(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("getHandler");
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
+ final StringBuilder sb = new StringBuilder();
+ boolean isEmpty = true;
+ if ((flags & MissingMethodFlags.GET_SELECTED_TEXT) != 0) {
+ sb.append("getSelectedText(int)");
+ isEmpty = false;
+ }
+ if ((flags & MissingMethodFlags.SET_COMPOSING_REGION) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("setComposingRegion(int, int)");
+ isEmpty = false;
+ }
+ if ((flags & MissingMethodFlags.COMMIT_CORRECTION) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("commitCorrection(CorrectionInfo)");
+ isEmpty = false;
+ }
+ if ((flags & MissingMethodFlags.REQUEST_CURSOR_UPDATES) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("requestCursorUpdate(int)");
+ isEmpty = false;
+ }
+ if ((flags & MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("deleteSurroundingTextInCodePoints(int, int)");
+ isEmpty = false;
+ }
+ if ((flags & MissingMethodFlags.GET_HANDLER) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("getHandler()");
+ }
+ return sb.toString();
+ }
+}
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 42d1442a35bb..381df49d8a69 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -26,6 +26,8 @@ import android.view.KeyEvent;
public class InputConnectionWrapper implements InputConnection {
private InputConnection mTarget;
final boolean mMutable;
+ @InputConnectionInspector.MissingMethodFlags
+ private int mMissingMethodFlags;
/**
* Initializes a wrapper.
@@ -40,6 +42,7 @@ public class InputConnectionWrapper implements InputConnection {
public InputConnectionWrapper(InputConnection target, boolean mutable) {
mMutable = mutable;
mTarget = target;
+ mMissingMethodFlags = InputConnectionInspector.getMissingMethodFlags(target);
}
/**
@@ -56,6 +59,15 @@ public class InputConnectionWrapper implements InputConnection {
throw new SecurityException("not mutable");
}
mTarget = target;
+ mMissingMethodFlags = InputConnectionInspector.getMissingMethodFlags(target);
+ }
+
+ /**
+ * @hide
+ */
+ @InputConnectionInspector.MissingMethodFlags
+ public int getMissingMethodFlags() {
+ return mMissingMethodFlags;
}
/**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 859df432eb8c..97cafb713186 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1224,6 +1224,7 @@ public final class InputMethodManager {
notifyInputConnectionFinished();
mServedInputConnection = ic;
ControlledInputConnectionWrapper servedContext;
+ final int missingMethodFlags;
if (ic != null) {
mCursorSelStart = tba.initialSelStart;
mCursorSelEnd = tba.initialSelEnd;
@@ -1231,11 +1232,20 @@ public final class InputMethodManager {
mCursorCandEnd = -1;
mCursorRect.setEmpty();
mCursorAnchorInfo = null;
- final Handler icHandler = ic.getHandler();
+ final Handler icHandler;
+ missingMethodFlags = InputConnectionInspector.getMissingMethodFlags(ic);
+ if ((missingMethodFlags & InputConnectionInspector.MissingMethodFlags.GET_HANDLER)
+ != 0) {
+ // InputConnection#getHandler() is not implemented.
+ icHandler = null;
+ } else {
+ icHandler = ic.getHandler();
+ }
servedContext = new ControlledInputConnectionWrapper(
icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this);
} else {
servedContext = null;
+ missingMethodFlags = 0;
}
if (mServedInputConnectionWrapper != null) {
mServedInputConnectionWrapper.deactivate();
@@ -1248,7 +1258,7 @@ public final class InputMethodManager {
+ Integer.toHexString(controlFlags));
final InputBindResult res = mService.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, controlFlags, softInputMode,
- windowFlags, tba, servedContext);
+ windowFlags, tba, servedContext, missingMethodFlags);
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
if (res != null) {
if (res.id != null) {
@@ -1476,7 +1486,7 @@ public final class InputMethodManager {
mService.startInputOrWindowGainedFocus(
InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
rootView.getWindowToken(), controlFlags, softInputMode, windowFlags, null,
- null);
+ null, 0 /* missingMethodFlags */);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 77456da248ea..6ab1ec797a22 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -38,9 +38,9 @@ oneway interface IInputMethod {
void unbindInput();
- void startInput(in IInputContext inputContext, in EditorInfo attribute);
+ void startInput(in IInputContext inputContext, int missingMethods, in EditorInfo attribute);
- void restartInput(in IInputContext inputContext, in EditorInfo attribute);
+ void restartInput(in IInputContext inputContext, int missingMethods, in EditorInfo attribute);
void createSession(in InputChannel channel, IInputSessionCallback callback);
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5576f13ba488..94c94c190e6b 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -57,7 +57,8 @@ interface IInputMethodManager {
/* @InputMethodClient.StartInputReason */ int startInputReason,
in IInputMethodClient client, in IBinder windowToken, int controlFlags,
int softInputMode, int windowFlags, in EditorInfo attribute,
- IInputContext inputContext);
+ IInputContext inputContext,
+ /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags);
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index fc672454a0a0..85b8606f7500 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -27,11 +27,15 @@ import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionInspector;
+import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
public class InputConnectionWrapper implements InputConnection {
private static final int MAX_WAIT_TIME_MILLIS = 2000;
private final IInputContext mIInputContext;
-
+ @MissingMethodFlags
+ private final int mMissingMethods;
+
static class InputContextCallback extends IInputContextCallback.Stub {
private static final String TAG = "InputConnectionWrapper.ICC";
public int mSeq;
@@ -191,8 +195,10 @@ public class InputConnectionWrapper implements InputConnection {
}
}
- public InputConnectionWrapper(IInputContext inputContext) {
+ public InputConnectionWrapper(IInputContext inputContext,
+ @MissingMethodFlags final int missingMethods) {
mIInputContext = inputContext;
+ mMissingMethods = missingMethods;
}
public CharSequence getTextAfterCursor(int length, int flags) {
@@ -230,8 +236,12 @@ public class InputConnectionWrapper implements InputConnection {
}
return value;
}
-
+
public CharSequence getSelectedText(int flags) {
+ if (isMethodMissing(MissingMethodFlags.GET_SELECTED_TEXT)) {
+ // This method is not implemented.
+ return null;
+ }
CharSequence value = null;
try {
InputContextCallback callback = InputContextCallback.getInstance();
@@ -295,6 +305,10 @@ public class InputConnectionWrapper implements InputConnection {
}
public boolean commitCompletion(CompletionInfo text) {
+ if (isMethodMissing(MissingMethodFlags.COMMIT_CORRECTION)) {
+ // This method is not implemented.
+ return false;
+ }
try {
mIInputContext.commitCompletion(text);
return true;
@@ -340,6 +354,10 @@ public class InputConnectionWrapper implements InputConnection {
}
public boolean setComposingRegion(int start, int end) {
+ if (isMethodMissing(MissingMethodFlags.SET_COMPOSING_REGION)) {
+ // This method is not implemented.
+ return false;
+ }
try {
mIInputContext.setComposingRegion(start, end);
return true;
@@ -412,6 +430,10 @@ public class InputConnectionWrapper implements InputConnection {
}
public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+ if (isMethodMissing(MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS)) {
+ // This method is not implemented.
+ return false;
+ }
try {
mIInputContext.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
return true;
@@ -440,6 +462,10 @@ public class InputConnectionWrapper implements InputConnection {
public boolean requestCursorUpdates(int cursorUpdateMode) {
boolean result = false;
+ if (isMethodMissing(MissingMethodFlags.REQUEST_CURSOR_UPDATES)) {
+ // This method is not implemented.
+ return false;
+ }
try {
InputContextCallback callback = InputContextCallback.getInstance();
mIInputContext.requestUpdateCursorAnchorInfo(cursorUpdateMode, callback.mSeq, callback);
@@ -460,4 +486,16 @@ public class InputConnectionWrapper implements InputConnection {
// Nothing should happen when called from input method.
return null;
}
+
+ private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
+ return (mMissingMethods & methodFlag) == methodFlag;
+ }
+
+ @Override
+ public String toString() {
+ return "InputConnectionWrapper{idHash=#"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " mMissingMethods="
+ + InputConnectionInspector.getMissingMethodFlagsAsString(mMissingMethods) + "}";
+ }
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 00a49bdb7273..c1b341e7dc67 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -112,6 +112,7 @@ import android.view.WindowManager;
import android.view.WindowManagerInternal;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
+import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
@@ -327,6 +328,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
IInputContext mCurInputContext;
/**
+ * The missing method flags for the input context last provided by the current client.
+ *
+ * @see android.view.inputmethod.InputConnectionInspector.MissingMethodFlags
+ */
+ int mCurInputContextMissingMethods;
+
+ /**
* The attributes last provided by the current client.
*/
EditorInfo mCurAttribute;
@@ -1289,11 +1297,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
final SessionState session = mCurClient.curSession;
if (initial) {
- executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
- MSG_START_INPUT, session, mCurInputContext, mCurAttribute));
+ executeOrSendMessage(session.method, mCaller.obtainMessageIOOO(
+ MSG_START_INPUT, mCurInputContextMissingMethods, session, mCurInputContext,
+ mCurAttribute));
} else {
- executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
- MSG_RESTART_INPUT, session, mCurInputContext, mCurAttribute));
+ executeOrSendMessage(session.method, mCaller.obtainMessageIOOO(
+ MSG_RESTART_INPUT, mCurInputContextMissingMethods, session, mCurInputContext,
+ mCurAttribute));
}
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
@@ -1306,7 +1316,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
InputBindResult startInputLocked(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
- IInputMethodClient client, IInputContext inputContext, EditorInfo attribute,
+ IInputMethodClient client, IInputContext inputContext,
+ /* @InputConnectionInspector.missingMethods */ final int missingMethods,
+ EditorInfo attribute,
int controlFlags) {
// If no method is currently selected, do nothing.
if (mCurMethodId == null) {
@@ -1333,10 +1345,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} catch (RemoteException e) {
}
- return startInputUncheckedLocked(cs, inputContext, attribute, controlFlags);
+ return startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
+ controlFlags);
}
InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
+ /* @InputConnectionInspector.missingMethods */ final int missingMethods,
@NonNull EditorInfo attribute, int controlFlags) {
// If no method is currently selected, do nothing.
if (mCurMethodId == null) {
@@ -1371,6 +1385,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (mCurSeq <= 0) mCurSeq = 1;
mCurClient = cs;
mCurInputContext = inputContext;
+ mCurInputContextMissingMethods = missingMethods;
mCurAttribute = attribute;
// Check if the input method is changing.
@@ -1459,8 +1474,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private InputBindResult startInput(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
- IInputMethodClient client, IInputContext inputContext, EditorInfo attribute,
- int controlFlags) {
+ IInputMethodClient client, IInputContext inputContext,
+ /* @InputConnectionInspector.missingMethods */ final int missingMethods,
+ EditorInfo attribute, int controlFlags) {
if (!calledFromValidUser()) {
return null;
}
@@ -1470,13 +1486,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
+ InputMethodClient.getStartInputReason(startInputReason)
+ " client = " + client.asBinder()
+ " inputContext=" + inputContext
+ + " missingMethods="
+ + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
+ " attribute=" + attribute
+ " controlFlags=#" + Integer.toHexString(controlFlags));
}
final long ident = Binder.clearCallingIdentity();
try {
- return startInputLocked(startInputReason, client, inputContext, attribute,
- controlFlags);
+ return startInputLocked(startInputReason, client, inputContext, missingMethods,
+ attribute, controlFlags);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2190,19 +2208,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public InputBindResult startInputOrWindowGainedFocus(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode,
- int windowFlags, EditorInfo attribute, IInputContext inputContext) {
+ int windowFlags, EditorInfo attribute, IInputContext inputContext,
+ /* @InputConnectionInspector.missingMethods */ final int missingMethods) {
if (windowToken != null) {
return windowGainedFocus(startInputReason, client, windowToken, controlFlags,
- softInputMode, windowFlags, attribute, inputContext);
+ softInputMode, windowFlags, attribute, inputContext, missingMethods);
} else {
- return startInput(startInputReason, client, inputContext, attribute, controlFlags);
+ return startInput(startInputReason, client, inputContext, missingMethods, attribute,
+ controlFlags);
}
}
private InputBindResult windowGainedFocus(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode,
- int windowFlags, EditorInfo attribute, IInputContext inputContext) {
+ int windowFlags, EditorInfo attribute, IInputContext inputContext,
+ /* @InputConnectionInspector.missingMethods */ final int missingMethods) {
// Needs to check the validity before clearing calling identity
final boolean calledFromValidUser = calledFromValidUser();
InputBindResult res = null;
@@ -2213,6 +2234,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
+ InputMethodClient.getStartInputReason(startInputReason)
+ " client=" + client.asBinder()
+ " inputContext=" + inputContext
+ + " missingMethods="
+ + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
+ " attribute=" + attribute
+ " controlFlags=#" + Integer.toHexString(controlFlags)
+ " softInputMode=#" + Integer.toHexString(softInputMode)
@@ -2250,8 +2273,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
+ " attribute=" + attribute + ", token = " + windowToken);
if (attribute != null) {
- return startInputUncheckedLocked(cs, inputContext, attribute,
- controlFlags);
+ return startInputUncheckedLocked(cs, inputContext, missingMethods,
+ attribute, controlFlags);
}
return null;
}
@@ -2300,8 +2323,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// is more room for the target window + IME.
if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, attribute,
- controlFlags);
+ res = startInputUncheckedLocked(cs, inputContext,
+ missingMethods, attribute, controlFlags);
didStart = true;
}
showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
@@ -2326,8 +2349,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
if (DEBUG) Slog.v(TAG, "Window asks to show input going forward");
if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, attribute,
- controlFlags);
+ res = startInputUncheckedLocked(cs, inputContext,
+ missingMethods, attribute, controlFlags);
didStart = true;
}
showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
@@ -2336,8 +2359,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
if (DEBUG) Slog.v(TAG, "Window asks to always show input");
if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, attribute,
- controlFlags);
+ res = startInputUncheckedLocked(cs, inputContext, missingMethods,
+ attribute, controlFlags);
didStart = true;
}
showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
@@ -2345,7 +2368,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
if (!didStart && attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, attribute,
+ res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
controlFlags);
}
}
@@ -2807,28 +2830,32 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
// ---------------------------------------------------------
- case MSG_START_INPUT:
- args = (SomeArgs)msg.obj;
+ case MSG_START_INPUT: {
+ int missingMethods = msg.arg1;
+ args = (SomeArgs) msg.obj;
try {
- SessionState session = (SessionState)args.arg1;
+ SessionState session = (SessionState) args.arg1;
setEnabledSessionInMainThread(session);
- session.method.startInput((IInputContext)args.arg2,
- (EditorInfo)args.arg3);
+ session.method.startInput((IInputContext) args.arg2, missingMethods,
+ (EditorInfo) args.arg3);
} catch (RemoteException e) {
}
args.recycle();
return true;
- case MSG_RESTART_INPUT:
- args = (SomeArgs)msg.obj;
+ }
+ case MSG_RESTART_INPUT: {
+ int missingMethods = msg.arg1;
+ args = (SomeArgs) msg.obj;
try {
- SessionState session = (SessionState)args.arg1;
+ SessionState session = (SessionState) args.arg1;
setEnabledSessionInMainThread(session);
- session.method.restartInput((IInputContext)args.arg2,
- (EditorInfo)args.arg3);
+ session.method.restartInput((IInputContext) args.arg2, missingMethods,
+ (EditorInfo) args.arg3);
} catch (RemoteException e) {
}
args.recycle();
return true;
+ }
// ---------------------------------------------------------
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
index 5c74caf2695d..0cf51a46f63f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
@@ -222,7 +222,8 @@ public class BridgeIInputMethodManager implements IInputMethodManager {
public InputBindResult startInputOrWindowGainedFocus(
/* @InputMethodClient.StartInputReason */ int startInputReason,
IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode,
- int windowFlags, EditorInfo attribute, IInputContext inputContext)
+ int windowFlags, EditorInfo attribute, IInputContext inputContext,
+ /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags)
throws RemoteException {
// TODO Auto-generated method stub
return null;