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}