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

Commit ed218c70 authored by Mita Yun's avatar Mita Yun
Browse files

Use asynchronous messages for input method events.

Improves the throughput of IME event handling by ensuring that
input events do not get serialized behind UI traversal and
drawing messages such as when the UI is animating.

Added support for creating an asynchronous Handler as part of a
HandlerCaller.  It turns out we should be using an asynchronous
Handler not only in IME dispatch but also in accessibility and
wallpaper events where HandlerCaller is used.  So fixed those
services to also use an asynchronous Handler.

Change-Id: I0b19140c9d5ca6ee300c1a150c48312fd55ed8eb
parent c56a57f7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -556,7 +556,7 @@ public abstract class AccessibilityService extends Service {
        public IAccessibilityServiceClientWrapper(Context context, Looper looper,
                Callbacks callback) {
            mCallback = callback;
            mCaller = new HandlerCaller(context, looper, this);
            mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
        }

        public void setConnection(IAccessibilityServiceConnection connection, int connectionId) {
+2 −1
Original line number Diff line number Diff line
@@ -70,7 +70,8 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
    
    public IInputMethodSessionWrapper(Context context,
            InputMethodSession inputMethodSession) {
        mCaller = new HandlerCaller(context, this);
        mCaller = new HandlerCaller(context, null,
                this, true /*asyncHandler*/);
        mInputMethodSession = inputMethodSession;
    }

+2 −1
Original line number Diff line number Diff line
@@ -102,7 +102,8 @@ class IInputMethodWrapper extends IInputMethod.Stub
    public IInputMethodWrapper(AbstractInputMethodService context,
            InputMethod inputMethod) {
        mTarget = new WeakReference<AbstractInputMethodService>(context);
        mCaller = new HandlerCaller(context.getApplicationContext(), this);
        mCaller = new HandlerCaller(context.getApplicationContext(), null,
                this, true /*asyncHandler*/);
        mInputMethod = new WeakReference<InputMethod>(inputMethod);
        mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
    }
+1 −1
Original line number Diff line number Diff line
@@ -967,7 +967,7 @@ public abstract class WallpaperService extends Service {
            mCaller = new HandlerCaller(context,
                    mCallbackLooper != null
                            ? mCallbackLooper : context.getMainLooper(),
                    this);
                    this, true /*asyncHandler*/);
            mConnection = conn;
            mWindowToken = windowToken;
            mWindowType = windowType;
+38 −29
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@ class SimulatedTrackball {
    private static final int MAX_TAP_TIME = 250;
    // Where the cutoff is for determining an edge swipe
    private static final float EDGE_SWIPE_THRESHOLD = 0.9f;
    private static final int FLICK_MSG_ID = 313;
    private static final int MSG_FLICK = 313;
    // TODO: Pass touch slop from the input device
    private static final int TOUCH_SLOP = 30;

@@ -75,8 +75,11 @@ class SimulatedTrackball {
    // Has the TouchSlop constraint been invalidated
    private boolean mAlwaysInTapRegion = true;

    // Most recent event. Used to determine what device sent the event.
    private MotionEvent mRecentEvent;
    // Information from the most recent event.
    // Used to determine what device sent the event during a fling.
    private int mLastSource;
    private int mLastMetaState;
    private int mLastDeviceId;

    // TODO: Currently using screen dimensions tuned to a Galaxy Nexus, need to
    // read this from a config file instead
@@ -101,33 +104,34 @@ class SimulatedTrackball {
        mTouchSlopSquared = mTouchSlop * mTouchSlop;
    }

    private final Handler mHandler = new Handler(new Callback() {
    private final Handler mHandler = new Handler(true /*async*/) {
            @Override
        public boolean handleMessage(Message msg) {
            if (msg.what != FLICK_MSG_ID)
                return false;

        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_FLICK: {
                    final long time = SystemClock.uptimeMillis();
                    ViewRootImpl viewroot = (ViewRootImpl) msg.obj;
                    // Send the key
                    viewroot.enqueueInputEvent(new KeyEvent(time, time,
                    KeyEvent.ACTION_DOWN, msg.arg2, 0, mRecentEvent.getMetaState(),
                    mRecentEvent.getDeviceId(), 0,
                    KeyEvent.FLAG_FALLBACK, mRecentEvent.getSource()));
                            KeyEvent.ACTION_DOWN, msg.arg2, 0, mLastMetaState,
                            mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
                    viewroot.enqueueInputEvent(new KeyEvent(time, time,
                    KeyEvent.ACTION_UP, msg.arg2, 0, mRecentEvent.getMetaState(),
                    mRecentEvent.getDeviceId(), 0,
                    KeyEvent.FLAG_FALLBACK, mRecentEvent.getSource()));
                            KeyEvent.ACTION_UP, msg.arg2, 0, mLastMetaState,
                            mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));

                    // Increase the delay by the decay factor and resend
                    final int delay = (int) Math.ceil(mFlickDecay * msg.arg1);
                    if (delay <= mMaxRepeatDelay) {
                        Message msgCopy = Message.obtain(msg);
            // Increase the delay by the decay factor
            msgCopy.arg1 = (int) Math.ceil(mFlickDecay * msgCopy.arg1);
            if (msgCopy.arg1 <= mMaxRepeatDelay) {
                // Send the key again in arg1 milliseconds
                mHandler.sendMessageDelayed(msgCopy, msgCopy.arg1);
                        msgCopy.arg1 = delay;
                        msgCopy.setAsynchronous(true);
                        mHandler.sendMessageDelayed(msgCopy, delay);
                    }
                    break;
                }
            return false;
            }
    });
        }
    };

    public void updateTrackballDirection(ViewRootImpl viewroot, MotionEvent event) {
        // Store what time the touchpad event occurred
@@ -148,7 +152,7 @@ class SimulatedTrackball {
                    mEdgeSwipePossible = true;
                }
                // Clear any flings
                mHandler.removeMessages(FLICK_MSG_ID);
                mHandler.removeMessages(MSG_FLICK);

                break;
            case MotionEvent.ACTION_MOVE:
@@ -245,15 +249,20 @@ class SimulatedTrackball {
                    if (mMinFlickDistanceSquared <= xMoveSquared + yMoveSquared &&
                            time - mLastTouchPadEventTimeMs <= MAX_TAP_TIME &&
                            mKeySendRateMs <= mMaxRepeatDelay && mKeySendRateMs > 0) {
                        Message message = Message.obtain(mHandler, FLICK_MSG_ID,
                        mLastDeviceId = event.getDeviceId();
                        mLastSource = event.getSource();
                        mLastMetaState = event.getMetaState();

                        Message message = Message.obtain(mHandler, MSG_FLICK,
                                mKeySendRateMs, mLastKeySent, viewroot);
                        mRecentEvent = event;
                        message.setAsynchronous(true);
                        mHandler.sendMessageDelayed(message, mKeySendRateMs);
                    }
                }
                mEdgeSwipePossible = false;
                break;
        }

        // Store touch event position and time
        mLastTouchPadEventTimeMs = time;
        mLastTouchpadXPosition = event.getX();
Loading