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

Commit 9d744c73 authored by Michael Wright's avatar Michael Wright
Browse files

Add ability to request unbuffered dispatching.

While in general input batching has a lot of benefits, some apps would prefer
the input system not batch at all. This is typically because they do some of
the batching as part of their own input processing, as is the case with Chrome.

Bug: 12973909
Change-Id: I2e0c2b4c51bacc06f88245c528aa8849e4c4dab2
parent 3fce73a8
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -31790,6 +31790,7 @@ package android.view {
    method public void requestLayout();
    method public boolean requestRectangleOnScreen(android.graphics.Rect);
    method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
    method public final void requestUnbufferedDispatch(android.view.MotionEvent);
    method public static int resolveSize(int, int);
    method public static int resolveSizeAndState(int, int, int);
    method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>);
+30 −0
Original line number Diff line number Diff line
@@ -9233,6 +9233,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return mTouchDelegate;
    }
    /**
     * Request unbuffered dispatch of the given stream of MotionEvents to this View.
     *
     * Until this View receives a corresponding {@link MotionEvent#ACTION_UP}, ask that the input
     * system not batch {@link MotionEvent}s but instead deliver them as soon as they're
     * available. This method should only be called for touch events.
     *
     * <p class="note">This api is not intended for most applications. Buffered dispatch
     * provides many of benefits, and just requesting unbuffered dispatch on most MotionEvent
     * streams will not improve your input latency. Side effects include: increased latency,
     * jittery scrolls and inability to take advantage of system resampling. Talk to your input
     * professional to see if {@link #requestUnbufferedDispatch(MotionEvent)} is right for
     * you.</p>
     */
    public final void requestUnbufferedDispatch(MotionEvent event) {
        final int action = event.getAction();
        if (mAttachInfo == null
                || action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_MOVE
                || !event.isTouchEvent()) {
            return;
        }
        mAttachInfo.mUnbufferedDispatchRequested = true;
    }
    /**
     * Set flags controlling behavior of this view.
     *
@@ -19739,6 +19763,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         */
        boolean mInTouchMode;
        /**
         * Indicates whether the view has requested unbuffered input dispatching for the current
         * event stream.
         */
        boolean mUnbufferedDispatchRequested;
        /**
         * Indicates that ViewAncestor should trigger a global layout change
         * the next time it performs a traversal
+55 −8
Original line number Diff line number Diff line
@@ -230,6 +230,7 @@ public final class ViewRootImpl implements ViewParent,
    QueuedInputEvent mPendingInputEventTail;
    int mPendingInputEventCount;
    boolean mProcessInputEventsScheduled;
    boolean mUnbufferedInputDispatch;
    String mPendingInputEventQueueLengthCounterName = "pq";

    InputStage mFirstInputStage;
@@ -1000,9 +1001,11 @@ public final class ViewRootImpl implements ViewParent,
            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
        }
    }

    void unscheduleTraversals() {
        if (mTraversalScheduled) {
@@ -2598,7 +2601,7 @@ public final class ViewRootImpl implements ViewParent,
        }

        final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
        final Rect bounds = mView.mAttachInfo.mTmpInvalRect;
        final Rect bounds = mAttachInfo.mTmpInvalRect;
        if (provider == null) {
            host.getBoundsOnScreen(bounds);
        } else if (mAccessibilityFocusedVirtualView != null) {
@@ -3876,6 +3879,18 @@ public final class ViewRootImpl implements ViewParent,
            }
        }

        @Override
        protected void onDeliverToNext(QueuedInputEvent q) {
            if (mUnbufferedInputDispatch
                    && q.mEvent instanceof MotionEvent
                    && ((MotionEvent)q.mEvent).isTouchEvent()
                    && isTerminalInputEvent(q.mEvent)) {
                mUnbufferedInputDispatch = false;
                scheduleConsumeBatchedInput();
            }
            super.onDeliverToNext(q);
        }

        private int processKeyEvent(QueuedInputEvent q) {
            final KeyEvent event = (KeyEvent)q.mEvent;

@@ -3988,10 +4003,15 @@ public final class ViewRootImpl implements ViewParent,
        private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;

            if (mView.dispatchPointerEvent(event)) {
                return FINISH_HANDLED;
            mAttachInfo.mUnbufferedDispatchRequested = false;
            boolean handled = mView.dispatchPointerEvent(event);
            if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
                mUnbufferedInputDispatch = true;
                if (mConsumeBatchedInputScheduled) {
                    scheduleConsumeBatchedInputImmediately();
                }
            return FORWARD;
            }
            return handled ? FINISH_HANDLED : FORWARD;
        }

        private int processTrackballEvent(QueuedInputEvent q) {
@@ -5256,6 +5276,8 @@ public final class ViewRootImpl implements ViewParent,
                writer.print(" mRemoved="); writer.println(mRemoved);
        writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled=");
                writer.println(mConsumeBatchedInputScheduled);
        writer.print(innerPrefix); writer.print("mConsumeBatchedInputImmediatelyScheduled=");
                writer.println(mConsumeBatchedInputImmediatelyScheduled);
        writer.print(innerPrefix); writer.print("mPendingInputEventCount=");
                writer.println(mPendingInputEventCount);
        writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled=");
@@ -5635,6 +5657,7 @@ public final class ViewRootImpl implements ViewParent,
    private void finishInputEvent(QueuedInputEvent q) {
        Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
                q.mEvent.getSequenceNumber());

        if (q.mReceiver != null) {
            boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
            q.mReceiver.finishInputEvent(q.mEvent, handled);
@@ -5674,15 +5697,25 @@ public final class ViewRootImpl implements ViewParent,
        }
    }

    void scheduleConsumeBatchedInputImmediately() {
        if (!mConsumeBatchedInputImmediatelyScheduled) {
            unscheduleConsumeBatchedInput();
            mConsumeBatchedInputImmediatelyScheduled = true;
            mHandler.post(mConsumeBatchedInputImmediatelyRunnable);
        }
    }

    void doConsumeBatchedInput(long frameTimeNanos) {
        if (mConsumeBatchedInputScheduled) {
            mConsumeBatchedInputScheduled = false;
            if (mInputEventReceiver != null) {
                if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)) {
                if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)
                        && frameTimeNanos != -1) {
                    // If we consumed a batch here, we want to go ahead and schedule the
                    // consumption of batched input events on the next frame. Otherwise, we would
                    // wait until we have more input events pending and might get starved by other
                    // things occurring in the process.
                    // things occurring in the process. If the frame time is -1, however, then
                    // we're in a non-batching mode, so there's no need to schedule this.
                    scheduleConsumeBatchedInput();
                }
            }
@@ -5710,8 +5743,12 @@ public final class ViewRootImpl implements ViewParent,

        @Override
        public void onBatchedInputEventPending() {
            if (mUnbufferedInputDispatch) {
                super.onBatchedInputEventPending();
            } else {
                scheduleConsumeBatchedInput();
            }
        }

        @Override
        public void dispose() {
@@ -5731,6 +5768,16 @@ public final class ViewRootImpl implements ViewParent,
            new ConsumeBatchedInputRunnable();
    boolean mConsumeBatchedInputScheduled;

    final class ConsumeBatchedInputImmediatelyRunnable implements Runnable {
        @Override
        public void run() {
            doConsumeBatchedInput(-1);
        }
    }
    final ConsumeBatchedInputImmediatelyRunnable mConsumeBatchedInputImmediatelyRunnable =
            new ConsumeBatchedInputImmediatelyRunnable();
    boolean mConsumeBatchedInputImmediatelyScheduled;

    final class InvalidateOnAnimationRunnable implements Runnable {
        private boolean mPosted;
        private final ArrayList<View> mViews = new ArrayList<View>();