am d7f5a51b: Merge "IME support for trackball and generic motion events" into jb-mr1-dev

* commit 'd7f5a51baf2c46436dc5bac3807fb0d46cbff304':
  IME support for trackball and generic motion events
This commit is contained in:
Victoria Lease
2012-09-10 14:16:20 -07:00
committed by Android Git Automerger
10 changed files with 271 additions and 34 deletions

View File

@@ -10217,6 +10217,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);
}
@@ -10229,6 +10230,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();
@@ -26697,6 +26699,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[]);

View File

@@ -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>");
}
}

View File

@@ -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;
}
}

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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.
*
* <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

View File

@@ -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);