AllAppsRecyclerView.java revision 1cddad6c6bff0b271e8ebde44013a5a09f273436
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; 30b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyalimport com.android.launcher3.util.Thunk; 3193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 3293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport java.util.List; 3393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 3493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung/** 35ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song * A RecyclerView with custom fast scroll support for the all apps view. 3693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 378f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chungpublic class AllAppsRecyclerView extends BaseRecyclerView 388f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung implements Stats.LaunchSourceProvider { 3993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 40b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private static final int FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON = 0; 41b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private static final int FAST_SCROLL_MODE_FREE_SCROLL = 1; 42b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 43b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private static final int FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW = 0; 44b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private static final int FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_SECTIONS = 1; 45b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 4693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung private AlphabeticalAppsList mApps; 4793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung private int mNumAppsPerRow; 48208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung private int mPredictionBarHeight; 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); 7593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 7693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 7793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 7893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * Sets the list of apps in this view, used to determine the fastscroll position. 7993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 8093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung public void setApps(AlphabeticalAppsList apps) { 8193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung mApps = apps; 8293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 8393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 8493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 8593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * Sets the number of apps per row in this recycler view. 8693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 871cddad6c6bff0b271e8ebde44013a5a09f273436Winson Chung public void setNumAppsPerRow(DeviceProfile grid, int numAppsPerRow) { 88208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung mNumAppsPerRow = numAppsPerRow; 89da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung 90da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung RecyclerView.RecycledViewPool pool = getRecycledViewPool(); 91da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx); 925f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_BAR_SPACER_TYPE, 1); 935f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE, 1); 945f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow); 955f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows); 9693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 9793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 9893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 99208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung * Sets the prediction bar height. 100208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung */ 101208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung public void setPredictionBarHeight(int height) { 102208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung mPredictionBarHeight = height; 103208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung } 104208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung 105208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung /** 106ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung * Scrolls this recycler view to the top. 107ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung */ 108ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung public void scrollToTop() { 109ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung scrollToPosition(0); 11032f14071cc5808737c88da7b5f3047a8222f8843Winson Chung } 11132f14071cc5808737c88da7b5f3047a8222f8843Winson Chung 11232f14071cc5808737c88da7b5f3047a8222f8843Winson Chung /** 11332f14071cc5808737c88da7b5f3047a8222f8843Winson Chung * Returns the current scroll position. 11432f14071cc5808737c88da7b5f3047a8222f8843Winson Chung */ 11532f14071cc5808737c88da7b5f3047a8222f8843Winson Chung public int getScrollPosition() { 11632f14071cc5808737c88da7b5f3047a8222f8843Winson Chung List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); 117b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung getCurScrollState(mScrollPosState, items); 118b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mScrollPosState.rowIndex != -1) { 11932f14071cc5808737c88da7b5f3047a8222f8843Winson Chung int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; 120b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return getPaddingTop() + predictionBarHeight + 121b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung (mScrollPosState.rowIndex * mScrollPosState.rowHeight) - 122b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mScrollPosState.rowTopOffset; 12332f14071cc5808737c88da7b5f3047a8222f8843Winson Chung } 12432f14071cc5808737c88da7b5f3047a8222f8843Winson Chung return 0; 125ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 126ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung 127243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung /** 128243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung * We need to override the draw to ensure that we don't draw the overscroll effect beyond the 129243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung * background bounds. 130243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung */ 131243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung @Override 132243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung protected void dispatchDraw(Canvas canvas) { 133243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung canvas.clipRect(mBackgroundPadding.left, mBackgroundPadding.top, 134243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung getWidth() - mBackgroundPadding.right, 135243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung getHeight() - mBackgroundPadding.bottom); 136243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung super.dispatchDraw(canvas); 137243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung } 138243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung 13993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung @Override 14093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung protected void onFinishInflate() { 141ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song super.onFinishInflate(); 142ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung 143ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung // Bind event handlers 14493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung addOnItemTouchListener(this); 14593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 14693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 1478f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung @Override 1488f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung public void fillInLaunchSourceData(Bundle sourceData) { 1498f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS); 1508f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung if (mApps.hasFilter()) { 1518f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, 1528f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung Stats.SUB_CONTAINER_ALL_APPS_SEARCH); 1538f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung } else { 1548f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, 1558f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung Stats.SUB_CONTAINER_ALL_APPS_A_Z); 1568f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung } 1578f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung } 1588f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung 15993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 16099d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung * Maps the touch (from 0..1) to the adapter position that should be visible. 16193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 162ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song @Override 163ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song public String scrollToPositionAtProgress(float touchFraction) { 164b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int rowCount = mApps.getNumAppRows(); 165b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (rowCount == 0) { 1669121fbffafe0b90a23768e5ebc5fffab2a2175c2Winson Chung return ""; 1679121fbffafe0b90a23768e5ebc5fffab2a2175c2Winson Chung } 16893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 169208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung // Stop the scroller if it is scrolling 170208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung stopScroll(); 171208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung 172b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Find the fastscroll section that maps to this touch fraction 173b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections = 174b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mApps.getFastScrollerSections(); 175b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung AlphabeticalAppsList.FastScrollSectionInfo lastInfo = fastScrollSections.get(0); 176b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mScrollBarMode == FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW) { 177b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung for (int i = 1; i < fastScrollSections.size(); i++) { 178b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung AlphabeticalAppsList.FastScrollSectionInfo info = fastScrollSections.get(i); 179b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (info.touchFraction > touchFraction) { 180b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung break; 181b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 182b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung lastInfo = info; 183208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung } 184b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } else if (mScrollBarMode == FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_SECTIONS){ 185b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung lastInfo = fastScrollSections.get((int) (touchFraction * (fastScrollSections.size() - 1))); 186b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } else { 187b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung throw new RuntimeException("Unexpected scroll bar mode"); 188208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung } 189208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung 190b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Map the touch position back to the scroll of the recycler view 191b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung getCurScrollState(mScrollPosState, mApps.getAdapterItems()); 192b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; 193b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight, 194b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung predictionBarHeight); 195b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager(); 196b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) { 197b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction)); 19893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 19993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 200b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mPrevFastScrollFocusedPosition != lastInfo.fastScrollToItem.position) { 201b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mPrevFastScrollFocusedPosition = lastInfo.fastScrollToItem.position; 202208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung 203b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Reset the last focused view 204b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mLastFastScrollFocusedView != null) { 205b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView.setFastScrollFocused(false, true); 206b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView = null; 207b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 208f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung 209b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mFastScrollMode == FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON) { 210b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung smoothSnapToPosition(mPrevFastScrollFocusedPosition, mScrollPosState); 211b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } else if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) { 212b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition); 213b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (vh != null && 214b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung vh.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) { 215b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView = 216b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView; 217b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView.setFastScrollFocused(true, true); 218b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 219b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } else { 220b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung throw new RuntimeException("Unexpected fast scroll mode"); 221ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 222ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 223b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return lastInfo.sectionName; 224ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 225ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 226b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung @Override 227b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung public void onFastScrollCompleted() { 228b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung super.onFastScrollCompleted(); 229b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Reset and clean up the last focused view 230b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mLastFastScrollFocusedView != null) { 231b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView.setFastScrollFocused(false, true); 232b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView = null; 233ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 234b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mPrevFastScrollFocusedPosition = -1; 235ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 236ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 237f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung /** 238515f2897e876ba52ed6371d22e6d0f7d1016aaf3Winson Chung * Updates the bounds for the scrollbar. 239f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung */ 240ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song @Override 241b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung public void onUpdateScrollbar() { 24283f59abc9c566da5deb98afe7ea35cfb061f2920Winson Chung List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); 24383f59abc9c566da5deb98afe7ea35cfb061f2920Winson Chung 244ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung // Skip early if there are no items or we haven't been measured 245ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung if (items.isEmpty() || mNumAppsPerRow == 0) { 246b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mScrollbar.setScrollbarThumbOffset(-1, -1); 247f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung return; 248f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung } 249f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung 250f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung // Find the index and height of the first visible row (all rows have the same height) 251b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int rowCount = mApps.getNumAppRows(); 252b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung getCurScrollState(mScrollPosState, items); 253b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mScrollPosState.rowIndex < 0) { 254b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mScrollbar.setScrollbarThumbOffset(-1, -1); 255b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return; 256b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 257b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 2588c01f810ebcddeb2e9f3a6599c62f701e9c3cac9Winson Chung int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; 259b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount, predictionBarHeight); 260b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 261f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung 262b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung /** 263b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * This runnable runs a single frame of the smooth scroll animation and posts the next frame 264b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * if necessary. 265b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung */ 266b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyal @Thunk Runnable mSmoothSnapNextFrameRunnable = new Runnable() { 267b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung @Override 268b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung public void run() { 269b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mFastScrollFrameIndex < mFastScrollFrames.length) { 270b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung scrollBy(0, mFastScrollFrames[mFastScrollFrameIndex]); 271b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mFastScrollFrameIndex++; 272b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung postOnAnimation(mSmoothSnapNextFrameRunnable); 273b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } else { 274b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Animation completed, set the fast scroll state on the target view 275b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition); 276b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (vh != null && 277b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung vh.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView && 278b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView != vh.itemView) { 279b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView = 280b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView; 281b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mLastFastScrollFocusedView.setFastScrollFocused(true, true); 282b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 283f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung } 284f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung } 285b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung }; 286b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 287b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung /** 288b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * Smoothly snaps to a given position. We do this manually by calculating the keyframes 289b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * ourselves and animating the scroll on the recycler view. 290b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung */ 291b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private void smoothSnapToPosition(final int position, ScrollPositionState scrollPosState) { 292b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung removeCallbacks(mSmoothSnapNextFrameRunnable); 293b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 294b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Calculate the full animation from the current scroll position to the final scroll 295b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // position, and then run the animation for the duration. 296b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; 297b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int curScrollY = getPaddingTop() + predictionBarHeight + 298b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset; 299b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int newScrollY = getScrollAtPosition(position, scrollPosState.rowHeight); 300b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int numFrames = mFastScrollFrames.length; 301b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung for (int i = 0; i < numFrames; i++) { 302b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // TODO(winsonc): We can interpolate this as well. 303b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mFastScrollFrames[i] = (newScrollY - curScrollY) / numFrames; 304b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 305b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mFastScrollFrameIndex = 0; 306b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung postOnAnimation(mSmoothSnapNextFrameRunnable); 307f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung } 308ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung 309ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung /** 310b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * Returns the current scroll state of the apps rows, not including the prediction 311b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * bar. 312ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung */ 313ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung private void getCurScrollState(ScrollPositionState stateOut, 314ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung List<AlphabeticalAppsList.AdapterItem> items) { 315ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowIndex = -1; 316ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowTopOffset = -1; 317ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowHeight = -1; 318532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung 319ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung // Return early if there are no items or we haven't been measured 320ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung if (items.isEmpty() || mNumAppsPerRow == 0) { 321532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung return; 322532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung } 323532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung 324ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung int childCount = getChildCount(); 325ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung for (int i = 0; i < childCount; i++) { 326ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung View child = getChildAt(i); 327ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung int position = getChildPosition(child); 328ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung if (position != NO_POSITION) { 329ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung AlphabeticalAppsList.AdapterItem item = items.get(position); 3305f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) { 331b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung stateOut.rowIndex = item.rowIndex; 332ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); 333ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowHeight = child.getHeight(); 334ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung break; 335ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 336ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 337ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 338ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 339b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 340b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung /** 341b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * Returns the scrollY for the given position in the adapter. 342b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung */ 343b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung private int getScrollAtPosition(int position, int rowHeight) { 344b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position); 345b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) { 346b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; 347b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return getPaddingTop() + predictionBarHeight + item.rowIndex * rowHeight; 348b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } else { 349b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return 0; 350b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 351b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 35293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung} 353