AllAppsRecyclerView.java revision 2605900854a64eef49380332de17935d8204597d
193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung/* 293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * Copyright (C) 2015 The Android Open Source Project 393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * 493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * Licensed under the Apache License, Version 2.0 (the "License"); 593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * you may not use this file except in compliance with the License. 693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * You may obtain a copy of the License at 793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * 893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * http://www.apache.org/licenses/LICENSE-2.0 993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * 1093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * Unless required by applicable law or agreed to in writing, software 1193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * distributed under the License is distributed on an "AS IS" BASIS, 1293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * See the License for the specific language governing permissions and 1493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * limitations under the License. 1593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 165f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungpackage com.android.launcher3.allapps; 1793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 1893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.content.Context; 19243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chungimport android.graphics.Canvas; 208f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chungimport android.os.Bundle; 2124cf70092b8b0281df071891573642f56e34f9e5Winson Chungimport android.support.v7.widget.LinearLayoutManager; 2293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.support.v7.widget.RecyclerView; 2393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.util.AttributeSet; 24f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chungimport android.view.View; 25b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyal 265f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungimport com.android.launcher3.BaseRecyclerView; 27b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chungimport com.android.launcher3.BaseRecyclerViewFastScrollBar; 285f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungimport com.android.launcher3.DeviceProfile; 298f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chungimport com.android.launcher3.Stats; 301ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chungimport com.android.launcher3.Utilities; 31b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyalimport com.android.launcher3.util.Thunk; 3293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 3393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport java.util.List; 3493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 3593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung/** 36ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song * A RecyclerView with custom fast scroll support for the all apps view. 3793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 388f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chungpublic class AllAppsRecyclerView extends BaseRecyclerView 398f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung implements Stats.LaunchSourceProvider { 4093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 41b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private static final int FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON = 0; 42b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private static final int FAST_SCROLL_MODE_FREE_SCROLL = 1; 43b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 44b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private static final int FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW = 0; 45b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private static final int FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_SECTIONS = 1; 46b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 4793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung private AlphabeticalAppsList mApps; 4893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung private int mNumAppsPerRow; 49b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 50b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyal @Thunk BaseRecyclerViewFastScrollBar.FastScrollFocusableView mLastFastScrollFocusedView; 51b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyal @Thunk int mPrevFastScrollFocusedPosition; 52b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyal @Thunk int mFastScrollFrameIndex; 53b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyal @Thunk final int[] mFastScrollFrames = new int[10]; 54b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyal 55b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private final int mFastScrollMode = FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON; 56b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private final int mScrollBarMode = FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW; 5793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 58b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private ScrollPositionState mScrollPosState = new ScrollPositionState(); 592e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen 605f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public AllAppsRecyclerView(Context context) { 6193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung this(context, null); 6293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 6393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 645f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public AllAppsRecyclerView(Context context, AttributeSet attrs) { 6593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung this(context, attrs, 0); 6693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 6793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 685f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { 6993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung this(context, attrs, defStyleAttr, 0); 7093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 7193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 725f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, 7393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung int defStyleRes) { 7493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung super(context, attrs, defStyleAttr); 75d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setDetachThumbOnFastScroll(); 7693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 7793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 7893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 7993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * Sets the list of apps in this view, used to determine the fastscroll position. 8093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 8193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung public void setApps(AlphabeticalAppsList apps) { 8293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung mApps = apps; 8393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 8493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 8593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 8693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * Sets the number of apps per row in this recycler view. 8793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 881cddad6c6bff0b271e8ebde44013a5a09f273436Winson Chung public void setNumAppsPerRow(DeviceProfile grid, int numAppsPerRow) { 89208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung mNumAppsPerRow = numAppsPerRow; 90da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung 91da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung RecyclerView.RecycledViewPool pool = getRecycledViewPool(); 92da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx); 935f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE, 1); 94bedf9232eb67a420f0372d3ca135ca13194e603bWinson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.SEARCH_MARKET_DIVIDER_VIEW_TYPE, 1); 95bedf9232eb67a420f0372d3ca135ca13194e603bWinson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE, 1); 965f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow); 971ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE, mNumAppsPerRow); 985f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows); 9993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 10093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 10193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 102ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung * Scrolls this recycler view to the top. 103ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung */ 104ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung public void scrollToTop() { 1052605900854a64eef49380332de17935d8204597dWinson // Ensure we reattach the scrollbar if it was previously detached while fast-scrolling 1062605900854a64eef49380332de17935d8204597dWinson if (mScrollbar.isThumbDetached()) { 1072605900854a64eef49380332de17935d8204597dWinson mScrollbar.reattachThumbToScroll(); 1082605900854a64eef49380332de17935d8204597dWinson } 109ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung scrollToPosition(0); 11032f14071cc5808737c88da7b5f3047a8222f8843Winson Chung } 11132f14071cc5808737c88da7b5f3047a8222f8843Winson Chung 11232f14071cc5808737c88da7b5f3047a8222f8843Winson Chung /** 113243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung * We need to override the draw to ensure that we don't draw the overscroll effect beyond the 114243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung * background bounds. 115243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung */ 116243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung @Override 117243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung protected void dispatchDraw(Canvas canvas) { 118243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung canvas.clipRect(mBackgroundPadding.left, mBackgroundPadding.top, 119243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung getWidth() - mBackgroundPadding.right, 120243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung getHeight() - mBackgroundPadding.bottom); 121243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung super.dispatchDraw(canvas); 122243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung } 123243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung 12493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung @Override 12593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung protected void onFinishInflate() { 126ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song super.onFinishInflate(); 127ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung 128ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung // Bind event handlers 12993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung addOnItemTouchListener(this); 13093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 13193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 1328f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung @Override 1338f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung public void fillInLaunchSourceData(Bundle sourceData) { 1348f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS); 1358f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung if (mApps.hasFilter()) { 1368f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, 1378f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung Stats.SUB_CONTAINER_ALL_APPS_SEARCH); 1388f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung } else { 1398f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, 1408f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung Stats.SUB_CONTAINER_ALL_APPS_A_Z); 1418f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung } 1428f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung } 1438f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung 14493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 14599d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung * Maps the touch (from 0..1) to the adapter position that should be visible. 14693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 147ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song @Override 148ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song public String scrollToPositionAtProgress(float touchFraction) { 149b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int rowCount = mApps.getNumAppRows(); 150b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (rowCount == 0) { 1519121fbffafe0b90a23768e5ebc5fffab2a2175c2Winson Chung return ""; 1529121fbffafe0b90a23768e5ebc5fffab2a2175c2Winson Chung } 15393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 154208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung // Stop the scroller if it is scrolling 155208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung stopScroll(); 156208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung 157b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Find the fastscroll section that maps to this touch fraction 158b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections = 159b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mApps.getFastScrollerSections(); 160b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung AlphabeticalAppsList.FastScrollSectionInfo lastInfo = fastScrollSections.get(0); 161b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mScrollBarMode == FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW) { 162b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung for (int i = 1; i < fastScrollSections.size(); i++) { 163b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung AlphabeticalAppsList.FastScrollSectionInfo info = fastScrollSections.get(i); 164b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (info.touchFraction > touchFraction) { 165b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung break; 166b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 167b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung lastInfo = info; 168208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung } 169b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } else if (mScrollBarMode == FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_SECTIONS){ 170b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung lastInfo = fastScrollSections.get((int) (touchFraction * (fastScrollSections.size() - 1))); 171b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } else { 172b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung throw new RuntimeException("Unexpected scroll bar mode"); 173208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung } 174208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung 175b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Map the touch position back to the scroll of the recycler view 176d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson getCurScrollState(mScrollPosState); 177d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight); 178b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager(); 179b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) { 180b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction)); 18193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 18293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 183b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mPrevFastScrollFocusedPosition != lastInfo.fastScrollToItem.position) { 184b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mPrevFastScrollFocusedPosition = lastInfo.fastScrollToItem.position; 185208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung 186b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Reset the last focused view 187b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mLastFastScrollFocusedView != null) { 188b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView.setFastScrollFocused(false, true); 189b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView = null; 190b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 191f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung 192b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mFastScrollMode == FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON) { 193b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung smoothSnapToPosition(mPrevFastScrollFocusedPosition, mScrollPosState); 194b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } else if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) { 195b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition); 196b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (vh != null && 197b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung vh.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) { 198b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView = 199b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView; 200b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView.setFastScrollFocused(true, true); 201b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 202b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } else { 203b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung throw new RuntimeException("Unexpected fast scroll mode"); 204ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 205ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 206b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return lastInfo.sectionName; 207ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 208ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 209b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung @Override 210b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung public void onFastScrollCompleted() { 211b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung super.onFastScrollCompleted(); 212b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Reset and clean up the last focused view 213b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mLastFastScrollFocusedView != null) { 214b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView.setFastScrollFocused(false, true); 215b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView = null; 216ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 217b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mPrevFastScrollFocusedPosition = -1; 218ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 219ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 220f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung /** 221515f2897e876ba52ed6371d22e6d0f7d1016aaf3Winson Chung * Updates the bounds for the scrollbar. 222f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung */ 223ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song @Override 224d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson public void onUpdateScrollbar(int dy) { 22583f59abc9c566da5deb98afe7ea35cfb061f2920Winson Chung List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); 22683f59abc9c566da5deb98afe7ea35cfb061f2920Winson Chung 227ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung // Skip early if there are no items or we haven't been measured 228ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung if (items.isEmpty() || mNumAppsPerRow == 0) { 229d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setThumbOffset(-1, -1); 230f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung return; 231f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung } 232f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung 233f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung // Find the index and height of the first visible row (all rows have the same height) 234b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int rowCount = mApps.getNumAppRows(); 235d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson getCurScrollState(mScrollPosState); 236b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mScrollPosState.rowIndex < 0) { 237d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setThumbOffset(-1, -1); 238b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return; 239b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 240b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 241d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // Only show the scrollbar if there is height to be scrolled 242d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int availableScrollBarHeight = getAvailableScrollBarHeight(); 243d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows(), mScrollPosState.rowHeight); 244d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (availableScrollHeight <= 0) { 245d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setThumbOffset(-1, -1); 246d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson return; 247d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 248d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson 249d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // Calculate the current scroll position, the scrollY of the recycler view accounts for the 250d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // view padding, while the scrollBarY is drawn right up to the background padding (ignoring 251d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // padding) 252d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int scrollY = getPaddingTop() + 253d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson (mScrollPosState.rowIndex * mScrollPosState.rowHeight) - mScrollPosState.rowTopOffset; 254d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int scrollBarY = mBackgroundPadding.top + 255d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight); 256d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson 257d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (mScrollbar.isThumbDetached()) { 258d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int scrollBarX; 259d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (Utilities.isRtl(getResources())) { 260d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson scrollBarX = mBackgroundPadding.left; 261d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } else { 262d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson scrollBarX = getWidth() - mBackgroundPadding.right - mScrollbar.getThumbWidth(); 263d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 264d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson 265d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (mScrollbar.isDraggingThumb()) { 266d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // If the thumb is detached, then just update the thumb to the current 267d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // touch position 268d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setThumbOffset(scrollBarX, (int) mScrollbar.getLastTouchY()); 269d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } else { 270d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int thumbScrollY = mScrollbar.getThumbOffset().y; 271d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int diffScrollY = scrollBarY - thumbScrollY; 272d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (diffScrollY * dy > 0f) { 273d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // User is scrolling in the same direction the thumb needs to catch up to the 27451a7d965443373e3a9f4fce1c0895669ed4110bdWinson // current scroll position. We do this by mapping the difference in movement 27551a7d965443373e3a9f4fce1c0895669ed4110bdWinson // from the original scroll bar position to the difference in movement necessary 27651a7d965443373e3a9f4fce1c0895669ed4110bdWinson // in the detached thumb position to ensure that both speed towards the same 27751a7d965443373e3a9f4fce1c0895669ed4110bdWinson // position at either end of the list. 27851a7d965443373e3a9f4fce1c0895669ed4110bdWinson if (dy < 0) { 27951a7d965443373e3a9f4fce1c0895669ed4110bdWinson int offset = (int) ((dy * thumbScrollY) / (float) scrollBarY); 28051a7d965443373e3a9f4fce1c0895669ed4110bdWinson thumbScrollY += Math.max(offset, diffScrollY); 28151a7d965443373e3a9f4fce1c0895669ed4110bdWinson } else { 28251a7d965443373e3a9f4fce1c0895669ed4110bdWinson int offset = (int) ((dy * (availableScrollBarHeight - thumbScrollY)) / 28351a7d965443373e3a9f4fce1c0895669ed4110bdWinson (float) (availableScrollBarHeight - scrollBarY)); 28451a7d965443373e3a9f4fce1c0895669ed4110bdWinson thumbScrollY += Math.min(offset, diffScrollY); 28551a7d965443373e3a9f4fce1c0895669ed4110bdWinson } 286d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson thumbScrollY = Math.max(0, Math.min(availableScrollBarHeight, thumbScrollY)); 287d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setThumbOffset(scrollBarX, thumbScrollY); 288d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (scrollBarY == thumbScrollY) { 289d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.reattachThumbToScroll(); 290d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 291d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } else { 292d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // User is scrolling in an opposite direction to the direction that the thumb 293d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // needs to catch up to the scroll position. Do nothing except for updating 294d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // the scroll bar x to match the thumb width. 295d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setThumbOffset(scrollBarX, thumbScrollY); 296d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 297d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 298d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } else { 299d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount); 300d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 301b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 302f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung 303b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung /** 304b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * This runnable runs a single frame of the smooth scroll animation and posts the next frame 305b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * if necessary. 306b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung */ 307b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyal @Thunk Runnable mSmoothSnapNextFrameRunnable = new Runnable() { 308b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung @Override 309b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung public void run() { 310b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mFastScrollFrameIndex < mFastScrollFrames.length) { 311b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung scrollBy(0, mFastScrollFrames[mFastScrollFrameIndex]); 312b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mFastScrollFrameIndex++; 313b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung postOnAnimation(mSmoothSnapNextFrameRunnable); 314b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } else { 315b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Animation completed, set the fast scroll state on the target view 316b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition); 317b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (vh != null && 318b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung vh.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView && 319b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView != vh.itemView) { 320b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView = 321b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView; 322b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView.setFastScrollFocused(true, true); 323b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 324f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung } 325f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung } 326b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung }; 327b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 328b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung /** 329b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * Smoothly snaps to a given position. We do this manually by calculating the keyframes 330b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * ourselves and animating the scroll on the recycler view. 331b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung */ 332b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private void smoothSnapToPosition(final int position, ScrollPositionState scrollPosState) { 333b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung removeCallbacks(mSmoothSnapNextFrameRunnable); 334b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 335b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Calculate the full animation from the current scroll position to the final scroll 336b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // position, and then run the animation for the duration. 3371ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chung int curScrollY = getPaddingTop() + 338b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset; 339b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int newScrollY = getScrollAtPosition(position, scrollPosState.rowHeight); 340b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int numFrames = mFastScrollFrames.length; 341b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung for (int i = 0; i < numFrames; i++) { 342b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // TODO(winsonc): We can interpolate this as well. 343b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mFastScrollFrames[i] = (newScrollY - curScrollY) / numFrames; 344b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 345b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mFastScrollFrameIndex = 0; 346b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung postOnAnimation(mSmoothSnapNextFrameRunnable); 347f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung } 348ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung 349ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung /** 3501ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chung * Returns the current scroll state of the apps rows. 351ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung */ 352d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson protected void getCurScrollState(ScrollPositionState stateOut) { 353ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowIndex = -1; 354ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowTopOffset = -1; 355ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowHeight = -1; 356532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung 357ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung // Return early if there are no items or we haven't been measured 358d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); 359ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung if (items.isEmpty() || mNumAppsPerRow == 0) { 360532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung return; 361532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung } 362532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung 363ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung int childCount = getChildCount(); 364ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung for (int i = 0; i < childCount; i++) { 365ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung View child = getChildAt(i); 366ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung int position = getChildPosition(child); 367ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung if (position != NO_POSITION) { 368ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung AlphabeticalAppsList.AdapterItem item = items.get(position); 3691ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chung if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE || 3701ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chung item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) { 371b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung stateOut.rowIndex = item.rowIndex; 372ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); 373ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowHeight = child.getHeight(); 374ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung break; 375ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 376ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 377ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 378ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 379b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 380b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung /** 381b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * Returns the scrollY for the given position in the adapter. 382b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung */ 383b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private int getScrollAtPosition(int position, int rowHeight) { 384b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position); 3851ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chung if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE || 3861ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chung item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) { 3871ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chung int offset = item.rowIndex > 0 ? getPaddingTop() : 0; 3881ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chung return offset + item.rowIndex * rowHeight; 389b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } else { 390b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return 0; 391b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 392b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 39393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung} 394