Skip to content

Commit b38070c

Browse files
author
Victoria Lease
committed
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
1 parent 37ee534 commit b38070c

File tree

10 files changed

+271
-34
lines changed

10 files changed

+271
-34
lines changed

api/current.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10215,6 +10215,7 @@ package android.inputmethodservice {
1021510215
method public final android.os.IBinder onBind(android.content.Intent);
1021610216
method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface();
1021710217
method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
10218+
method public boolean onGenericMotionEvent(android.view.MotionEvent);
1021810219
method public boolean onTrackballEvent(android.view.MotionEvent);
1021910220
}
1022010221

@@ -10227,6 +10228,7 @@ package android.inputmethodservice {
1022710228

1022810229
public abstract class AbstractInputMethodService.AbstractInputMethodSessionImpl implements android.view.inputmethod.InputMethodSession {
1022910230
ctor public AbstractInputMethodService.AbstractInputMethodSessionImpl();
10231+
method public void dispatchGenericMotionEvent(int, android.view.MotionEvent, android.view.inputmethod.InputMethodSession.EventCallback);
1023010232
method public void dispatchKeyEvent(int, android.view.KeyEvent, android.view.inputmethod.InputMethodSession.EventCallback);
1023110233
method public void dispatchTrackballEvent(int, android.view.MotionEvent, android.view.inputmethod.InputMethodSession.EventCallback);
1023210234
method public boolean isEnabled();
@@ -26692,6 +26694,7 @@ package android.view.inputmethod {
2669226694

2669326695
public abstract interface InputMethodSession {
2669426696
method public abstract void appPrivateCommand(java.lang.String, android.os.Bundle);
26697+
method public abstract void dispatchGenericMotionEvent(int, android.view.MotionEvent, android.view.inputmethod.InputMethodSession.EventCallback);
2669526698
method public abstract void dispatchKeyEvent(int, android.view.KeyEvent, android.view.inputmethod.InputMethodSession.EventCallback);
2669626699
method public abstract void dispatchTrackballEvent(int, android.view.MotionEvent, android.view.inputmethod.InputMethodSession.EventCallback);
2669726700
method public abstract void displayCompletions(android.view.inputmethod.CompletionInfo[]);

cmds/input/src/com/android/commands/input/Input.java

Lines changed: 89 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,54 @@ private void run(String[] args) {
6666
}
6767
} else if (command.equals("tap")) {
6868
if (args.length == 3) {
69-
sendTap(Float.parseFloat(args[1]), Float.parseFloat(args[2]));
69+
sendTap(InputDevice.SOURCE_TOUCHSCREEN, Float.parseFloat(args[1]), Float.parseFloat(args[2]));
7070
return;
7171
}
7272
} else if (command.equals("swipe")) {
7373
if (args.length == 5) {
74-
sendSwipe(Float.parseFloat(args[1]), Float.parseFloat(args[2]),
74+
sendSwipe(InputDevice.SOURCE_TOUCHSCREEN, Float.parseFloat(args[1]), Float.parseFloat(args[2]),
7575
Float.parseFloat(args[3]), Float.parseFloat(args[4]));
7676
return;
7777
}
78+
} else if (command.equals("touchscreen") || command.equals("touchpad")) {
79+
// determine input source
80+
int inputSource = InputDevice.SOURCE_TOUCHSCREEN;
81+
if (command.equals("touchpad")) {
82+
inputSource = InputDevice.SOURCE_TOUCHPAD;
83+
}
84+
// determine subcommand
85+
if (args.length > 1) {
86+
String subcommand = args[1];
87+
if (subcommand.equals("tap")) {
88+
if (args.length == 4) {
89+
sendTap(inputSource, Float.parseFloat(args[2]),
90+
Float.parseFloat(args[3]));
91+
return;
92+
}
93+
} else if (subcommand.equals("swipe")) {
94+
if (args.length == 6) {
95+
sendSwipe(inputSource, Float.parseFloat(args[2]),
96+
Float.parseFloat(args[3]), Float.parseFloat(args[4]),
97+
Float.parseFloat(args[5]));
98+
return;
99+
}
100+
}
101+
}
102+
} else if (command.equals("trackball")) {
103+
// determine subcommand
104+
if (args.length > 1) {
105+
String subcommand = args[1];
106+
if (subcommand.equals("press")) {
107+
sendTap(InputDevice.SOURCE_TRACKBALL, 0.0f, 0.0f);
108+
return;
109+
} else if (subcommand.equals("roll")) {
110+
if (args.length == 4) {
111+
sendMove(InputDevice.SOURCE_TRACKBALL, Float.parseFloat(args[2]),
112+
Float.parseFloat(args[3]));
113+
return;
114+
}
115+
}
116+
}
78117
} else {
79118
System.err.println("Error: Unknown command: " + command);
80119
showUsage();
@@ -127,33 +166,64 @@ private void sendKeyEvent(int keyCode) {
127166
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
128167
}
129168

130-
private void sendTap(float x, float y) {
169+
private void sendTap(int inputSource, float x, float y) {
131170
long now = SystemClock.uptimeMillis();
132-
injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, x, y, 0));
133-
injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, x, y, 0));
171+
injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x, y, 1.0f);
172+
injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x, y, 0.0f);
134173
}
135174

