OSDN Git Service

Merge "Optimize focused input event dispatch in view root."
[android-x86/frameworks-base.git] / core / java / android / view / ViewRootImpl.java
index f9f955e..1ae69fe 100644 (file)
@@ -169,9 +169,6 @@ public final class ViewRootImpl implements ViewParent,
     int mSeq;
 
     View mView;
-    View mFocusedView;
-    View mRealFocusedView;  // this is not set to null in touch mode
-    View mOldFocusedView;
 
     View mAccessibilityFocusedHost;
     AccessibilityNodeInfo mAccessibilityFocusedVirtualView;
@@ -232,6 +229,13 @@ public final class ViewRootImpl implements ViewParent,
     int mLastSystemUiVisibility;
     int mClientWindowLayoutFlags;
 
+    /** @hide */
+    public static final int EVENT_NOT_HANDLED = 0;
+    /** @hide */
+    public static final int EVENT_HANDLED = 1;
+    /** @hide */
+    public static final int EVENT_IN_PROGRESS = 2;
+
     // Pool of queued input events.
     private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
     private QueuedInputEvent mQueuedInputEventPool;
@@ -269,7 +273,7 @@ public final class ViewRootImpl implements ViewParent,
 
     boolean mScrollMayChange;
     int mSoftInputMode;
-    View mLastScrolledFocus;
+    WeakReference<View> mLastScrolledFocus;
     int mScrollY;
     int mCurScrollY;
     Scroller mScroller;
@@ -1526,7 +1530,9 @@ public final class ViewRootImpl implements ViewParent,
                 } else if (!mSurface.isValid()) {
                     // If the surface has been removed, then reset the scroll
                     // positions.
-                    mLastScrolledFocus = null;
+                    if (mLastScrolledFocus != null) {
+                        mLastScrolledFocus.clear();
+                    }
                     mScrollY = mCurScrollY = 0;
                     if (mScroller != null) {
                         mScroller.abortAnimation();
@@ -1802,13 +1808,11 @@ public final class ViewRootImpl implements ViewParent,
             if (mView != null) {
                 if (!mView.hasFocus()) {
                     mView.requestFocus(View.FOCUS_FORWARD);
-                    mFocusedView = mRealFocusedView = mView.findFocus();
                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
-                            + mFocusedView);
+                            + mView.findFocus());
                 } else {
-                    mRealFocusedView = mView.findFocus();
                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
-                            + mRealFocusedView);
+                            + mView.findFocus());
                 }
             }
             if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0) {
@@ -2514,17 +2518,12 @@ public final class ViewRootImpl implements ViewParent,
             // requestChildRectangleOnScreen() call (in which case 'rectangle'
             // is non-null and we just want to scroll to whatever that
             // rectangle is).
-            View focus = mRealFocusedView;
-
-            // When in touch mode, focus points to the previously focused view,
-            // which may have been removed from the view hierarchy. The following
-            // line checks whether the view is still in our hierarchy.
-            if (focus == null || focus.mAttachInfo != mAttachInfo) {
-                mRealFocusedView = null;
+            View focus = mView.findFocus();
+            if (focus == null) {
                 return false;
             }
-
-            if (focus != mLastScrolledFocus) {
+            View lastScrolledFocus = (mLastScrolledFocus != null) ? mLastScrolledFocus.get() : null;
+            if (lastScrolledFocus != null && focus != lastScrolledFocus) {
                 // If the focus has changed, then ignore any requests to scroll
                 // to a rectangle; first we want to make sure the entire focus
                 // view is visible.
@@ -2533,8 +2532,7 @@ public final class ViewRootImpl implements ViewParent,
             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus
                     + " rectangle=" + rectangle + " ci=" + ci
                     + " vi=" + vi);
-            if (focus == mLastScrolledFocus && !mScrollMayChange
-                    && rectangle == null) {
+            if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) {
                 // Optimization: if the focus hasn't changed since last
                 // time, and no layout has happened, then just leave things
                 // as they are.
@@ -2544,7 +2542,7 @@ public final class ViewRootImpl implements ViewParent,
                 // We need to determine if the currently focused view is
                 // within the visible part of the window and, if not, apply
                 // a pan so it can be seen.
-                mLastScrolledFocus = focus;
+                mLastScrolledFocus = new WeakReference<View>(focus);
                 mScrollMayChange = false;
                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?");
                 // Try to find the rectangle from the focus view.
@@ -2671,33 +2669,19 @@ public final class ViewRootImpl implements ViewParent,
     }
 
     public void requestChildFocus(View child, View focused) {
-        checkThread();
-
         if (DEBUG_INPUT_RESIZE) {
             Log.v(TAG, "Request child focus: focus now " + focused);
         }
-
-        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, focused);
+        checkThread();
         scheduleTraversals();
-
-        mFocusedView = mRealFocusedView = focused;
     }
 
     public void clearChildFocus(View child) {
-        checkThread();
-
         if (DEBUG_INPUT_RESIZE) {
             Log.v(TAG, "Clearing child focus");
         }
-
-        mOldFocusedView = mFocusedView;
-
-        // Invoke the listener only if there is no view to take focus
-        if (focusSearch(null, View.FOCUS_FORWARD) == null) {
-            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, null);
-        }
-
-        mFocusedView = mRealFocusedView = null;
+        checkThread();
+        scheduleTraversals();
     }
 
     @Override
@@ -2714,14 +2698,13 @@ public final class ViewRootImpl implements ViewParent,
                 // the one case where will transfer focus away from the current one
                 // is if the current view is a view group that prefers to give focus
                 // to its children first AND the view is a descendant of it.
-                mFocusedView = mView.findFocus();
-                boolean descendantsHaveDibsOnFocus =
-                        (mFocusedView instanceof ViewGroup) &&
-                            (((ViewGroup) mFocusedView).getDescendantFocusability() ==
-                                    ViewGroup.FOCUS_AFTER_DESCENDANTS);
-                if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
-                    // If a view gets the focus, the listener will be invoked from requestChildFocus()
-                    v.requestFocus();
+                View focused = mView.findFocus();
+                if (focused instanceof ViewGroup) {
+                    ViewGroup group = (ViewGroup) focused;
+                    if (group.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS
+                            && isViewDescendantOf(v, focused)) {
+                        v.requestFocus();
+                    }
                 }
             }
         }
