Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b38070ca authored by Victoria Lease's avatar Victoria Lease
Browse files

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
parent 37ee5342
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -10215,6 +10215,7 @@ package android.inputmethodservice {
    method public final android.os.IBinder onBind(android.content.Intent);
    method public final android.os.IBinder onBind(android.content.Intent);
    method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface();
    method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface();
    method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
    method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
    method public boolean onGenericMotionEvent(android.view.MotionEvent);
    method public boolean onTrackballEvent(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 {
  public abstract class AbstractInputMethodService.AbstractInputMethodSessionImpl implements android.view.inputmethod.InputMethodSession {
    ctor public AbstractInputMethodService.AbstractInputMethodSessionImpl();
    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 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 void dispatchTrackballEvent(int, android.view.MotionEvent, android.view.inputmethod.InputMethodSession.EventCallback);
    method public boolean isEnabled();
    method public boolean isEnabled();
@@ -26692,6 +26694,7 @@ package android.view.inputmethod {
  public abstract interface InputMethodSession {
  public abstract interface InputMethodSession {
    method public abstract void appPrivateCommand(java.lang.String, android.os.Bundle);
    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 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 dispatchTrackballEvent(int, android.view.MotionEvent, android.view.inputmethod.InputMethodSession.EventCallback);
    method public abstract void displayCompletions(android.view.inputmethod.CompletionInfo[]);
    method public abstract void displayCompletions(android.view.inputmethod.CompletionInfo[]);
+89 −17
Original line number Original line Diff line number Diff line
@@ -66,15 +66,54 @@ public class Input {
                }
                }
            } else if (command.equals("tap")) {
            } else if (command.equals("tap")) {
                if (args.length == 3) {
                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;
                    return;
                }
                }
            } else if (command.equals("swipe")) {
            } else if (command.equals("swipe")) {
                if (args.length == 5) {
                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]));
                            Float.parseFloat(args[3]), Float.parseFloat(args[4]));
                    return;
                    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 {
            } else {
                System.err.println("Error: Unknown command: " + command);
                System.err.println("Error: Unknown command: " + command);
                showUsage();
                showUsage();
@@ -127,33 +166,64 @@ public class Input {
                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
                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();
        long now = SystemClock.uptimeMillis();
        injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, x, y, 0));
        injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x, y, 1.0f);
        injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, x, y, 0));
        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;
        final int NUM_STEPS = 11;
        long now = SystemClock.uptimeMillis();
        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++) {
        for (int i = 1; i < NUM_STEPS; i++) {
            float alpha = (float) i / NUM_STEPS;
            float alpha = (float) i / NUM_STEPS;
            injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE,
            injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha),
                    lerp(x1, x2, alpha), lerp(y1, y2, alpha), 0));
                    lerp(y1, y2, alpha), 1.0f);
        }
        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x1, y1, 0.0f);
    }
    }
        injectPointerEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, x2, y2, 0));

    /**
     * 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) {
    private void injectKeyEvent(KeyEvent event) {
        Log.i(TAG, "InjectKeyEvent: " + event);
        Log.i(TAG, "injectKeyEvent: " + event);
        InputManager.getInstance().injectInputEvent(event,
        InputManager.getInstance().injectInputEvent(event,
                InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
                InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
    }
    }


    private void injectPointerEvent(MotionEvent event) {
    /**
        event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
     * Builds a MotionEvent and injects it into the event stream.
        Log.i("Input", "InjectPointerEvent: " + event);
     *
     * @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.getInstance().injectInputEvent(event,
                InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
                InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
    }
    }
@@ -166,7 +236,9 @@ public class Input {
        System.err.println("usage: input ...");
        System.err.println("usage: input ...");
        System.err.println("       input text <string>");
        System.err.println("       input text <string>");
        System.err.println("       input keyevent <key code number or name>");
        System.err.println("       input keyevent <key code number or name>");
        System.err.println("       input tap <x> <y>");
        System.err.println("       input [touchscreen|touchpad] tap <x> <y>");
        System.err.println("       input swipe <x1> <y1> <x2> <y2>");
        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>");
    }
    }
}
}
+29 −0
Original line number Original line Diff line number Diff line
@@ -149,6 +149,17 @@ public abstract class AbstractInputMethodService extends Service
                callback.finishedEvent(seq, handled);
                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);
        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) {
    public boolean onTrackballEvent(MotionEvent event) {
        return false;
        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;
    }
}
}
+16 −0
Original line number Original line Diff line number Diff line
@@ -43,6 +43,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
    private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
    private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
    private static final int DO_DISPATCH_KEY_EVENT = 70;
    private static final int DO_DISPATCH_KEY_EVENT = 70;
    private static final int DO_DISPATCH_TRACKBALL_EVENT = 80;
    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_SELECTION = 90;
    private static final int DO_UPDATE_CURSOR = 95;
    private static final int DO_UPDATE_CURSOR = 95;
    private static final int DO_APP_PRIVATE_COMMAND = 100;
    private static final int DO_APP_PRIVATE_COMMAND = 100;
@@ -109,6 +110,15 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
                args.recycle();
                args.recycle();
                return;
                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: {
            case DO_UPDATE_SELECTION: {
                SomeArgs args = (SomeArgs)msg.obj;
                SomeArgs args = (SomeArgs)msg.obj;
                mInputMethodSession.updateSelection(args.argi1, args.argi2,
                mInputMethodSession.updateSelection(args.argi1, args.argi2,
@@ -167,6 +177,12 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
                event, callback));
                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,
    public void updateSelection(int oldSelStart, int oldSelEnd,
            int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
            int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
        mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION,
        mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION,
+23 −2
Original line number Original line Diff line number Diff line
@@ -1773,7 +1773,7 @@ public class InputMethodService extends AbstractInputMethodService {
     * Override this to intercept special key multiple events before they are
     * Override this to intercept special key multiple events before they are
     * processed by the
     * processed by the
     * application.  If you return true, the application will not itself
     * 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.
     * will occur as if the IME had not seen the event at all.
     * 
     * 
     * <p>The default implementation always returns false, except when
     * <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
     * Override this to intercept key up events before they are processed by the
     * application.  If you return true, the application will not itself
     * 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.
     * will occur as if the IME had not seen the event at all.
     * 
     * 
     * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
     * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
@@ -1806,8 +1806,29 @@ public class InputMethodService extends AbstractInputMethodService {
        return doMovementKey(keyCode, event, MOVEMENT_UP);
        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
    @Override
    public boolean onTrackballEvent(MotionEvent event) {
    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;
        return false;
    }
    }


Loading