diff options
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; |