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

Commit 995c043a authored by Adam Powell's avatar Adam Powell
Browse files

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
parent 39cb8895
Loading
Loading
Loading
Loading
+12 −1
Original line number Original line Diff line number Diff line
@@ -6389,6 +6389,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * @see ViewGroup#getTouchscreenBlocksFocus()
     * @see ViewGroup#getTouchscreenBlocksFocus()
     */
     */
    public boolean hasFocusable() {
    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()) {
        if (!isFocusableInTouchMode()) {
            for (ViewParent p = mParent; p instanceof ViewGroup; p = p.getParent()) {
            for (ViewParent p = mParent; p instanceof ViewGroup; p = p.getParent()) {
                final ViewGroup g = (ViewGroup) p;
                final ViewGroup g = (ViewGroup) p;
@@ -6397,7 +6405,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                }
                }
            }
            }
        }
        }
        return (mViewFlags & VISIBILITY_MASK) == VISIBLE && isFocusable();
        if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
            return false;
        }
        return allowAutoFocus ? getFocusable() != NOT_FOCUSABLE : getFocusable() == FOCUSABLE;
    }
    }
    /**
    /**
+5 −3
Original line number Original line Diff line number Diff line
@@ -1105,13 +1105,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        return null;
        return null;
    }
    }


    /** @hide Overriding hidden method */
    @Override
    @Override
    public boolean hasFocusable() {
    public boolean hasFocusable(boolean allowAutoFocus) {
        if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
        if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
            return false;
            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;
            return true;
        }
        }


@@ -1122,7 +1124,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager


            for (int i = 0; i < count; i++) {
            for (int i = 0; i < count; i++) {
                final View child = children[i];
                final View child = children[i];
                if (child.hasFocusable()) {
                if (child.hasFocusable(allowAutoFocus)) {
                    return true;
                    return true;
                }
                }
            }
            }
+4 −4
Original line number Original line Diff line number Diff line
@@ -2548,7 +2548,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
    }
    }


    private boolean isItemClickable(View view) {
    private boolean isItemClickable(View view) {
        return !view.hasFocusable();
        return !view.hasFocusable(false);
    }
    }


    /**
    /**
@@ -2824,7 +2824,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            final View v = getChildAt(mSelectedPosition - mFirstPosition);
            final View v = getChildAt(mSelectedPosition - mFirstPosition);


            if (v != null) {
            if (v != null) {
                if (v.hasFocusable()) return;
                if (v.hasFocusable(false)) return;
                v.setPressed(true);
                v.setPressed(true);
            }
            }
            setPressed(true);
            setPressed(true);
@@ -3428,7 +3428,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            if (mTouchMode == TOUCH_MODE_DOWN) {
            if (mTouchMode == TOUCH_MODE_DOWN) {
                mTouchMode = TOUCH_MODE_TAP;
                mTouchMode = TOUCH_MODE_TAP;
                final View child = getChildAt(mMotionPosition - mFirstPosition);
                final View child = getChildAt(mMotionPosition - mFirstPosition);
                if (child != null && !child.hasFocusable()) {
                if (child != null && !child.hasFocusable(false)) {
                    mLayoutMode = LAYOUT_NORMAL;
                    mLayoutMode = LAYOUT_NORMAL;


                    if (!mDataChanged) {
                    if (!mDataChanged) {
@@ -4005,7 +4005,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te


                final float x = ev.getX();
                final float x = ev.getX();
                final boolean inList = x > mListPadding.left && x < getWidth() - mListPadding.right;
                final boolean inList = x > mListPadding.left && x < getWidth() - mListPadding.right;
                if (inList && !child.hasFocusable()) {
                if (inList && !child.hasFocusable(false)) {
                    if (mPerformClick == null) {
                    if (mPerformClick == null) {
                        mPerformClick = new PerformClick();
                        mPerformClick = new PerformClick();
                    }
                    }