1f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg/*
2f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * Copyright (C) 2015 The Android Open Source Project
3f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg *
4f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * Licensed under the Apache License, Version 2.0 (the "License");
5f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * you may not use this file except in compliance with the License.
6f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * You may obtain a copy of the License at
7f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg *
8f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg *      http://www.apache.org/licenses/LICENSE-2.0
9f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg *
10f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * Unless required by applicable law or agreed to in writing, software
11f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * distributed under the License is distributed on an "AS IS" BASIS,
12f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * See the License for the specific language governing permissions and
14f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * limitations under the License.
15f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg */
16f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
17f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasbergpackage android.widget;
18f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
19f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
20f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasbergimport com.android.internal.widget.AutoScrollHelper.AbsListViewAutoScroller;
21f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
22be91ad5e21d917c3e9d5eff3bbd30d6df76c8213Alan Viveretteimport android.annotation.NonNull;
23f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasbergimport android.content.Context;
24f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasbergimport android.view.MotionEvent;
25f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasbergimport android.view.View;
26f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
27f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg/**
28f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * Wrapper class for a ListView. This wrapper can hijack the focus to
29f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * make sure the list uses the appropriate drawables and states when
30f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * displayed on screen within a drop down. The focus is never actually
31f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * passed to the drop down in this mode; the list only looks focused.
32f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg *
33f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg * @hide
34f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg */
35f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasbergpublic class DropDownListView extends ListView {
36f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    /*
37f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * WARNING: This is a workaround for a touch mode issue.
38f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *
39f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * Touch mode is propagated lazily to windows. This causes problems in
40f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * the following scenario:
41f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * - Type something in the AutoCompleteTextView and get some results
42f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * - Move down with the d-pad to select an item in the list
43f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * - Move up with the d-pad until the selection disappears
44f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * - Type more text in the AutoCompleteTextView *using the soft keyboard*
45f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *   and get new results; you are now in touch mode
46f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * - The selection comes back on the first item in the list, even though
47f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *   the list is supposed to be in touch mode
48f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *
49f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * Using the soft keyboard triggers the touch mode change but that change
50f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * is propagated to our window only after the first list layout, therefore
51f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * after the list attempts to resurrect the selection.
52f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *
53f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * The trick to work around this issue is to pretend the list is in touch
54f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * mode when we know that the selection should not appear, that is when
55f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * we know the user moved the selection away from the list.
56f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *
57f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * This boolean is set to true whenever we explicitly hide the list's
58f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * selection and reset to false whenever we know the user moved the
59f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * selection back to the list.
60f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *
61f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * When this boolean is true, isInTouchMode() returns true, otherwise it
62f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * returns super.isInTouchMode().
63f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     */
64f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    private boolean mListSelectionHidden;
65f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
66f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    /**
67f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * True if this wrapper should fake focus.
68f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     */
69f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    private boolean mHijackFocus;
70f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
71f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    /** Whether to force drawing of the pressed state selector. */
72f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    private boolean mDrawsInPressedState;
73f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
74f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    /** Helper for drag-to-open auto scrolling. */
75f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    private AbsListViewAutoScroller mScrollHelper;
76f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
77f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    /**
78fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette     * Runnable posted when we are awaiting hover event resolution. When set,
79fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette     * drawable state changes are postponed.
80fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette     */
81fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette    private ResolveHoverRunnable mResolveHoverRunnable;
82fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette
83fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette    /**
84f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * Creates a new list view wrapper.
85f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *
86f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * @param context this view's context
87f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     */
88be91ad5e21d917c3e9d5eff3bbd30d6df76c8213Alan Viverette    public DropDownListView(@NonNull Context context, boolean hijackFocus) {
89f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        this(context, hijackFocus, com.android.internal.R.attr.dropDownListViewStyle);
90f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    }
91f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
92f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    /**
93f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * Creates a new list view wrapper.
94f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *
95f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * @param context this view's context
96f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     */
97be91ad5e21d917c3e9d5eff3bbd30d6df76c8213Alan Viverette    public DropDownListView(@NonNull Context context, boolean hijackFocus, int defStyleAttr) {
98f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        super(context, null, defStyleAttr);
99f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        mHijackFocus = hijackFocus;
100f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        // TODO: Add an API to control this
101f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        setCacheColorHint(0); // Transparent, since the background drawable could be anything.
102f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    }
103f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
1048e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg    @Override
10500aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette    boolean shouldShowSelector() {
10600aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette        return isHovered() || super.shouldShowSelector();
1078e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg    }
1088e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg
1098e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg    @Override
110fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette    public boolean onTouchEvent(MotionEvent ev) {
111fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        if (mResolveHoverRunnable != null) {
112fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            // Resolved hover event as hover => touch transition.
113fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            mResolveHoverRunnable.cancel();
114fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        }
115fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette
116fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        return super.onTouchEvent(ev);
117fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette    }
118fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette
119fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette    @Override
120be91ad5e21d917c3e9d5eff3bbd30d6df76c8213Alan Viverette    public boolean onHoverEvent(@NonNull MotionEvent ev) {
121fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        final int action = ev.getActionMasked();
122fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        if (action == MotionEvent.ACTION_HOVER_EXIT && mResolveHoverRunnable == null) {
123fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            // This may be transitioning to TOUCH_DOWN. Postpone drawable state
124fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            // updates until either the next frame or the next touch event.
125fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            mResolveHoverRunnable = new ResolveHoverRunnable();
126fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            mResolveHoverRunnable.post();
127fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        }
128fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette
12900aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette        // Allow the super class to handle hover state management first.
13000aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette        final boolean handled = super.onHoverEvent(ev);
13100aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette
1328e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg        if (action == MotionEvent.ACTION_HOVER_ENTER
1338e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg                || action == MotionEvent.ACTION_HOVER_MOVE) {
1348e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg            final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
1358e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg            if (position != INVALID_POSITION && position != mSelectedPosition) {
1368e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg                final View hoveredItem = getChildAt(position - getFirstVisiblePosition());
1378e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg                if (hoveredItem.isEnabled()) {
138fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette                    // Force a focus so that the proper selector state gets
139fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette                    // used when we update.
1408e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg                    requestFocus();
1418e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg
1428e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg                    positionSelector(position, hoveredItem);
1438e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg                    setSelectedPositionInt(position);
1448e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg                    setNextSelectedPositionInt(position);
1458e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg                }
1468e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg                updateSelectorState();
1478e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg            }
1488e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg        } else {
149fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            // Do not cancel the selected position if the selection is visible
150fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            // by other means.
1518e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg            if (!super.shouldShowSelector()) {
1528e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg                setSelectedPositionInt(INVALID_POSITION);
15300aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette                setNextSelectedPositionInt(INVALID_POSITION);
1548e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg            }
1558e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg        }
1562ac975deb3d5833ad3daf7f4325f062d875b6036Alan Viverette
15700aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette        return handled;
1582ac975deb3d5833ad3daf7f4325f062d875b6036Alan Viverette    }
1592ac975deb3d5833ad3daf7f4325f062d875b6036Alan Viverette
160fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette    @Override
161fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette    protected void drawableStateChanged() {
162fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        if (mResolveHoverRunnable == null) {
163fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            super.drawableStateChanged();
164fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        }
165fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette    }
166fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette
167f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    /**
168f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * Handles forwarded events.
169f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *
170f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * @param activePointerId id of the pointer that activated forwarding
171f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * @return whether the event was handled
172f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     */
173be91ad5e21d917c3e9d5eff3bbd30d6df76c8213Alan Viverette    public boolean onForwardedEvent(@NonNull MotionEvent event, int activePointerId) {
174f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        boolean handledEvent = true;
175f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        boolean clearPressedItem = false;
176f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
177f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        final int actionMasked = event.getActionMasked();
178f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        switch (actionMasked) {
179f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            case MotionEvent.ACTION_CANCEL:
180f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                handledEvent = false;
181f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                break;
182f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            case MotionEvent.ACTION_UP:
183f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                handledEvent = false;
184f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                // $FALL-THROUGH$
185f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            case MotionEvent.ACTION_MOVE:
186f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                final int activeIndex = event.findPointerIndex(activePointerId);
187f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                if (activeIndex < 0) {
188f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                    handledEvent = false;
189f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                    break;
190f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                }
191f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
192f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                final int x = (int) event.getX(activeIndex);
193f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                final int y = (int) event.getY(activeIndex);
194f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                final int position = pointToPosition(x, y);
195f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                if (position == INVALID_POSITION) {
196f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                    clearPressedItem = true;
197f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                    break;
198f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                }
199f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
200f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                final View child = getChildAt(position - getFirstVisiblePosition());
201f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                setPressedItem(child, position, x, y);
202f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                handledEvent = true;
203f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
204f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                if (actionMasked == MotionEvent.ACTION_UP) {
205be91ad5e21d917c3e9d5eff3bbd30d6df76c8213Alan Viverette                    final long id = getItemIdAtPosition(position);
206be91ad5e21d917c3e9d5eff3bbd30d6df76c8213Alan Viverette                    performItemClick(child, position, id);
207f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                }
208f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                break;
209f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        }
210f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
211f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        // Failure to handle the event cancels forwarding.
212f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        if (!handledEvent || clearPressedItem) {
213f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            clearPressedItem();
214f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        }
215f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
216f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        // Manage automatic scrolling.
217f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        if (handledEvent) {
218f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            if (mScrollHelper == null) {
219f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg                mScrollHelper = new AbsListViewAutoScroller(this);
220f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            }
221f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            mScrollHelper.setEnabled(true);
222f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            mScrollHelper.onTouch(this, event);
223f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        } else if (mScrollHelper != null) {
224f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            mScrollHelper.setEnabled(false);
225f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        }
226f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
227f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        return handledEvent;
228f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    }
229f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
230f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    /**
231fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette     * Sets whether the list selection is hidden, as part of a workaround for a
232fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette     * touch mode issue (see the declaration for mListSelectionHidden).
233fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette     *
234fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette     * @param hideListSelection {@code true} to hide list selection,
235fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette     *                          {@code false} to show
236f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     */
237fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette    public void setListSelectionHidden(boolean hideListSelection) {
238fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        mListSelectionHidden = hideListSelection;
239f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    }
240f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
241f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    private void clearPressedItem() {
242f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        mDrawsInPressedState = false;
243f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        setPressed(false);
244f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        updateSelectorState();
245f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
246f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        final View motionView = getChildAt(mMotionPosition - mFirstPosition);
247f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        if (motionView != null) {
248f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            motionView.setPressed(false);
249f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        }
250f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    }
251f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
252be91ad5e21d917c3e9d5eff3bbd30d6df76c8213Alan Viverette    private void setPressedItem(@NonNull View child, int position, float x, float y) {
253f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        mDrawsInPressedState = true;
254f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
255f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        // Ordering is essential. First, update the container's pressed state.
256f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        drawableHotspotChanged(x, y);
257f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        if (!isPressed()) {
258f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            setPressed(true);
259f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        }
260f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
261f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        // Next, run layout if we need to stabilize child positions.
262f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        if (mDataChanged) {
263f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            layoutChildren();
264f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        }
265f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
266f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        // Manage the pressed view based on motion position. This allows us to
267f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        // play nicely with actual touch and scroll events.
268f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        final View motionView = getChildAt(mMotionPosition - mFirstPosition);
269f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        if (motionView != null && motionView != child && motionView.isPressed()) {
270f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            motionView.setPressed(false);
271f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        }
272f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        mMotionPosition = position;
273f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
274f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        // Offset for child coordinates.
275f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        final float childX = x - child.getLeft();
276f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        final float childY = y - child.getTop();
277f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        child.drawableHotspotChanged(childX, childY);
278f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        if (!child.isPressed()) {
279f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            child.setPressed(true);
280f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        }
281f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
282f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        // Ensure that keyboard focus starts from the last touched position.
283f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        setSelectedPositionInt(position);
284f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        positionSelectorLikeTouch(position, child, x, y);
285f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
286f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        // Refresh the drawable state to reflect the new pressed state,
287f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        // which will also update the selector state.
288f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        refreshDrawableState();
289f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    }
290f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
291f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    @Override
292f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    boolean touchModeDrawsInPressedState() {
293f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        return mDrawsInPressedState || super.touchModeDrawsInPressedState();
294f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    }
295f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
296f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    /**
297f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * Avoids jarring scrolling effect by ensuring that list elements
298f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * made of a text view fit on a single line.
299f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *
300f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * @param position the item index in the list to get a view for
301f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * @return the view for the specified item
302f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     */
303f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    @Override
304f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    View obtainView(int position, boolean[] isScrap) {
305f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        View view = super.obtainView(position, isScrap);
306f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
307f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        if (view instanceof TextView) {
308f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg            ((TextView) view).setHorizontallyScrolling(true);
309f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        }
310f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
311f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        return view;
312f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    }
313f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
314f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    @Override
315f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    public boolean isInTouchMode() {
316f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        // WARNING: Please read the comment where mListSelectionHidden is declared
317f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        return (mHijackFocus && mListSelectionHidden) || super.isInTouchMode();
318f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    }
319f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
320f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    /**
321f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * Returns the focus state in the drop down.
322f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *
323f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * @return true always if hijacking focus
324f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     */
325f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    @Override
326f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    public boolean hasWindowFocus() {
327f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        return mHijackFocus || super.hasWindowFocus();
328f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    }
329f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
330f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    /**
331f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * Returns the focus state in the drop down.
332f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *
333f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * @return true always if hijacking focus
334f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     */
335f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    @Override
336f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    public boolean isFocused() {
337f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        return mHijackFocus || super.isFocused();
338f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    }
339f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg
340f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    /**
341f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * Returns the focus state in the drop down.
342f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     *
343f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     * @return true always if hijacking focus
344f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg     */
345f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    @Override
346f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    public boolean hasFocus() {
347f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg        return mHijackFocus || super.hasFocus();
348f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg    }
349fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette
350fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette    /**
351fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette     * Runnable that forces hover event resolution and updates drawable state.
352fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette     */
353fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette    private class ResolveHoverRunnable implements Runnable {
354fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        @Override
355fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        public void run() {
356fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            // Resolved hover event as standard hover exit.
357fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            mResolveHoverRunnable = null;
358fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            drawableStateChanged();
359fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        }
360fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette
361fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        public void cancel() {
362fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            mResolveHoverRunnable = null;
363fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            removeCallbacks(this);
364fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        }
365fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette
366fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        public void post() {
367fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette            DropDownListView.this.post(this);
368fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette        }
369fb4cbc4a06b1ff6064c831cee8a0cded78df8442Alan Viverette    }
370f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg}