Send hover to all children under pointer.

Previously we only sent hover to the topmost child, but this doesn't
handle cases where multiple children are overlapped to achieve
certain special effects.  Now we send hover to all children until
one of them handles it.

Also moved the call to send the accessibility event into the
main dispatch function so that we can send the accessibility event
for all innermost hovered views even when setHovered() might
not be called.

Change-Id: I6fb8b974db44b594c441deafc012b8415afdfac7
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1148436..f70ca90 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2486,6 +2486,12 @@
     Rect mLocalDirtyRect;
 
     /**
+     * Set to true when the view is sending hover accessibility events because it
+     * is the innermost hovered view.
+     */
+    private boolean mSendingHoverAccessibilityEvents;
+
+    /**
      * Consistency verifier for debugging purposes.
      * @hide
      */
@@ -5200,6 +5206,21 @@
      * @return True if the event was handled by the view, false otherwise.
      */
     protected boolean dispatchHoverEvent(MotionEvent event) {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_HOVER_ENTER:
+                if (!hasHoveredChild() && !mSendingHoverAccessibilityEvents) {
+                    mSendingHoverAccessibilityEvents = true;
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+                }
+                break;
+            case MotionEvent.ACTION_HOVER_EXIT:
+                if (mSendingHoverAccessibilityEvents) {
+                    mSendingHoverAccessibilityEvents = false;
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+                }
+                break;
+        }
+
         if (mOnHoverListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                 && mOnHoverListener.onHover(this, event)) {
             return true;
@@ -5209,6 +5230,16 @@
     }
 
     /**
+     * Returns true if the view has a child to which it has recently sent
+     * {@link MotionEvent#ACTION_HOVER_ENTER}.  If this view is hovered and
+     * it does not have a hovered child, then it must be the innermost hovered view.
+     * @hide
+     */
+    protected boolean hasHoveredChild() {
+        return false;
+    }
+
+    /**
      * Dispatch a generic motion event to the view under the first pointer.
      * <p>
      * Do not call this method directly.
@@ -5840,13 +5871,7 @@
      * @see #onHoverChanged
      */
     public boolean onHoverEvent(MotionEvent event) {
-        final int viewFlags = mViewFlags;
-        if ((viewFlags & ENABLED_MASK) == DISABLED) {
-            return false;
-        }
-
-        if ((viewFlags & CLICKABLE) == CLICKABLE
-                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
+        if (isHoverable()) {
             switch (event.getAction()) {
                 case MotionEvent.ACTION_HOVER_ENTER:
                     setHovered(true);
@@ -5857,11 +5882,26 @@
             }
             return true;
         }
-
         return false;
     }
 
     /**
+     * Returns true if the view should handle {@link #onHoverEvent}
+     * by calling {@link #setHovered} to change its hovered state.
+     *
+     * @return True if the view is hoverable.
+     */
+    private boolean isHoverable() {
+        final int viewFlags = mViewFlags;
+        if ((viewFlags & ENABLED_MASK) == DISABLED) {
+            return false;
+        }
+
+        return (viewFlags & CLICKABLE) == CLICKABLE
+                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
+    }
+
+    /**
      * Returns true if the view is currently hovered.
      *
      * @return True if the view is currently hovered.
@@ -5918,8 +5958,6 @@
      * @see #setHovered
      */
     public void onHoverChanged(boolean hovered) {
-        sendAccessibilityEvent(hovered ? AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
-                : AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
     }
 
     /**