@@ -3199,7 +3182,6 @@ public final class ViewRootImpl implements ViewParent,
                 // set yet.
                 final View focused = mView.findFocus();
                 if (focused != null && !focused.isFocusableInTouchMode()) {
-
                     final ViewGroup ancestorToTakeFocus =
                             findAncestorToTakeFocusInTouchMode(focused);
                     if (ancestorToTakeFocus != null) {
@@ -3208,10 +3190,7 @@ public final class ViewRootImpl implements ViewParent,
                         return ancestorToTakeFocus.requestFocus();
                     } else {
                         // nothing appropriate to have focus in touch mode, clear it out
-                        mView.unFocus();
-                        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
-                        mFocusedView = null;
-                        mOldFocusedView = null;
+                        focused.unFocus();
                         return true;
                     }
                 }
@@ -3246,12 +3225,11 @@ public final class ViewRootImpl implements ViewParent,
     private boolean leaveTouchMode() {
         if (mView != null) {
             if (mView.hasFocus()) {
-                // i learned the hard way to not trust mFocusedView :)
-                mFocusedView = mView.findFocus();
-                if (!(mFocusedView instanceof ViewGroup)) {
+                View focusedView = mView.findFocus();
+                if (!(focusedView instanceof ViewGroup)) {
                     // some view has focus, let it keep it
                     return false;
-                } else if (((ViewGroup)mFocusedView).getDescendantFocusability() !=
+                } else if (((ViewGroup) focusedView).getDescendantFocusability() !=
                         ViewGroup.FOCUS_AFTER_DESCENDANTS) {
                     // some view group has focus, and doesn't prefer its children
                     // over itself for focus, so let them keep it.
@@ -3269,19 +3247,37 @@ public final class ViewRootImpl implements ViewParent,
         return false;
     }
 
-    private void deliverInputEvent(QueuedInputEvent q) {
+    private int deliverInputEvent(QueuedInputEvent q) {
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
         try {
             if (q.mEvent instanceof KeyEvent) {
-                deliverKeyEvent(q);
+                return deliverKeyEvent(q);
             } else {
                 final int source = q.mEvent.getSource();
                 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-                    deliverPointerEvent(q);
+                    return deliverPointerEvent(q);
                 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-                    deliverTrackballEvent(q);
+                    return deliverTrackballEvent(q);
+                } else {
+                    return deliverGenericMotionEvent(q);
+                }
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+    }
+
+    private int deliverInputEventPostIme(QueuedInputEvent q) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEventPostIme");
+        try {
+            if (q.mEvent instanceof KeyEvent) {
+                return deliverKeyEventPostIme(q);
+            } else {
+                final int source = q.mEvent.getSource();
+                if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+                    return deliverTrackballEventPostIme(q);
                 } else {
-                    deliverGenericMotionEvent(q);
+                    return deliverGenericMotionEventPostIme(q);
                 }
             }
         } finally {
@@ -3289,7 +3285,7 @@ public final class ViewRootImpl implements ViewParent,
         }
     }
 
-    private void deliverPointerEvent(QueuedInputEvent q) {
+    private int deliverPointerEvent(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent)q.mEvent;
         final boolean isTouchEvent = event.isTouchEvent();
         if (mInputEventConsistencyVerifier != null) {
@@ -3302,8 +3298,7 @@ public final class ViewRootImpl implements ViewParent,
 
         // If there is no view, then the event will not be handled.
         if (mView == null || !mAdded) {
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // Translate the pointer event for compatibility, if needed.
@@ -3336,16 +3331,10 @@ public final class ViewRootImpl implements ViewParent,
         if (MEASURE_LATENCY) {
             lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
         }
-        if (handled) {
-            finishInputEvent(q, true);
-            return;
-        }
-
-        // Pointer event was unhandled.
-        finishInputEvent(q, false);
+        return handled ? EVENT_HANDLED : EVENT_NOT_HANDLED;
     }
 
-    private void deliverTrackballEvent(QueuedInputEvent q) {
+    private int deliverTrackballEvent(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent)q.mEvent;
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
@@ -3364,24 +3353,25 @@ public final class ViewRootImpl implements ViewParent,
                     if (DEBUG_IMF)
                         Log.v(TAG, "Sending trackball event to IME: seq="
                                 + seq + " event=" + event);
-                    imm.dispatchTrackballEvent(mView.getContext(), seq, event,
+                    int result = imm.dispatchTrackballEvent(mView.getContext(), seq, event,
                             mInputMethodCallback);
-                    return;
+                    if (result != EVENT_NOT_HANDLED) {
+                        return result;
+                    }
                 }
             }
         }
 
         // Not dispatching to IME, continue with post IME actions.
-        deliverTrackballEventPostIme(q);
+        return deliverTrackballEventPostIme(q);
     }
 
-    private void deliverTrackballEventPostIme(QueuedInputEvent q) {
+    private int 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);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // Deliver the trackball event to the view.
@@ -3391,10 +3381,8 @@ public final class ViewRootImpl implements ViewParent,
             // event into a key event, touch mode will not exit, so we exit
             // touch mode here.
             ensureTouchMode(false);
-
-            finishInputEvent(q, true);
             mLastTrackballTime = Integer.MIN_VALUE;
-            return;
+            return EVENT_HANDLED;
         }
 
         // Translate the trackball event into DPAD keys and try to deliver those.
@@ -3502,10 +3490,10 @@ public final class ViewRootImpl implements ViewParent,
 
         // Unfortunately we can't tell whether the application consumed the keys, so
         // we always consider the trackball event handled.
-        finishInputEvent(q, true);
+        return EVENT_HANDLED;
     }
 
-    private void deliverGenericMotionEvent(QueuedInputEvent q) {
+    private int deliverGenericMotionEvent(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent)q.mEvent;
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
@@ -3523,18 +3511,20 @@ public final class ViewRootImpl implements ViewParent,
                     if (DEBUG_IMF)
                         Log.v(TAG, "Sending generic motion event to IME: seq="
                                 + seq + " event=" + event);
-                    imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
+                    int result = imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
                             mInputMethodCallback);
-                    return;
+                    if (result != EVENT_NOT_HANDLED) {
+                        return result;
+                    }
                 }
             }
         }
 
         // Not dispatching to IME, continue with post IME actions.