136-
private void sendSwipe(float x1, float y1, float x2, float y2) {
175+
private void sendSwipe(int inputSource, float x1, float y1, float x2, float y2) {
137176
final int NUM_STEPS = 11;
138177
long now = SystemClock.uptimeMillis();
139-
injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, x1, y1, 0));
178+
injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
140179
for (int i = 1; i < NUM_STEPS; i++) {
141-
float alpha = (float)i / NUM_STEPS;
142-
injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE,
143-
lerp(x1, x2, alpha), lerp(y1, y2, alpha), 0));
180+
float alpha = (float) i / NUM_STEPS;
181+
injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha),
182+
lerp(y1, y2, alpha), 1.0f);
144183
}
145-
injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, x2, y2, 0));
184+
injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x1, y1, 0.0f);
185+
}
186+
187+
/**
188+
* Sends a simple zero-pressure move event.
189+
*
190+
* @param inputSource the InputDevice.SOURCE_* sending the input event
191+
* @param dx change in x coordinate due to move
192+
* @param dy change in y coordinate due to move
193+
*/
194+
private void sendMove(int inputSource, float dx, float dy) {
195+
long now = SystemClock.uptimeMillis();
196+
injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, dx, dy, 0.0f);
146197
}
147198

148199
private void injectKeyEvent(KeyEvent event) {
149-
Log.i(TAG, "InjectKeyEvent: " + event);
200+
Log.i(TAG, "injectKeyEvent: " + event);
150201
InputManager.getInstance().injectInputEvent(event,
151202
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
152203
}
153204

154-
private void injectPointerEvent(MotionEvent event) {
155-
event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
156-
Log.i("Input", "InjectPointerEvent: " + event);
205+
/**
206+
* Builds a MotionEvent and injects it into the event stream.
207+
*
208+
* @param inputSource the InputDevice.SOURCE_* sending the input event
209+
* @param action the MotionEvent.ACTION_* for the event
210+
* @param when the value of SystemClock.uptimeMillis() at which the event happened
211+
* @param x x coordinate of event
212+
* @param y y coordinate of event
213+
* @param pressure pressure of event
214+
*/
215+
private void injectMotionEvent(int inputSource, int action, long when, float x, float y, float pressure) {
216+
final float DEFAULT_SIZE = 1.0f;
217+
final int DEFAULT_META_STATE = 0;
218+
final float DEFAULT_PRECISION_X = 1.0f;
219+
final float DEFAULT_PRECISION_Y = 1.0f;
220+
final int DEFAULT_DEVICE_ID = 0;
221+
final int DEFAULT_EDGE_FLAGS = 0;
222+
MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, DEFAULT_SIZE,
223+
DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y, DEFAULT_DEVICE_ID,
224+
DEFAULT_EDGE_FLAGS);
225+
event.setSource(inputSource);
226+
Log.i("Input", "injectMotionEvent: " + event);
157227
InputManager.getInstance().injectInputEvent(event,
158228
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
159229
}
@@ -166,7 +236,9 @@ private void showUsage() {
166236
System.err.println("usage: input ...");
167237
System.err.println(" input text <string>");
168238
System.err.println(" input keyevent <key code number or name>");
169-
System.err.println(" input tap <x> <y>");
170-
System.err.println(" input swipe <x1> <y1> <x2> <y2>");
239+
System.err.println(" input [touchscreen|touchpad] tap <x> <y>");
240+
System.err.println(" input [touchscreen|touchpad] swipe <x1> <y1> <x2> <y2>");
241+
System.err.println(" input trackball press");
242+
System.err.println(" input trackball roll <dx> <dy>");
171243
}
172244
}

core/java/android/inputmethodservice/AbstractInputMethodService.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,17 @@ public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback cal
149149
callback.finishedEvent(seq, handled);
150150
}
151151
}
152+
153+
/**
154+
* Take care of dispatching incoming generic motion events to the appropriate
155+
* callbacks on the service, and tell the client when this is done.
156+
*/
157+
public void dispatchGenericMotionEvent(int seq, MotionEvent event, EventCallback callback) {
158+
boolean handled = onGenericMotionEvent(event);
159+
if (callback != null) {
160+
callback.finishedEvent(seq, handled);
161+
}
162+
}
152163
}
153164

