From b38070caa5143ab9fd1883e0c7c879533a480bc7 Mon Sep 17 00:00:00 2001 From: Victoria Lease Date: Fri, 24 Aug 2012 13:46:02 -0700 Subject: [PATCH] IME support for trackball and generic motion events Trackball and generic motion events now pass through the IME in case it would like to handle them before passing them on to the view hierarchy. While I was at it, I also... ...fixed the documentation on InputMethodService.onKeyUp() ...added documentation to InputMethodService.onTrackballEvent() ...added trackball and generic motion events to the "input" command ...fixed input consistency verification involving ACTION_OUTSIDE Bug: 7050005 Change-Id: I40ab68df4a9542af6df25de6ec2ec500e4c02902 --- api/current.txt | 3 + .../src/com/android/commands/input/Input.java | 106 +++++++++++++++--- .../AbstractInputMethodService.java | 29 +++++ .../IInputMethodSessionWrapper.java | 16 +++ .../InputMethodService.java | 25 ++++- .../view/InputEventConsistencyVerifier.java | 2 +- core/java/android/view/ViewRootImpl.java | 81 ++++++++++--- .../view/inputmethod/InputMethodManager.java | 26 ++++- .../view/inputmethod/InputMethodSession.java | 15 +++ .../internal/view/IInputMethodSession.aidl | 2 + 10 files changed, 271 insertions(+), 34 deletions(-) diff --git a/api/current.txt b/api/current.txt index 7ad9cad58b73a..c394c9973c46a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10215,6 +10215,7 @@ package android.inputmethodservice { method public final android.os.IBinder onBind(android.content.Intent); method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface(); method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface(); + method public boolean onGenericMotionEvent(android.view.MotionEvent); method public boolean onTrackballEvent(android.view.MotionEvent); } @@ -10227,6 +10228,7 @@ package android.inputmethodservice { public abstract class AbstractInputMethodService.AbstractInputMethodSessionImpl implements android.view.inputmethod.InputMethodSession { ctor public AbstractInputMethodService.AbstractInputMethodSessionImpl(); + method public void dispatchGenericMotionEvent(int, android.view.MotionEvent, android.view.inputmethod.InputMethodSession.EventCallback); method public void dispatchKeyEvent(int, android.view.KeyEvent, android.view.inputmethod.InputMethodSession.EventCallback); method public void dispatchTrackballEvent(int, android.view.MotionEvent, android.view.inputmethod.InputMethodSession.EventCallback); method public boolean isEnabled(); @@ -26692,6 +26694,7 @@ package android.view.inputmethod { public abstract interface InputMethodSession { method public abstract void appPrivateCommand(java.lang.String, android.os.Bundle); + method public abstract void dispatchGenericMotionEvent(int, android.view.MotionEvent, android.view.inputmethod.InputMethodSession.EventCallback); method public abstract void dispatchKeyEvent(int, android.view.KeyEvent, android.view.inputmethod.InputMethodSession.EventCallback); method public abstract void dispatchTrackballEvent(int, android.view.MotionEvent, android.view.inputmethod.InputMethodSession.EventCallback); method public abstract void displayCompletions(android.view.inputmethod.CompletionInfo[]); diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java index 341f30fbaf9e2..a21df0d2c8b22 100755 --- a/cmds/input/src/com/android/commands/input/Input.java +++ b/cmds/input/src/com/android/commands/input/Input.java @@ -66,15 +66,54 @@ public class Input { } } else if (command.equals("tap")) { if (args.length == 3) { - sendTap(Float.parseFloat(args[1]), Float.parseFloat(args[2])); + sendTap(InputDevice.SOURCE_TOUCHSCREEN, Float.parseFloat(args[1]), Float.parseFloat(args[2])); return; } } else if (command.equals("swipe")) { if (args.length == 5) { - sendSwipe(Float.parseFloat(args[1]), Float.parseFloat(args[2]), + sendSwipe(InputDevice.SOURCE_TOUCHSCREEN, Float.parseFloat(args[1]), Float.parseFloat(args[2]), Float.parseFloat(args[3]), Float.parseFloat(args[4])); return; } + } else if (command.equals("touchscreen") || command.equals("touchpad")) { + // determine input source + int inputSource = InputDevice.SOURCE_TOUCHSCREEN; + if (command.equals("touchpad")) { + inputSource = InputDevice.SOURCE_TOUCHPAD; + } + // determine subcommand + if (args.length > 1) { + String subcommand = args[1]; + if (subcommand.equals("tap")) { + if (args.length == 4) { + sendTap(inputSource, Float.parseFloat(args[2]), + Float.parseFloat(args[3])); + return; + } + } else if (subcommand.equals("swipe")) { + if (args.length == 6) { + sendSwipe(inputSource, Float.parseFloat(args[2]), + Float.parseFloat(args[3]), Float.parseFloat(args[4]), + Float.parseFloat(args[5])); + return; + } + } + } + } else if (command.equals("trackball")) { + // determine subcommand + if (args.length > 1) { + String subcommand = args[1]; + if (subcommand.equals("press")) { + sendTap(InputDevice.SOURCE_TRACKBALL, 0.0f, 0.0f); + return; + } else if (subcommand.equals("roll")) { + if (args.length == 4) { + sendMove(InputDevice.SOURCE_TRACKBALL, Float.parseFloat(args[2]), + Float.parseFloat(args[3])); + return; + } + } + } } else { System.err.println("Error: Unknown command: " + command); showUsage(); @@ -127,33 +166,64 @@ public class Input { KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD)); } - private void sendTap(float x, float y) { + private void sendTap(int inputSource, float x, float y) { long now = SystemClock.uptimeMillis(); - injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, x, y, 0)); - injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, x, y, 0)); + injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x, y, 1.0f); + injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x, y, 0.0f); } - private void sendSwipe(float x1, float y1, float x2, float y2) { + private void sendSwipe(int inputSource, float x1, float y1, float x2, float y2) { final int NUM_STEPS = 11; long now = SystemClock.uptimeMillis(); - injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, x1, y1, 0)); + injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f); for (int i = 1; i < NUM_STEPS; i++) { - float alpha = (float)i / NUM_STEPS; - injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE, - lerp(x1, x2, alpha), lerp(y1, y2, alpha), 0)); + float alpha = (float) i / NUM_STEPS; + injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha), + lerp(y1, y2, alpha), 1.0f); } - injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, x2, y2, 0)); + injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x1, y1, 0.0f); + } + + /** + * Sends a simple zero-pressure move event. + * + * @param inputSource the InputDevice.SOURCE_* sending the input event + * @param dx change in x coordinate due to move + * @param dy change in y coordinate due to move + */ + private void sendMove(int inputSource, float dx, float dy) { + long now = SystemClock.uptimeMillis(); + injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, dx, dy, 0.0f); } private void injectKeyEvent(KeyEvent event) { - Log.i(TAG, "InjectKeyEvent: " + event); + Log.i(TAG, "injectKeyEvent: " + event); InputManager.getInstance().injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); } - private void injectPointerEvent(MotionEvent event) { - event.setSource(InputDevice.SOURCE_TOUCHSCREEN); - Log.i("Input", "InjectPointerEvent: " + event); + /** + * Builds a MotionEvent and injects it into the event stream. + * + * @param inputSource the InputDevice.SOURCE_* sending the input event + * @param action the MotionEvent.ACTION_* for the event + * @param when the value of SystemClock.uptimeMillis() at which the event happened + * @param x x coordinate of event + * @param y y coordinate of event + * @param pressure pressure of event + */ + private void injectMotionEvent(int inputSource, int action, long when, float x, float y, float pressure) { + final float DEFAULT_SIZE = 1.0f; + final int DEFAULT_META_STATE = 0; + final float DEFAULT_PRECISION_X = 1.0f; + final float DEFAULT_PRECISION_Y = 1.0f; + final int DEFAULT_DEVICE_ID = 0; + final int DEFAULT_EDGE_FLAGS = 0; + MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, DEFAULT_SIZE, + DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y, DEFAULT_DEVICE_ID, + DEFAULT_EDGE_FLAGS); + event.setSource(inputSource); + Log.i("Input", "injectMotionEvent: " + event); InputManager.getInstance().injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); } @@ -166,7 +236,9 @@ public class Input { System.err.println("usage: input ..."); System.err.println(" input text "); System.err.println(" input keyevent "); - System.err.println(" input tap "); - System.err.println(" input swipe "); + System.err.println(" input [touchscreen|touchpad] tap "); + System.err.println(" input [touchscreen|touchpad] swipe "); + System.err.println(" input trackball press"); + System.err.println(" input trackball roll "); } } diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 27af0135cc057..3c3182accc8ec 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -149,6 +149,17 @@ public abstract class AbstractInputMethodService extends Service callback.finishedEvent(seq, handled); } } + + /** + * Take care of dispatching incoming generic motion events to the appropriate + * callbacks on the service, and tell the client when this is done. + */ + public void dispatchGenericMotionEvent(int seq, MotionEvent event, EventCallback callback) { + boolean handled = onGenericMotionEvent(event); + if (callback != null) { + callback.finishedEvent(seq, handled); + } + } } /** @@ -189,7 +200,25 @@ public abstract class AbstractInputMethodService extends Service return new IInputMethodWrapper(this, mInputMethod); } + /** + * Implement this to handle trackball events on your input method. + * + * @param event The motion event being received. + * @return True if the event was handled in this function, false otherwise. + * @see View#onTrackballEvent + */ public boolean onTrackballEvent(MotionEvent event) { return false; } + + /** + * Implement this to handle generic motion events on your input method. + * + * @param event The motion event being received. + * @return True if the event was handled in this function, false otherwise. + * @see View#onGenericMotionEvent + */ + public boolean onGenericMotionEvent(MotionEvent event) { + return false; + } } diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index ce797d1d50f76..5324f81794f1e 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -43,6 +43,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub 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; @@ -109,6 +110,15 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub 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, @@ -167,6 +177,12 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub event, callback)); } + public void dispatchGenericMotionEvent(int seq, MotionEvent event, + IInputMethodCallback callback) { + mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_GENERIC_MOTION_EVENT, seq, + event, callback)); + } + public void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION, diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index d5b9edcc5588e..cf3b802b6a42c 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1773,7 +1773,7 @@ public class InputMethodService extends AbstractInputMethodService { * Override this to intercept special key multiple events before they are * processed by the * application. If you return true, the application will not itself - * process the event. If you return true, the normal application processing + * process the event. If you return false, the normal application processing * will occur as if the IME had not seen the event at all. * *

The default implementation always returns false, except when @@ -1788,7 +1788,7 @@ public class InputMethodService extends AbstractInputMethodService { /** * Override this to intercept key up events before they are processed by the * application. If you return true, the application will not itself - * process the event. If you return true, the normal application processing + * process the event. If you return false, the normal application processing * will occur as if the IME had not seen the event at all. * *

The default implementation intercepts {@link KeyEvent#KEYCODE_BACK @@ -1806,8 +1806,29 @@ public class InputMethodService extends AbstractInputMethodService { return doMovementKey(keyCode, event, MOVEMENT_UP); } + /** + * Override this to intercept trackball motion events before they are + * processed by the application. + * If you return true, the application will not itself process the event. + * If you return false, the normal application processing will occur as if + * the IME had not seen the event at all. + */ @Override public boolean onTrackballEvent(MotionEvent event) { + if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event); + return false; + } + + /** + * Override this to intercept generic motion events before they are + * processed by the application. + * If you return true, the application will not itself process the event. + * If you return false, the normal application processing will occur as if + * the IME had not seen the event at all. + */ + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event); return false; } diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java index fafe416defdf7..5dda9343a742c 100644 --- a/core/java/android/view/InputEventConsistencyVerifier.java +++ b/core/java/android/view/InputEventConsistencyVerifier.java @@ -322,7 +322,7 @@ public final class InputEventConsistencyVerifier { final int action = event.getAction(); final boolean newStream = action == MotionEvent.ACTION_DOWN - || action == MotionEvent.ACTION_CANCEL; + || action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_OUTSIDE; if (newStream && (mTouchEventStreamIsTainted || mTouchEventStreamUnhandled)) { mTouchEventStreamIsTainted = false; mTouchEventStreamUnhandled = false; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index a4c02354ed45b..3006b5d5687c2 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3211,6 +3211,33 @@ public final class ViewRootImpl implements ViewParent, mInputEventConsistencyVerifier.onTrackballEvent(event, 0); } + 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); + imm.dispatchTrackballEvent(mView.getContext(), seq, event, + mInputMethodCallback); + return; + } + } + } + + // Not dispatching to IME, continue with post IME actions. + deliverTrackballEventPostIme(q); + } + + private void deliverTrackballEventPostIme(QueuedInputEvent q) { + final MotionEvent event = (MotionEvent) q.mEvent; + // If there is no view, then the event will not be handled. if (mView == null || !mAdded) { finishInputEvent(q, false); @@ -3344,8 +3371,33 @@ public final class ViewRootImpl implements ViewParent, mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); } - final int source = event.getSource(); - final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0; + 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); + imm.dispatchGenericMotionEvent(mView.getContext(), seq, event, + mInputMethodCallback); + return; + } + } + } + + // Not dispatching to IME, continue with post IME actions. + deliverGenericMotionEventPostIme(q); + } + + private void deliverGenericMotionEventPostIme(QueuedInputEvent q) { + final MotionEvent event = (MotionEvent) q.mEvent; + final boolean isJoystick = (event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0; // If there is no view, then the event will not be handled. if (mView == null || !mAdded) { @@ -3366,7 +3418,8 @@ public final class ViewRootImpl implements ViewParent, } if (isJoystick) { - // Translate the joystick event into DPAD keys and try to deliver those. + // Translate the joystick event into DPAD keys and try to deliver + // those. updateJoystickDirection(event, true); finishInputEvent(q, true); } else { @@ -3521,13 +3574,7 @@ public final class ViewRootImpl implements ViewParent, mInputEventConsistencyVerifier.onKeyEvent(event, 0); } - if ((q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) { - // If there is no view, then the event will not be handled. - if (mView == null || !mAdded) { - finishInputEvent(q, false); - return; - } - + if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) { if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView); // Perform predispatching before the IME. @@ -3557,15 +3604,23 @@ public final class ViewRootImpl implements ViewParent, void handleImeFinishedEvent(int seq, boolean handled) { final QueuedInputEvent q = mCurrentInputEvent; if (q != null && q.mEvent.getSequenceNumber() == seq) { - final KeyEvent event = (KeyEvent)q.mEvent; if (DEBUG_IMF) { Log.v(TAG, "IME finished event: seq=" + seq - + " handled=" + handled + " event=" + event); + + " handled=" + handled + " event=" + q); } if (handled) { finishInputEvent(q, true); } else { - deliverKeyEventPostIme(q); + if (q.mEvent instanceof KeyEvent) { + deliverKeyEventPostIme(q); + } else { + final int source = q.mEvent.getSource(); + if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { + deliverTrackballEventPostIme(q); + } else { + deliverGenericMotionEventPostIme(q); + } + } } } else { if (DEBUG_IMF) { diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index e754adc54cf93..3ea6df3a89cc4 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1593,7 +1593,7 @@ public final class InputMethodManager { /** * @hide */ - void dispatchTrackballEvent(Context context, int seq, MotionEvent motion, + public void dispatchTrackballEvent(Context context, int seq, MotionEvent motion, FinishedEventCallback callback) { synchronized (mH) { if (DEBUG) Log.d(TAG, "dispatchTrackballEvent"); @@ -1614,6 +1614,30 @@ public final class InputMethodManager { callback.finishedEvent(seq, false); } + /** + * @hide + */ + public void 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; + } catch (RemoteException e) { + Log.w(TAG, "IME died: " + mCurId + " dropping generic motion: " + motion, e); + } + } + } + + callback.finishedEvent(seq, false); + } + void finishedEvent(int seq, boolean handled) { final FinishedEventCallback callback; synchronized (mH) { diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java index ea6f5ee4a9762..63862999519d9 100644 --- a/core/java/android/view/inputmethod/InputMethodSession.java +++ b/core/java/android/view/inputmethod/InputMethodSession.java @@ -137,6 +137,21 @@ public interface InputMethodSession { */ public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback); + /** + * This method is called when there is a generic motion event. + * + *

+ * If the input method wants to handle this event, return true, otherwise + * return false and the caller (i.e. the application) will handle the event. + * + * @param event The motion event. + * + * @return Whether the input method wants to handle this event. + * + * @see android.view.MotionEvent + */ + public void dispatchGenericMotionEvent(int seq, MotionEvent event, EventCallback callback); + /** * Process a private command sent from the application to the input method. * This can be used to provide domain-specific features that are diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl index f875cbd95a079..cdec254e28df5 100644 --- a/core/java/com/android/internal/view/IInputMethodSession.aidl +++ b/core/java/com/android/internal/view/IInputMethodSession.aidl @@ -47,6 +47,8 @@ oneway interface IInputMethodSession { void dispatchTrackballEvent(int seq, in MotionEvent event, IInputMethodCallback callback); + void dispatchGenericMotionEvent(int seq, in MotionEvent event, IInputMethodCallback callback); + void appPrivateCommand(String action, in Bundle data); void toggleSoftInput(int showFlags, int hideFlags);