-        deliverGenericMotionEventPostIme(q);
+        return deliverGenericMotionEventPostIme(q);
     }
 
-    private void deliverGenericMotionEventPostIme(QueuedInputEvent q) {
+    private int deliverGenericMotionEventPostIme(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent) q.mEvent;
         final int source = event.getSource();
         final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
@@ -3548,8 +3538,7 @@ public final class ViewRootImpl implements ViewParent,
               //Convert TouchPad motion into a TrackBall event
               mSimulatedTrackball.updateTrackballDirection(this, event);
             }
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // Deliver the event to the view.
@@ -3560,22 +3549,21 @@ public final class ViewRootImpl implements ViewParent,
               //Convert TouchPad motion into a TrackBall event
               mSimulatedTrackball.updateTrackballDirection(this, event);
             }
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         if (isJoystick) {
             // Translate the joystick event into DPAD keys and try to deliver
             // those.
             updateJoystickDirection(event, true);
-            finishInputEvent(q, true);
-        } else if (isTouchPad) {
+            return EVENT_HANDLED;
+        }
+        if (isTouchPad) {
             //Convert TouchPad motion into a TrackBall event
             mSimulatedTrackball.updateTrackballDirection(this, event);
-            finishInputEvent(q, true);
-        } else {
-            finishInputEvent(q, false);
+            return EVENT_HANDLED;
         }
