diff options
Diffstat (limited to 'core/java/android')
8 files changed, 393 insertions, 232 deletions
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java index 396f910..51867bc 100644 --- a/core/java/android/app/NativeActivity.java +++ b/core/java/android/app/NativeActivity.java @@ -353,8 +353,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, } void preDispatchKeyEvent(KeyEvent event, int seq) { - mIMM.dispatchKeyEvent(this, seq, event, - mInputMethodCallback); + mIMM.dispatchInputEvent(this, seq, event, mInputMethodCallback); } void setWindowFlags(int flags, int mask) { diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 3c3182a..3531926 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -126,11 +126,12 @@ public abstract class AbstractInputMethodService extends Service mRevoked = true; mEnabled = false; } - + /** * Take care of dispatching incoming key events to the appropriate * callbacks on the service, and tell the client when this is done. */ + @Override public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) { boolean handled = event.dispatch(AbstractInputMethodService.this, mDispatcherState, this); @@ -143,6 +144,7 @@ public abstract class AbstractInputMethodService extends Service * Take care of dispatching incoming trackball events to the appropriate * callbacks on the service, and tell the client when this is done. */ + @Override public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback) { boolean handled = onTrackballEvent(event); if (callback != null) { @@ -154,6 +156,7 @@ public abstract class AbstractInputMethodService extends Service * Take care of dispatching incoming generic motion events to the appropriate * callbacks on the service, and tell the client when this is done. */ + @Override public void dispatchGenericMotionEvent(int seq, MotionEvent event, EventCallback callback) { boolean handled = onGenericMotionEvent(event); if (callback != null) { diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index d78262b..726dcec 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -18,15 +18,20 @@ package android.inputmethodservice; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; -import com.android.internal.view.IInputMethodCallback; import com.android.internal.view.IInputMethodSession; import android.content.Context; import android.graphics.Rect; import android.os.Bundle; +import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.Log; +import android.util.SparseArray; +import android.view.InputChannel; +import android.view.InputDevice; +import android.view.InputEvent; +import android.view.InputEventReceiver; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.inputmethod.CompletionInfo; @@ -36,14 +41,10 @@ import android.view.inputmethod.InputMethodSession; class IInputMethodSessionWrapper extends IInputMethodSession.Stub implements HandlerCaller.Callback { private static final String TAG = "InputMethodWrapper"; - private static final boolean DEBUG = false; private static final int DO_FINISH_INPUT = 60; private static final int DO_DISPLAY_COMPLETIONS = 65; private static final int DO_UPDATE_EXTRACTED_TEXT = 67; - private static final int DO_DISPATCH_KEY_EVENT = 70; - private static final int DO_DISPATCH_TRACKBALL_EVENT = 80; - private static final int DO_DISPATCH_GENERIC_MOTION_EVENT = 85; private static final int DO_UPDATE_SELECTION = 90; private static final int DO_UPDATE_CURSOR = 95; private static final int DO_APP_PRIVATE_COMMAND = 100; @@ -53,34 +54,30 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub HandlerCaller mCaller; InputMethodSession mInputMethodSession; - - // NOTE: we should have a cache of these. - static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback { - final IInputMethodCallback mCb; - InputMethodEventCallbackWrapper(IInputMethodCallback cb) { - mCb = cb; - } - public void finishedEvent(int seq, boolean handled) { - try { - mCb.finishedEvent(seq, handled); - } catch (RemoteException e) { - } - } - } - + InputChannel mChannel; + ImeInputEventReceiver mReceiver; + public IInputMethodSessionWrapper(Context context, - InputMethodSession inputMethodSession) { + InputMethodSession inputMethodSession, InputChannel channel) { mCaller = new HandlerCaller(context, null, this, true /*asyncHandler*/); mInputMethodSession = inputMethodSession; + mChannel = channel; + if (channel != null) { + mReceiver = new ImeInputEventReceiver(channel, context.getMainLooper()); + } } public InputMethodSession getInternalInputMethodSession() { return mInputMethodSession; } + @Override public void executeMessage(Message msg) { - if (mInputMethodSession == null) return; + if (mInputMethodSession == null) { + // The session has been finished. + return; + } switch (msg.what) { case DO_FINISH_INPUT: @@ -93,33 +90,6 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mInputMethodSession.updateExtractedText(msg.arg1, (ExtractedText)msg.obj); return; - case DO_DISPATCH_KEY_EVENT: { - SomeArgs args = (SomeArgs)msg.obj; - mInputMethodSession.dispatchKeyEvent(msg.arg1, - (KeyEvent)args.arg1, - new InputMethodEventCallbackWrapper( - (IInputMethodCallback)args.arg2)); - args.recycle(); - return; - } - case DO_DISPATCH_TRACKBALL_EVENT: { - SomeArgs args = (SomeArgs)msg.obj; - mInputMethodSession.dispatchTrackballEvent(msg.arg1, - (MotionEvent)args.arg1, - new InputMethodEventCallbackWrapper( - (IInputMethodCallback)args.arg2)); - args.recycle(); - return; - } - case DO_DISPATCH_GENERIC_MOTION_EVENT: { - SomeArgs args = (SomeArgs)msg.obj; - mInputMethodSession.dispatchGenericMotionEvent(msg.arg1, - (MotionEvent)args.arg1, - new InputMethodEventCallbackWrapper( - (IInputMethodCallback)args.arg2)); - args.recycle(); - return; - } case DO_UPDATE_SELECTION: { SomeArgs args = (SomeArgs)msg.obj; mInputMethodSession.updateSelection(args.argi1, args.argi2, @@ -143,7 +113,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub return; } case DO_FINISH_SESSION: { - mInputMethodSession = null; + doFinishSession(); return; } case DO_VIEW_CLICKED: { @@ -153,37 +123,37 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub } Log.w(TAG, "Unhandled message code: " + msg.what); } - + + private void doFinishSession() { + mInputMethodSession = null; + if (mReceiver != null) { + mReceiver.dispose(); + mReceiver = null; + } + if (mChannel != null) { + mChannel.dispose(); + mChannel = null; + } + } + + @Override public void finishInput() { mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT)); } + @Override public void displayCompletions(CompletionInfo[] completions) { mCaller.executeOrSendMessage(mCaller.obtainMessageO( DO_DISPLAY_COMPLETIONS, completions)); } - + + @Override public void updateExtractedText(int token, ExtractedText text) { mCaller.executeOrSendMessage(mCaller.obtainMessageIO( DO_UPDATE_EXTRACTED_TEXT, token, text)); } - - public void dispatchKeyEvent(int seq, KeyEvent event, IInputMethodCallback callback) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_KEY_EVENT, seq, - event, callback)); - } - - public void dispatchTrackballEvent(int seq, MotionEvent event, IInputMethodCallback callback) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_TRACKBALL_EVENT, seq, - event, callback)); - } - - public void dispatchGenericMotionEvent(int seq, MotionEvent event, - IInputMethodCallback callback) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_GENERIC_MOTION_EVENT, seq, - event, callback)); - } + @Override public void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION, @@ -191,24 +161,74 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub candidatesStart, candidatesEnd)); } + @Override public void viewClicked(boolean focusChanged) { - mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0)); + mCaller.executeOrSendMessage( + mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0)); } + @Override public void updateCursor(Rect newCursor) { - mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_UPDATE_CURSOR, - newCursor)); + mCaller.executeOrSendMessage( + mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor)); } - + + @Override public void appPrivateCommand(String action, Bundle data) { - mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data)); + mCaller.executeOrSendMessage( + mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data)); } - + + @Override public void toggleSoftInput(int showFlags, int hideFlags) { - mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags)); + mCaller.executeOrSendMessage( + mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags)); } + @Override public void finishSession() { mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION)); } + + private final class ImeInputEventReceiver extends InputEventReceiver + implements InputMethodSession.EventCallback { + private final SparseArray<InputEvent> mPendingEvents = new SparseArray<InputEvent>(); + + public ImeInputEventReceiver(InputChannel inputChannel, Looper looper) { + super(inputChannel, looper); + } + + @Override + public void onInputEvent(InputEvent event) { + if (mInputMethodSession == null) { + // The session has been finished. + finishInputEvent(event, false); + return; + } + + final int seq = event.getSequenceNumber(); + mPendingEvents.put(seq, event); + if (event instanceof KeyEvent) { + KeyEvent keyEvent = (KeyEvent)event; + mInputMethodSession.dispatchKeyEvent(seq, keyEvent, this); + } else { + MotionEvent motionEvent = (MotionEvent)event; + if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) { + mInputMethodSession.dispatchTrackballEvent(seq, motionEvent, this); + } else { + mInputMethodSession.dispatchGenericMotionEvent(seq, motionEvent, this); + } + } + } + + @Override + public void finishedEvent(int seq, boolean handled) { + int index = mPendingEvents.indexOfKey(seq); + if (index >= 0) { + InputEvent event = mPendingEvents.valueAt(index); + mPendingEvents.removeAt(index); + finishInputEvent(event, handled); + } + } + } } diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 0d981be..9306373 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -32,6 +32,7 @@ import android.os.Message; import android.os.RemoteException; import android.os.ResultReceiver; import android.util.Log; +import android.view.InputChannel; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputBinding; import android.view.inputmethod.InputConnection; @@ -53,7 +54,6 @@ import java.util.concurrent.TimeUnit; class IInputMethodWrapper extends IInputMethod.Stub implements HandlerCaller.Callback { private static final String TAG = "InputMethodWrapper"; - private static final boolean DEBUG = false; private static final int DO_DUMP = 1; private static final int DO_ATTACH_TOKEN = 10; @@ -78,20 +78,29 @@ class IInputMethodWrapper extends IInputMethod.Stub } // NOTE: we should have a cache of these. - static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { + static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { final Context mContext; + final InputChannel mChannel; final IInputSessionCallback mCb; - InputMethodSessionCallbackWrapper(Context context, IInputSessionCallback cb) { + + InputMethodSessionCallbackWrapper(Context context, InputChannel channel, + IInputSessionCallback cb) { mContext = context; + mChannel = channel; mCb = cb; } + + @Override public void sessionCreated(InputMethodSession session) { try { if (session != null) { IInputMethodSessionWrapper wrap = - new IInputMethodSessionWrapper(mContext, session); + new IInputMethodSessionWrapper(mContext, session, mChannel); mCb.sessionCreated(wrap); } else { + if (mChannel != null) { + mChannel.dispose(); + } mCb.sessionCreated(null); } } catch (RemoteException e) { @@ -112,6 +121,7 @@ class IInputMethodWrapper extends IInputMethod.Stub return mInputMethod.get(); } + @Override public void executeMessage(Message msg) { InputMethod inputMethod = mInputMethod.get(); // Need a valid reference to the inputMethod for everything except a dump. @@ -174,8 +184,11 @@ class IInputMethodWrapper extends IInputMethod.Stub return; } case DO_CREATE_SESSION: { + SomeArgs args = (SomeArgs)msg.obj; inputMethod.createSession(new InputMethodSessionCallbackWrapper( - mCaller.mContext, (IInputSessionCallback)msg.obj)); + mCaller.mContext, (InputChannel)args.arg1, + (IInputSessionCallback)args.arg2)); + args.recycle(); return; } case DO_SET_SESSION_ENABLED: @@ -197,8 +210,9 @@ class IInputMethodWrapper extends IInputMethod.Stub } Log.w(TAG, "Unhandled message code: " + msg.what); } - - @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + + @Override + protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { AbstractInputMethodService target = mTarget.get(); if (target == null) { return; @@ -224,10 +238,12 @@ class IInputMethodWrapper extends IInputMethod.Stub } } + @Override public void attachToken(IBinder token) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token)); } - + + @Override public void bindInput(InputBinding binding) { InputConnection ic = new InputConnectionWrapper( IInputContext.Stub.asInterface(binding.getConnectionToken())); @@ -235,24 +251,30 @@ class IInputMethodWrapper extends IInputMethod.Stub mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu)); } + @Override public void unbindInput() { mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT)); } + @Override public void startInput(IInputContext inputContext, EditorInfo attribute) { mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT, inputContext, attribute)); } + @Override public void restartInput(IInputContext inputContext, EditorInfo attribute) { mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT, inputContext, attribute)); } - public void createSession(IInputSessionCallback callback) { - mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CREATE_SESSION, callback)); + @Override + public void createSession(InputChannel channel, IInputSessionCallback callback) { + mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION, + channel, callback)); } + @Override public void setSessionEnabled(IInputMethodSession session, boolean enabled) { try { InputMethodSession ls = ((IInputMethodSessionWrapper) @@ -263,7 +285,8 @@ class IInputMethodWrapper extends IInputMethod.Stub Log.w(TAG, "Incoming session not of correct type: " + session, e); } } - + + @Override public void revokeSession(IInputMethodSession session) { try { InputMethodSession ls = ((IInputMethodSessionWrapper) @@ -273,17 +296,20 @@ class IInputMethodWrapper extends IInputMethod.Stub Log.w(TAG, "Incoming session not of correct type: " + session, e); } } - + + @Override public void showSoftInput(int flags, ResultReceiver resultReceiver) { mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT, flags, resultReceiver)); } - + + @Override public void hideSoftInput(int flags, ResultReceiver resultReceiver) { mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT, flags, resultReceiver)); } + @Override public void changeInputMethodSubtype(InputMethodSubtype subtype) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE, subtype)); diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java index 523af04..a797176 100644 --- a/core/java/android/view/InputChannel.java +++ b/core/java/android/view/InputChannel.java @@ -78,7 +78,9 @@ public final class InputChannel implements Parcelable { * Creates a new input channel pair. One channel should be provided to the input * dispatcher and the other to the application's input queue. * @param name The descriptive (non-unique) name of the channel pair. - * @return A pair of input channels. They are symmetric and indistinguishable. + * @return A pair of input channels. The first channel is designated as the + * server channel and should be used to publish input events. The second channel + * is designated as the client channel and should be used to consume input events. */ public static InputChannel[] openInputChannelPair(String name) { if (name == null) { @@ -123,10 +125,11 @@ public final class InputChannel implements Parcelable { nativeTransferTo(outParameter); } + @Override public int describeContents() { return Parcelable.CONTENTS_FILE_DESCRIPTOR; } - + public void readFromParcel(Parcel in) { if (in == null) { throw new IllegalArgumentException("in must not be null"); @@ -134,7 +137,8 @@ public final class InputChannel implements Parcelable { nativeReadFromParcel(in); } - + + @Override public void writeToParcel(Parcel out, int flags) { if (out == null) { throw new IllegalArgumentException("out must not be null"); diff --git a/core/java/android/view/InputEventSender.java b/core/java/android/view/InputEventSender.java new file mode 100644 index 0000000..adf63fe --- /dev/null +++ b/core/java/android/view/InputEventSender.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2013 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; + +import dalvik.system.CloseGuard; + +import android.os.Looper; +import android.os.MessageQueue; +import android.util.Log; + +/** + * Provides a low-level mechanism for an application to send input events. + * @hide + */ +public abstract class InputEventSender { + private static final String TAG = "InputEventSender"; + + private final CloseGuard mCloseGuard = CloseGuard.get(); + + private int mSenderPtr; + + // We keep references to the input channel and message queue objects here so that + // they are not GC'd while the native peer of the receiver is using them. + private InputChannel mInputChannel; + private MessageQueue mMessageQueue; + + private static native int nativeInit(InputEventSender sender, + InputChannel inputChannel, MessageQueue messageQueue); + private static native void nativeDispose(int senderPtr); + private static native boolean nativeSendKeyEvent(int senderPtr, int seq, KeyEvent event); + private static native boolean nativeSendMotionEvent(int senderPtr, int seq, MotionEvent event); + + /** + * Creates an input event sender bound to the specified input channel. + * + * @param inputChannel The input channel. + * @param looper The looper to use when invoking callbacks. + */ + public InputEventSender(InputChannel inputChannel, Looper looper) { + if (inputChannel == null) { + throw new IllegalArgumentException("inputChannel must not be null"); + } + if (looper == null) { + throw new IllegalArgumentException("looper must not be null"); + } + + mInputChannel = inputChannel; + mMessageQueue = looper.getQueue(); + mSenderPtr = nativeInit(this, inputChannel, mMessageQueue); + + mCloseGuard.open("dispose"); + } + + @Override + protected void finalize() throws Throwable { + try { + dispose(true); + } finally { + super.finalize(); + } + } + + /** + * Disposes the receiver. + */ + public void dispose() { + dispose(false); + } + + private void dispose(boolean finalized) { + if (mCloseGuard != null) { + if (finalized) { + mCloseGuard.warnIfOpen(); + } + mCloseGuard.close(); + } + + if (mSenderPtr != 0) { + nativeDispose(mSenderPtr); + mSenderPtr = 0; + } + mInputChannel = null; + mMessageQueue = null; + } + + /** + * Called when an input event is finished. + * + * @param seq The input event sequence number. + * @param handled True if the input event was handled. + */ + public void onInputEventFinished(int seq, boolean handled) { + } + + /** + * Sends an input event. + * Must be called on the same Looper thread to which the sender is attached. + * + * @param seq The input event sequence number. + * @param event The input event to send. + * @return True if the entire event was sent successfully. May return false + * if the input channel buffer filled before all samples were dispatched. + */ + public final boolean sendInputEvent(int seq, InputEvent event) { + if (event == null) { + throw new IllegalArgumentException("event must not be null"); + } + if (mSenderPtr == 0) { + Log.w(TAG, "Attempted to send an input event but the input event " + + "sender has already been disposed."); + return false; + } + + if (event instanceof KeyEvent) { + return nativeSendKeyEvent(mSenderPtr, seq, (KeyEvent)event); + } else { + return nativeSendMotionEvent(mSenderPtr, seq, (MotionEvent)event); + } + } + + // Called from native code. + @SuppressWarnings("unused") + private void dispatchInputEventFinished(int seq, boolean handled) { + onInputEventFinished(seq, handled); + } +} diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b30dc83..b228704 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2937,7 +2937,6 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_DISPATCH_KEY = 7; private final static int MSG_DISPATCH_APP_VISIBILITY = 8; private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9; - private final static int MSG_IME_FINISHED_EVENT = 10; private final static int MSG_DISPATCH_KEY_FROM_IME = 11; private final static int MSG_FINISH_INPUT_CONNECTION = 12; private final static int MSG_CHECK_FOCUS = 13; @@ -2975,8 +2974,6 @@ public final class ViewRootImpl implements ViewParent, return "MSG_DISPATCH_APP_VISIBILITY"; case MSG_DISPATCH_GET_NEW_SURFACE: return "MSG_DISPATCH_GET_NEW_SURFACE"; - case MSG_IME_FINISHED_EVENT: - return "MSG_IME_FINISHED_EVENT"; case MSG_DISPATCH_KEY_FROM_IME: return "MSG_DISPATCH_KEY_FROM_IME"; case MSG_FINISH_INPUT_CONNECTION: @@ -3018,9 +3015,6 @@ public final class ViewRootImpl implements ViewParent, info.target.invalidate(info.left, info.top, info.right, info.bottom); info.recycle(); break; - case MSG_IME_FINISHED_EVENT: - handleImeFinishedEvent(msg.arg1, msg.arg2 != 0); - break; case MSG_PROCESS_INPUT_EVENTS: mProcessInputEventsScheduled = false; doProcessInputEvents(); @@ -3444,26 +3438,15 @@ public final class ViewRootImpl implements ViewParent, mInputEventConsistencyVerifier.onTrackballEvent(event, 0); } + int result = EVENT_POST_IME; if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) { if (LOCAL_LOGV) Log.v(TAG, "Dispatching trackball " + event + " to " + mView); // Dispatch to the IME before propagating down the view hierarchy. - // The IME will eventually call back into handleImeFinishedEvent. - if (mLastWasImTarget) { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - final int seq = event.getSequenceNumber(); - if (DEBUG_IMF) - Log.v(TAG, "Sending trackball event to IME: seq=" - + seq + " event=" + event); - return imm.dispatchTrackballEvent(mView.getContext(), seq, event, - mInputMethodCallback); - } - } + result = dispatchImeInputEvent(q); } - - return EVENT_POST_IME; + return result; } private int deliverTrackballEventPostIme(QueuedInputEvent q) { @@ -3598,26 +3581,16 @@ public final class ViewRootImpl implements ViewParent, if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); } + + int result = EVENT_POST_IME; if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) { if (LOCAL_LOGV) Log.v(TAG, "Dispatching generic motion " + event + " to " + mView); // Dispatch to the IME before propagating down the view hierarchy. - // The IME will eventually call back into handleImeFinishedEvent. - if (mLastWasImTarget) { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - final int seq = event.getSequenceNumber(); - if (DEBUG_IMF) - Log.v(TAG, "Sending generic motion event to IME: seq=" - + seq + " event=" + event); - return imm.dispatchGenericMotionEvent(mView.getContext(), seq, event, - mInputMethodCallback); - } - } + result = dispatchImeInputEvent(q); } - - return EVENT_POST_IME; + return result; } private int deliverGenericMotionEventPostIme(QueuedInputEvent q) { @@ -3806,6 +3779,7 @@ public final class ViewRootImpl implements ViewParent, mInputEventConsistencyVerifier.onKeyEvent(event, 0); } + int result = EVENT_POST_IME; if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) { if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView); @@ -3815,20 +3789,9 @@ public final class ViewRootImpl implements ViewParent, } // Dispatch to the IME before propagating down the view hierarchy. - // The IME will eventually call back into handleImeFinishedEvent. - if (mLastWasImTarget) { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - final int seq = event.getSequenceNumber(); - if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq=" - + seq + " event=" + event); - return imm.dispatchKeyEvent(mView.getContext(), seq, event, - mInputMethodCallback); - } - } + result = dispatchImeInputEvent(q); } - - return EVENT_POST_IME; + return result; } private int deliverKeyEventPostIme(QueuedInputEvent q) { @@ -4317,14 +4280,6 @@ public final class ViewRootImpl implements ViewParent, } } - void dispatchImeFinishedEvent(int seq, boolean handled) { - Message msg = mHandler.obtainMessage(MSG_IME_FINISHED_EVENT); - msg.arg1 = seq; - msg.arg2 = handled ? 1 : 0; - msg.setAsynchronous(true); - mHandler.sendMessage(msg); - } - public void dispatchFinishInputConnection(InputConnection connection) { Message msg = mHandler.obtainMessage(MSG_FINISH_INPUT_CONNECTION, connection); mHandler.sendMessage(msg); @@ -4533,6 +4488,21 @@ public final class ViewRootImpl implements ViewParent, return q; } + int dispatchImeInputEvent(QueuedInputEvent q) { + if (mLastWasImTarget) { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + final InputEvent event = q.mEvent; + final int seq = event.getSequenceNumber(); + if (DEBUG_IMF) + Log.v(TAG, "Sending input event to IME: seq=" + seq + " event=" + event); + return imm.dispatchInputEvent(mView.getContext(), seq, event, + mInputMethodCallback); + } + } + return EVENT_POST_IME; + } + void handleImeFinishedEvent(int seq, boolean handled) { QueuedInputEvent q = mCurrentInputEventHead; if (q != null && q.mEvent.getSequenceNumber() == seq) { @@ -5132,7 +5102,7 @@ public final class ViewRootImpl implements ViewParent, public void finishedEvent(int seq, boolean handled) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { - viewAncestor.dispatchImeFinishedEvent(seq, handled); + viewAncestor.handleImeFinishedEvent(seq, handled); } } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index e602eb7..4207832 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -19,7 +19,6 @@ package android.view.inputmethod; import com.android.internal.os.SomeArgs; import com.android.internal.view.IInputConnectionWrapper; import com.android.internal.view.IInputContext; -import com.android.internal.view.IInputMethodCallback; import com.android.internal.view.IInputMethodClient; import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodSession; @@ -40,8 +39,10 @@ import android.text.style.SuggestionSpan; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; +import android.view.InputChannel; +import android.view.InputEvent; +import android.view.InputEventSender; import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.View; import android.view.ViewRootImpl; @@ -319,6 +320,8 @@ public final class InputMethodManager { * The actual instance of the method to make calls on it. */ IInputMethodSession mCurMethod; + InputChannel mCurChannel; + ImeInputEventSender mCurSender; PendingEvent mPendingEventPool; int mPendingEventPoolSize; @@ -363,10 +366,17 @@ public final class InputMethodManager { if (mBindSequence < 0 || mBindSequence != res.sequence) { Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence + ", given seq=" + res.sequence); + if (res.channel != null) { + res.channel.dispose(); + } return; } mCurMethod = res.method; + if (mCurChannel != null) { + mCurChannel.dispose(); + } + mCurChannel = res.channel; mCurId = res.id; mBindSequence = res.sequence; } @@ -482,10 +492,10 @@ public final class InputMethodManager { } final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { - @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + @Override + protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { // No need to check for dump permission, since we only give this // interface to the system. - CountDownLatch latch = new CountDownLatch(1); SomeArgs sargs = SomeArgs.obtain(); sargs.arg1 = fd; @@ -501,32 +511,29 @@ public final class InputMethodManager { fout.println("Interrupted waiting for dump"); } } - + + @Override public void setUsingInputMethod(boolean state) { } - + + @Override public void onBindMethod(InputBindResult res) { mH.sendMessage(mH.obtainMessage(MSG_BIND, res)); } - + + @Override public void onUnbindMethod(int sequence) { mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0)); } - + + @Override public void setActive(boolean active) { mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0)); } - }; - + }; + final InputConnection mDummyInputConnection = new BaseInputConnection(this, false); - final IInputMethodCallback mInputMethodCallback = new IInputMethodCallback.Stub() { - @Override - public void finishedEvent(int seq, boolean handled) { - InputMethodManager.this.finishedEvent(seq, handled); - } - }; - InputMethodManager(IInputMethodManager service, Looper looper) { mService = service; mMainLooper = looper; @@ -714,6 +721,14 @@ public final class InputMethodManager { mBindSequence = -1; mCurId = null; mCurMethod = null; + if (mCurSender != null) { + mCurSender.dispose(); + mCurSender = null; + } + if (mCurChannel != null) { + mCurChannel.dispose(); + mCurChannel = null; + } } /** @@ -1085,6 +1100,7 @@ public final class InputMethodManager { // we need to reschedule our work for over there. if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread"); vh.post(new Runnable() { + @Override public void run() { startInputInner(null, 0, 0, 0); } @@ -1158,11 +1174,20 @@ public final class InputMethodManager { if (res.id != null) { mBindSequence = res.sequence; mCurMethod = res.method; + if (mCurChannel != null) { + mCurChannel.dispose(); + } + mCurChannel = res.channel; mCurId = res.id; - } else if (mCurMethod == null) { - // This means there is no input method available. - if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); - return true; + } else { + if (res.channel != null) { + res.channel.dispose(); + } + if (mCurMethod == null) { + // This means there is no input method available. + if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); + return true; + } } } if (mCurMethod != null && mCompletions != null) { @@ -1556,76 +1581,39 @@ public final class InputMethodManager { throw new RuntimeException(e); } } - - /** - * @hide - */ - public int dispatchKeyEvent(Context context, int seq, KeyEvent key, - FinishedEventCallback callback) { - synchronized (mH) { - if (DEBUG) Log.d(TAG, "dispatchKeyEvent"); - - if (mCurMethod != null) { - if (key.getAction() == KeyEvent.ACTION_DOWN - && key.getKeyCode() == KeyEvent.KEYCODE_SYM - && key.getRepeatCount() == 0) { - showInputMethodPickerLocked(); - return ViewRootImpl.EVENT_HANDLED; - } - try { - if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod); - final long startTime = SystemClock.uptimeMillis(); - enqueuePendingEventLocked(startTime, seq, mCurId, callback); - mCurMethod.dispatchKeyEvent(seq, key, mInputMethodCallback); - return ViewRootImpl.EVENT_PENDING_IME; - } catch (RemoteException e) { - Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e); - } - } - } - return ViewRootImpl.EVENT_POST_IME; - } /** * @hide */ - public int dispatchTrackballEvent(Context context, int seq, MotionEvent motion, + public int dispatchInputEvent(Context context, int seq, InputEvent event, FinishedEventCallback callback) { synchronized (mH) { - if (DEBUG) Log.d(TAG, "dispatchTrackballEvent"); + if (DEBUG) Log.d(TAG, "dispatchInputEvent"); - if (mCurMethod != null && mCurrentTextBoxAttribute != null) { - try { - if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod); - final long startTime = SystemClock.uptimeMillis(); - enqueuePendingEventLocked(startTime, seq, mCurId, callback); - mCurMethod.dispatchTrackballEvent(seq, motion, mInputMethodCallback); - return ViewRootImpl.EVENT_PENDING_IME; - } catch (RemoteException e) { - Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e); + if (mCurMethod != null) { + if (event instanceof KeyEvent) { + KeyEvent keyEvent = (KeyEvent)event; + if (keyEvent.getAction() == KeyEvent.ACTION_DOWN + && keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM + && keyEvent.getRepeatCount() == 0) { + showInputMethodPickerLocked(); + return ViewRootImpl.EVENT_HANDLED; + } } - } - } - return ViewRootImpl.EVENT_POST_IME; - } - /** - * @hide - */ - public int dispatchGenericMotionEvent(Context context, int seq, MotionEvent motion, - FinishedEventCallback callback) { - synchronized (mH) { - if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent"); - - if (mCurMethod != null && mCurrentTextBoxAttribute != null) { - try { - if (DEBUG) Log.v(TAG, "DISPATCH GENERIC MOTION: " + mCurMethod); - final long startTime = SystemClock.uptimeMillis(); - enqueuePendingEventLocked(startTime, seq, mCurId, callback); - mCurMethod.dispatchGenericMotionEvent(seq, motion, mInputMethodCallback); - return ViewRootImpl.EVENT_PENDING_IME; - } catch (RemoteException e) { - Log.w(TAG, "IME died: " + mCurId + " dropping generic motion: " + motion, e); + if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod); + final long startTime = SystemClock.uptimeMillis(); + if (mCurChannel != null) { + if (mCurSender == null) { + mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper()); + } + if (mCurSender.sendInputEvent(seq, event)) { + enqueuePendingEventLocked(startTime, seq, mCurId, callback); + return ViewRootImpl.EVENT_PENDING_IME; + } else { + Log.w(TAG, "Unable to send input event to IME: " + + mCurId + " dropping: " + event); + } } } } @@ -1937,6 +1925,17 @@ public final class InputMethodManager { public void finishedEvent(int seq, boolean handled); } + private final class ImeInputEventSender extends InputEventSender { + public ImeInputEventSender(InputChannel inputChannel, Looper looper) { + super(inputChannel, looper); + } + + @Override + public void onInputEventFinished(int seq, boolean handled) { + finishedEvent(seq, handled); + } + } + private static final class PendingEvent { public PendingEvent mNext; |