diff options
10 files changed, 271 insertions, 34 deletions
diff --git a/api/current.txt b/api/current.txt index 7ad9cad..c394c99 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 341f30f..a21df0d 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 <string>"); System.err.println(" input keyevent <key code number or name>"); - System.err.println(" input tap <x> <y>"); - System.err.println(" input swipe <x1> <y1> <x2> <y2>"); + System.err.println(" input [touchscreen|touchpad] tap <x> <y>"); + System.err.println(" input [touchscreen|touchpad] swipe <x1> <y1> <x2> <y2>"); + System.err.println(" input trackball press"); + System.err.println(" input trackball roll <dx> <dy>"); } } diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 27af013..3c3182a 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 ce797d1..5324f81 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 d5b9edc..cf3b802 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. * * <p>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. * * <p>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 fafe416..5dda934 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 a4c0235..3006b5d 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 e754adc..3ea6df3 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 ea6f5ee..6386299 100644 --- a/core/java/android/view/inputmethod/InputMethodSession.java +++ b/core/java/android/view/inputmethod/InputMethodSession.java @@ -138,6 +138,21 @@ public interface InputMethodSession { public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback); /** + * This method is called when there is a generic motion event. + * + * <p> + * 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 * only known between certain input methods and their clients. diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl index f875cbd..cdec254 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); |