+        return EVENT_NOT_HANDLED;
     }
 
     private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
@@ -3719,7 +3707,7 @@ public final class ViewRootImpl implements ViewParent,
         return false;
     }
 
-    private void deliverKeyEvent(QueuedInputEvent q) {
+    private int deliverKeyEvent(QueuedInputEvent q) {
         final KeyEvent event = (KeyEvent)q.mEvent;
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onKeyEvent(event, 0);
@@ -3730,8 +3718,7 @@ public final class ViewRootImpl implements ViewParent,
 
             // Perform predispatching before the IME.
             if (mView.dispatchKeyEventPreIme(event)) {
-                finishInputEvent(q, true);
-                return;
+                return EVENT_HANDLED;
             }
 
             // Dispatch to the IME before propagating down the view hierarchy.
@@ -3742,81 +3729,30 @@ public final class ViewRootImpl implements ViewParent,
                     final int seq = event.getSequenceNumber();
                     if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
                             + seq + " event=" + event);
-                    imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
-                    return;
+                    int result = imm.dispatchKeyEvent(mView.getContext(), seq, event,
+                            mInputMethodCallback);
+                    if (result != EVENT_NOT_HANDLED) {
+                        return result;
+                    }
                 }
             }
         }
 
         // Not dispatching to IME, continue with post IME actions.
-        deliverKeyEventPostIme(q);
+        return deliverKeyEventPostIme(q);
     }
 
-    void handleImeFinishedEvent(int seq, boolean handled) {
-        final QueuedInputEvent q = mCurrentInputEvent;
-        if (q != null && q.mEvent.getSequenceNumber() == seq) {
-            if (DEBUG_IMF) {
-                Log.v(TAG, "IME finished event: seq=" + seq
-                        + " handled=" + handled + " event=" + q);
-            }
-            if (handled) {
-                finishInputEvent(q, true);
-            } else {
-                if (q.mEvent instanceof KeyEvent) {
-                    KeyEvent event = (KeyEvent)q.mEvent;
-                    if (event.getAction() != KeyEvent.ACTION_UP) {
-                        // If the window doesn't currently have input focus, then drop
-                        // this event.  This could be an event that came back from the
-                        // IME dispatch but the window has lost focus in the meantime.
-                        if (!mAttachInfo.mHasWindowFocus) {
-                            Slog.w(TAG, "Dropping event due to no window focus: " + event);
-                            finishInputEvent(q, true);
-                            return;
-                        }
-                    }
-                    deliverKeyEventPostIme(q);
-                } else {
-                    MotionEvent event = (MotionEvent)q.mEvent;
-                    if (event.getAction() != MotionEvent.ACTION_CANCEL
-                            && event.getAction() != MotionEvent.ACTION_UP) {
-                        // If the window doesn't currently have input focus, then drop
-                        // this event.  This could be an event that came back from the
-                        // IME dispatch but the window has lost focus in the meantime.
-                        if (!mAttachInfo.mHasWindowFocus) {
-                            Slog.w(TAG, "Dropping event due to no window focus: " + event);
-                            finishInputEvent(q, true);
-                            return;
-                        }
-                    }
-                    final int source = q.mEvent.getSource();
-                    if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-                        deliverTrackballEventPostIme(q);
-                    } else {
-                        deliverGenericMotionEventPostIme(q);
-                    }
-                }
-            }
-        } else {
-            if (DEBUG_IMF) {
-                Log.v(TAG, "IME finished event: seq=" + seq
-                        + " handled=" + handled + ", event not found!");
-            }
-        }
-    }
-
-    private void deliverKeyEventPostIme(QueuedInputEvent q) {
+    private int deliverKeyEventPostIme(QueuedInputEvent q) {
         final KeyEvent event = (KeyEvent)q.mEvent;
 
         // If the view went away, then the event will not be handled.
         if (mView == null || !mAdded) {
-            finishInputEvent(q, false);
-            return;
+            return EVENT_NOT_HANDLED;
         }
 
         // If the key's purpose is to exit touch mode then we consume it and consider it handled.
         if (checkForLeavingTouchModeAndConsume(event)) {
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         // Make sure the fallback event policy sees all keys that will be delivered to the
@@ -3825,8 +3761,7 @@ public final class ViewRootImpl implements ViewParent,
 
         // Deliver the key to the view hierarchy.
         if (mView.dispatchKeyEvent(event)) {
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         // If the Control modifier is held, try to interpret the key as a shortcut.
@@ -3835,15 +3770,13 @@ public final class ViewRootImpl implements ViewParent,
                 && event.getRepeatCount() == 0
                 && !KeyEvent.isModifierKey(event.getKeyCode())) {
             if (mView.dispatchKeyShortcutEvent(event)) {
-                finishInputEvent(q, true);
-                return;
+                return EVENT_HANDLED;
             }
         }
 
         // Apply the fallback event policy.
         if (mFallbackEventHandler.dispatchKeyEvent(event)) {
-            finishInputEvent(q, true);
-            return;
+            return EVENT_HANDLED;
         }
 
         // Handle automatic focus changes.
@@ -3896,22 +3829,20 @@ public final class ViewRootImpl implements ViewParent,
                         if (v.requestFocus(direction, mTempRect)) {
                             playSoundEffect(SoundEffectConstants
                                     .getContantForFocusDirection(direction));
-                            finishInputEvent(q, true);
-                            return;
+                            return EVENT_HANDLED;
                         }
                     }
 
                     // Give the focused view a last chance to handle the dpad key.
                     if (mView.dispatchUnhandledMove(focused, direction)) {
-                        finishInputEvent(q, true);
-                        return;
+                        return EVENT_HANDLED;
                     }
                 }
             }
         }
 
         // Key was unhandled.
