diff options
author | Jeff Brown <jeffbrown@google.com> | 2013-03-26 19:46:20 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2013-03-26 19:46:20 -0700 |
commit | 901b77c63bc707c5785a149975e2113a43e38ad6 (patch) | |
tree | 2766b887f99e37bcdc423ecf668b7ad6ee58d609 /core/java/android/view | |
parent | 483ac9a779af452d7ef4007d0e24c569ee894557 (diff) | |
parent | ca3d655d20c13c71972a4475cec3b98efa7dbdd0 (diff) |
am ca3d655d: Merge "Use input transport for communications between app and IME." into jb-mr2-dev
* commit 'ca3d655d20c13c71972a4475cec3b98efa7dbdd0':
Use input transport for communications between app and IME.
Diffstat (limited to 'core/java/android/view')
-rw-r--r-- | core/java/android/view/InputChannel.java | 10 | ||||
-rw-r--r-- | core/java/android/view/InputEventSender.java | 140 | ||||
-rw-r--r-- | core/java/android/view/ViewRootImpl.java | 82 | ||||
-rw-r--r-- | core/java/android/view/inputmethod/InputMethodManager.java | 163 |
4 files changed, 254 insertions, 141 deletions
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java index 523af04ada5e..a797176d799d 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 000000000000..adf63fea598e --- /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 401db1f4247d..7b34ce1cff95 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; @@ -2977,8 +2976,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: @@ -3024,9 +3021,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(); @@ -3462,26 +3456,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) { @@ -3616,26 +3599,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) { @@ -3834,6 +3807,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); @@ -3843,20 +3817,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) { @@ -4345,14 +4308,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); @@ -4561,6 +4516,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) { @@ -5160,7 +5130,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 e602eb7bd004..4207832d5f58 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; |