154165
/**
@@ -189,7 +200,25 @@ final public IBinder onBind(Intent intent) {
189200
return new IInputMethodWrapper(this, mInputMethod);
190201
}
191202

203+
/**
204+
* Implement this to handle trackball events on your input method.
205+
*
206+
* @param event The motion event being received.
207+
* @return True if the event was handled in this function, false otherwise.
208+
* @see View#onTrackballEvent
209+
*/
192210
public boolean onTrackballEvent(MotionEvent event) {
193211
return false;
194212
}
213+
214+
/**
215+
* Implement this to handle generic motion events on your input method.
216+
*
217+
* @param event The motion event being received.
218+
* @return True if the event was handled in this function, false otherwise.
219+
* @see View#onGenericMotionEvent
220+
*/
221+
public boolean onGenericMotionEvent(MotionEvent event) {
222+
return false;
223+
}
195224
}

core/java/android/inputmethodservice/IInputMethodSessionWrapper.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
4343
private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
4444
private static final int DO_DISPATCH_KEY_EVENT = 70;
4545
private static final int DO_DISPATCH_TRACKBALL_EVENT = 80;
46+
private static final int DO_DISPATCH_GENERIC_MOTION_EVENT = 85;
4647
private static final int DO_UPDATE_SELECTION = 90;
4748
private static final int DO_UPDATE_CURSOR = 95;
4849
private static final int DO_APP_PRIVATE_COMMAND = 100;
@@ -109,6 +110,15 @@ public void executeMessage(Message msg) {
109110
args.recycle();
110111
return;
111112
}
113+
case DO_DISPATCH_GENERIC_MOTION_EVENT: {
114+
SomeArgs args = (SomeArgs)msg.obj;
115+
mInputMethodSession.dispatchGenericMotionEvent(msg.arg1,
116+
(MotionEvent)args.arg1,
117+
new InputMethodEventCallbackWrapper(
118+
(IInputMethodCallback)args.arg2));
119+
args.recycle();
120+
return;
121+
}
112122
case DO_UPDATE_SELECTION: {
113123
SomeArgs args = (SomeArgs)msg.obj;
114124
mInputMethodSession.updateSelection(args.argi1, args.argi2,
@@ -167,6 +177,12 @@ public void dispatchTrackballEvent(int seq, MotionEvent event, IInputMethodCallb
167177
event, callback));
168178
}
169179

180+
public void dispatchGenericMotionEvent(int seq, MotionEvent event,
181+
IInputMethodCallback callback) {
182+
mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_GENERIC_MOTION_EVENT, seq,
183+
event, callback));
184+
}
185+
170186
public void updateSelection(int oldSelStart, int oldSelEnd,
171187
int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
172188
mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION,

core/java/android/inputmethodservice/InputMethodService.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,7 +1773,7 @@ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
17731773
* Override this to intercept special key multiple events before they are
17741774
* processed by the
17751775
* application. If you return true, the application will not itself
1776-
* process the event. If you return true, the normal application processing
1776+
* process the event. If you return false, the normal application processing
17771777
* will occur as if the IME had not seen the event at all.
17781778
*
17791779
* <p>The default implementation always returns false, except when
@@ -1788,7 +1788,7 @@ public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
17881788
/**
17891789
* Override this to intercept key up events before they are processed by the
17901790
* application. If you return true, the application will not itself
1791-
* process the event. If you return true, the normal application processing
1791+
* process the event. If you return false, the normal application processing
17921792
* will occur as if the IME had not seen the event at all.
17931793
*
17941794
* <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
@@ -1806,8 +1806,29 @@ public boolean onKeyUp(int keyCode, KeyEvent event) {
18061806
return doMovementKey(keyCode, event, MOVEMENT_UP);
18071807
}
18081808

1809+
/**
1810+
* Override this to intercept trackball motion events before they are
1811+
* processed by the application.
1812+
* If you return true, the application will not itself process the event.
1813+
* If you return false, the normal application processing will occur as if
1814+
* the IME had not seen the event at all.
1815+
*/
18091816
@Override
18101817
public boolean onTrackballEvent(MotionEvent event) {
1818+
if (DEBUG) Log.v(TAG, "onTrackballEvent: " + event);
1819+
return false;
1820+
}
1821+
1822+
/**
1823+
* Override this to intercept generic motion events before they are
1824+
* processed by the application.
1825+
* If you return true, the application will not itself process the event.
1826+
* If you return false, the normal application processing will occur as if
1827+
* the IME had not seen the event at all.
1828+
*/
1829+
@Override
1830+
public boolean onGenericMotionEvent(MotionEvent event) {
1831+
if (DEBUG) Log.v(TAG, "onGenericMotionEvent(): event " + event);
18111832
return false;
18121833
}
18131834

core/java/android/view/InputEventConsistencyVerifier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ public void onTouchEvent(MotionEvent event, int nestingLevel) {
322322

323323
final int action = event.getAction();
324324
final boolean newStream = action == MotionEvent.ACTION_DOWN
325-
|| action == MotionEvent.ACTION_CANCEL;
325+
|| action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_OUTSIDE;
326326
if (newStream && (mTouchEventStreamIsTainted || mTouchEventStreamUnhandled)) {
327327
mTouchEventStreamIsTainted = false;
328328
mTouchEventStreamUnhandled = false;

0 commit comments

Comments
 (0)