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

Commit fb4cbc4a authored by Alan Viverette's avatar Alan Viverette
Browse files

Postpone hover state resolution in DropDownListView

Prevents a HOVER_EXIT => TOUCH_DOWN transition from triggering two
Drawable.setState() calls in the same frame.

Bug: 25849904
Change-Id: Ib10f127fd68e433992b6b11e62ac2263d790ef52
parent 485a8d14
Loading
Loading
Loading
Loading
+63 −8
Original line number Diff line number Diff line
@@ -74,6 +74,12 @@ public class DropDownListView extends ListView {
    /** Helper for drag-to-open auto scrolling. */
    private AbsListViewAutoScroller mScrollHelper;

    /**
     * Runnable posted when we are awaiting hover event resolution. When set,
     * drawable state changes are postponed.
     */
    private ResolveHoverRunnable mResolveHoverRunnable;

    /**
     * Creates a new list view wrapper.
     *
@@ -100,19 +106,37 @@ public class DropDownListView extends ListView {
        return isHovered() || super.shouldShowSelector();
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mResolveHoverRunnable != null) {
            // Resolved hover event as hover => touch transition.
            mResolveHoverRunnable.cancel();
        }

        return super.onTouchEvent(ev);
    }

    @Override
    public boolean onHoverEvent(@NonNull MotionEvent ev) {
        final int action = ev.getActionMasked();
        if (action == MotionEvent.ACTION_HOVER_EXIT && mResolveHoverRunnable == null) {
            // This may be transitioning to TOUCH_DOWN. Postpone drawable state
            // updates until either the next frame or the next touch event.
            mResolveHoverRunnable = new ResolveHoverRunnable();
            mResolveHoverRunnable.post();
        }

        // Allow the super class to handle hover state management first.
        final boolean handled = super.onHoverEvent(ev);

        final int action = ev.getActionMasked();
        if (action == MotionEvent.ACTION_HOVER_ENTER
                || action == MotionEvent.ACTION_HOVER_MOVE) {
            final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
            if (position != INVALID_POSITION && position != mSelectedPosition) {
                final View hoveredItem = getChildAt(position - getFirstVisiblePosition());
                if (hoveredItem.isEnabled()) {
                    // Force a focus so that the proper selector state gets used when we update.
                    // Force a focus so that the proper selector state gets
                    // used when we update.
                    requestFocus();

                    positionSelector(position, hoveredItem);
@@ -122,7 +146,8 @@ public class DropDownListView extends ListView {
                updateSelectorState();
            }
        } else {
            // Do not cancel the selected position if the selection is visible by other reasons.
            // Do not cancel the selected position if the selection is visible
            // by other means.
            if (!super.shouldShowSelector()) {
                setSelectedPositionInt(INVALID_POSITION);
                setNextSelectedPositionInt(INVALID_POSITION);
@@ -132,6 +157,13 @@ public class DropDownListView extends ListView {
        return handled;
    }

    @Override
    protected void drawableStateChanged() {
        if (mResolveHoverRunnable == null) {
            super.drawableStateChanged();
        }
    }

    /**
     * Handles forwarded events.
     *
@@ -196,12 +228,14 @@ public class DropDownListView extends ListView {
    }

    /**
     * Sets whether the list selection is hidden, as part of a workaround for a touch mode issue
     * (see the declaration for mListSelectionHidden).
     * @param listSelectionHidden
     * Sets whether the list selection is hidden, as part of a workaround for a
     * touch mode issue (see the declaration for mListSelectionHidden).
     *
     * @param hideListSelection {@code true} to hide list selection,
     *                          {@code false} to show
     */
    public void setListSelectionHidden(boolean listSelectionHidden) {
        this.mListSelectionHidden = listSelectionHidden;
    public void setListSelectionHidden(boolean hideListSelection) {
        mListSelectionHidden = hideListSelection;
    }

    private void clearPressedItem() {
@@ -312,4 +346,25 @@ public class DropDownListView extends ListView {
    public boolean hasFocus() {
        return mHijackFocus || super.hasFocus();
    }

    /**
     * Runnable that forces hover event resolution and updates drawable state.
     */
    private class ResolveHoverRunnable implements Runnable {
        @Override
        public void run() {
            // Resolved hover event as standard hover exit.
            mResolveHoverRunnable = null;
            drawableStateChanged();
        }

        public void cancel() {
            mResolveHoverRunnable = null;
            removeCallbacks(this);
        }

        public void post() {
            DropDownListView.this.post(this);
        }
    }
}
 No newline at end of file