Fix ListView click handling under new focus rules

ListView historically uses View#hasFocusable to change signifcant
behavior around the clickability of items: an item view with any
focusable children could not be clicked via an item click
listener. Many apps therefore have sub-views in list items that are
deliberately clickable, but not focusable. This comes up in cases like
overflow menu buttons on list items.

Now that we have auto-focusability triggered when a view is set as
clickable, the expectations of apps using this pattern have changed.

Create an overload of hasFocusable that optionally can filter out
auto-focusable views in its results. Have ListView use it to preserve
its previous behavior. This isn't public API for now, but perhaps it
should be if this pattern shows up in practice in places other than
ListView.

Bug: 34756767
Change-Id: Ie71ee6e388449f634b30f9162a8b3fa578e32db8
(cherry picked from commit 995c043acfcdb6032ed7dc79b0c0aeb78b4f3b0f)
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8358f08..4072f78 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6391,6 +6391,14 @@
      * @see ViewGroup#getTouchscreenBlocksFocus()
      */
     public boolean hasFocusable() {
+        return hasFocusable(true);
+    }
+
+    /**
+     * @hide pending determination of whether this should be public or not.
+     * Currently used for compatibility with old focusability expectations in ListView.
+     */
+    public boolean hasFocusable(boolean allowAutoFocus) {
         if (!isFocusableInTouchMode()) {
             for (ViewParent p = mParent; p instanceof ViewGroup; p = p.getParent()) {
                 final ViewGroup g = (ViewGroup) p;
@@ -6399,7 +6407,10 @@
                 }
             }
         }
-        return (mViewFlags & VISIBILITY_MASK) == VISIBLE && isFocusable();
+        if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
+            return false;
+        }
+        return allowAutoFocus ? getFocusable() != NOT_FOCUSABLE : getFocusable() == FOCUSABLE;
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f8a1c6b..0c0ea42 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1105,13 +1105,15 @@
         return null;
     }
 
+    /** @hide Overriding hidden method */
     @Override
-    public boolean hasFocusable() {
+    public boolean hasFocusable(boolean allowAutoFocus) {
         if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
             return false;
         }
 
-        if (isFocusable()) {
+        // TODO This should probably be super.hasFocusable, but that would change behavior
+        if (allowAutoFocus ? getFocusable() != NOT_FOCUSABLE : getFocusable() == FOCUSABLE) {
             return true;
         }
 
@@ -1122,7 +1124,7 @@
 
             for (int i = 0; i < count; i++) {
                 final View child = children[i];
-                if (child.hasFocusable()) {
+                if (child.hasFocusable(allowAutoFocus)) {
                     return true;
                 }
             }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 8cedb17..47c4cf38 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2548,7 +2548,7 @@
     }
 
     private boolean isItemClickable(View view) {
-        return !view.hasFocusable();
+        return !view.hasFocusable(false);
     }
 
     /**
@@ -2824,7 +2824,7 @@
             final View v = getChildAt(mSelectedPosition - mFirstPosition);
 
             if (v != null) {
-                if (v.hasFocusable()) return;
+                if (v.hasFocusable(false)) return;
                 v.setPressed(true);
             }
             setPressed(true);
@@ -3428,7 +3428,7 @@
             if (mTouchMode == TOUCH_MODE_DOWN) {
                 mTouchMode = TOUCH_MODE_TAP;
                 final View child = getChildAt(mMotionPosition - mFirstPosition);
-                if (child != null && !child.hasFocusable()) {
+                if (child != null && !child.hasFocusable(false)) {
                     mLayoutMode = LAYOUT_NORMAL;
 
                     if (!mDataChanged) {
@@ -4005,7 +4005,7 @@
 
                 final float x = ev.getX();
                 final boolean inList = x > mListPadding.left && x < getWidth() - mListPadding.right;
-                if (inList && !child.hasFocusable()) {
+                if (inList && !child.hasFocusable(false)) {
                     if (mPerformClick == null) {
                         mPerformClick = new PerformClick();
                     }