-        finishInputEvent(q, false);
+        return EVENT_NOT_HANDLED;
     }
 
     /* drag/drop */
@@ -4432,7 +4363,11 @@ public final class ViewRootImpl implements ViewParent,
             mFirstPendingInputEvent = q.mNext;
             q.mNext = null;
             mCurrentInputEvent = q;
-            deliverInputEvent(q);
+
+            final int result = deliverInputEvent(q);
+            if (result != EVENT_IN_PROGRESS) {
+                finishCurrentInputEvent(result == EVENT_HANDLED);
+            }
         }
 
         // We are done processing all input events that we can process right now
@@ -4443,10 +4378,42 @@ public final class ViewRootImpl implements ViewParent,
         }
     }
 
-    private void finishInputEvent(QueuedInputEvent q, boolean handled) {
-        if (q != mCurrentInputEvent) {
-            throw new IllegalStateException("finished input event out of order");
+    void handleImeFinishedEvent(int seq, boolean handled) {
+        final QueuedInputEvent q = mCurrentInputEvent;
+        if (q != null && q.mEvent.getSequenceNumber() == seq) {
+            if (DEBUG_IMF) {
+                Log.v(TAG, "IME finished event: seq=" + seq
+                        + " handled=" + handled + " event=" + q);
+            }
+
+            if (!handled) {
+                // If the window doesn't currently have input focus, then drop
+                // this event.  This could be an event that came back from the
+                // IME dispatch but the window has lost focus in the meantime.
+                if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
+                    Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+                } else {
+                    final int result = deliverInputEventPostIme(q);
+                    if (result == EVENT_HANDLED) {
+                        handled = true;
+                    }
+                }
+            }
+            finishCurrentInputEvent(handled);
+
+            // Immediately start processing the next input event.
+            doProcessInputEvents();
+        } else {
+            if (DEBUG_IMF) {
+                Log.v(TAG, "IME finished event: seq=" + seq
+                        + " handled=" + handled + ", event not found!");
+            }
         }
+    }
+
+    private void finishCurrentInputEvent(boolean handled) {
+        final QueuedInputEvent q = mCurrentInputEvent;
+        mCurrentInputEvent = null;
 
         if (q.mReceiver != null) {
             q.mReceiver.finishInputEvent(q.mEvent, handled);
@@ -4455,10 +4422,18 @@ public final class ViewRootImpl implements ViewParent,
         }
 
         recycleQueuedInputEvent(q);
+    }
 
-        mCurrentInputEvent = null;
-        if (mFirstPendingInputEvent != null) {
-            scheduleProcessInputEvents();
+    private static boolean isTerminalInputEvent(InputEvent event) {
+        if (event instanceof KeyEvent) {
+            final KeyEvent keyEvent = (KeyEvent)event;
+            return keyEvent.getAction() == KeyEvent.ACTION_UP;
+        } else {
+            final MotionEvent motionEvent = (MotionEvent)event;
+            final int action = motionEvent.getAction();
+            return action == MotionEvent.ACTION_UP
+                    || action == MotionEvent.ACTION_CANCEL
+                    || action == MotionEvent.ACTION_HOVER_EXIT;
         }
     }