AllAppsRecyclerView.java revision a49b1f75cdf8efe88408173737a7b3c8f4b58a4c
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; 19fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winsonimport android.content.res.Resources; 20243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chungimport android.graphics.Canvas; 21fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winsonimport android.graphics.drawable.Drawable; 228f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chungimport android.os.Bundle; 2393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.support.v7.widget.RecyclerView; 2493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.util.AttributeSet; 25f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chungimport android.view.View; 26b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyal 275f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungimport com.android.launcher3.BaseRecyclerView; 28a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinsonimport com.android.launcher3.BubbleTextView; 295f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungimport com.android.launcher3.DeviceProfile; 30fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winsonimport com.android.launcher3.R; 318f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chungimport com.android.launcher3.Stats; 321ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chungimport com.android.launcher3.Utilities; 3393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 3493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport java.util.List; 3593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 3693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung/** 37ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song * A RecyclerView with custom fast scroll support for the all apps view. 3893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 398f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chungpublic class AllAppsRecyclerView extends BaseRecyclerView 408f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung implements Stats.LaunchSourceProvider { 4193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 4293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung private AlphabeticalAppsList mApps; 43c088049113c261331b5685e64050d14a31cd72dfWinson private AllAppsFastScrollHelper mFastScrollHelper; 44c088049113c261331b5685e64050d14a31cd72dfWinson private BaseRecyclerView.ScrollPositionState mScrollPosState = 45c088049113c261331b5685e64050d14a31cd72dfWinson new BaseRecyclerView.ScrollPositionState(); 4693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung private int mNumAppsPerRow; 47b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 48c088049113c261331b5685e64050d14a31cd72dfWinson // The specific icon heights that we use to calculate scroll 49c088049113c261331b5685e64050d14a31cd72dfWinson private int mPredictionIconHeight; 50c088049113c261331b5685e64050d14a31cd72dfWinson private int mIconHeight; 512e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen 52c088049113c261331b5685e64050d14a31cd72dfWinson // The empty-search result background 53fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson private AllAppsBackgroundDrawable mEmptySearchBackground; 54fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson private int mEmptySearchBackgroundTopOffset; 55fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 565f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public AllAppsRecyclerView(Context context) { 5793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung this(context, null); 5893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 5993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 605f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public AllAppsRecyclerView(Context context, AttributeSet attrs) { 6193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung this(context, attrs, 0); 6293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 6393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 645f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { 6593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung this(context, attrs, defStyleAttr, 0); 6693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 6793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 685f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, 6993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung int defStyleRes) { 7093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung super(context, attrs, defStyleAttr); 71fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson Resources res = getResources(); 72c088049113c261331b5685e64050d14a31cd72dfWinson addOnItemTouchListener(this); 73d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setDetachThumbOnFastScroll(); 74fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize( 75fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson R.dimen.all_apps_empty_search_bg_top_offset); 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; 83c088049113c261331b5685e64050d14a31cd72dfWinson mFastScrollHelper = new AllAppsFastScrollHelper(this, apps); 8493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 8593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 8693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 8793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * Sets the number of apps per row in this recycler view. 8893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 891cddad6c6bff0b271e8ebde44013a5a09f273436Winson Chung public void setNumAppsPerRow(DeviceProfile grid, int numAppsPerRow) { 90208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung mNumAppsPerRow = numAppsPerRow; 91da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung 92da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung RecyclerView.RecycledViewPool pool = getRecycledViewPool(); 93da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx); 945f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE, 1); 95bedf9232eb67a420f0372d3ca135ca13194e603bWinson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.SEARCH_MARKET_DIVIDER_VIEW_TYPE, 1); 96bedf9232eb67a420f0372d3ca135ca13194e603bWinson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE, 1); 975f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow); 981ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE, mNumAppsPerRow); 995f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows); 10093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 10193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 10293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 103c088049113c261331b5685e64050d14a31cd72dfWinson * Sets the heights of the icons in this view (for scroll calculations). 104c088049113c261331b5685e64050d14a31cd72dfWinson */ 105c088049113c261331b5685e64050d14a31cd72dfWinson public void setPremeasuredIconHeights(int predictionIconHeight, int iconHeight) { 106c088049113c261331b5685e64050d14a31cd72dfWinson mPredictionIconHeight = predictionIconHeight; 107c088049113c261331b5685e64050d14a31cd72dfWinson mIconHeight = iconHeight; 108c088049113c261331b5685e64050d14a31cd72dfWinson } 109c088049113c261331b5685e64050d14a31cd72dfWinson 110c088049113c261331b5685e64050d14a31cd72dfWinson /** 111ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung * Scrolls this recycler view to the top. 112ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung */ 113ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung public void scrollToTop() { 1142605900854a64eef49380332de17935d8204597dWinson // Ensure we reattach the scrollbar if it was previously detached while fast-scrolling 1152605900854a64eef49380332de17935d8204597dWinson if (mScrollbar.isThumbDetached()) { 1162605900854a64eef49380332de17935d8204597dWinson mScrollbar.reattachThumbToScroll(); 1172605900854a64eef49380332de17935d8204597dWinson } 118ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung scrollToPosition(0); 11932f14071cc5808737c88da7b5f3047a8222f8843Winson Chung } 12032f14071cc5808737c88da7b5f3047a8222f8843Winson Chung 12132f14071cc5808737c88da7b5f3047a8222f8843Winson Chung /** 122243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung * We need to override the draw to ensure that we don't draw the overscroll effect beyond the 123243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung * background bounds. 124243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung */ 125243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung @Override 126243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung protected void dispatchDraw(Canvas canvas) { 127c088049113c261331b5685e64050d14a31cd72dfWinson // Clip to ensure that we don't draw the overscroll effect beyond the background bounds 128243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung canvas.clipRect(mBackgroundPadding.left, mBackgroundPadding.top, 129243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung getWidth() - mBackgroundPadding.right, 130243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung getHeight() - mBackgroundPadding.bottom); 131243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung super.dispatchDraw(canvas); 132243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung } 133243fdd7cdf262b341b4f66177af27eec4f5cde45Winson Chung 13493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung @Override 135fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson public void onDraw(Canvas c) { 136fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson // Draw the background 137fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) { 138fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson c.clipRect(mBackgroundPadding.left, mBackgroundPadding.top, 139fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson getWidth() - mBackgroundPadding.right, 140fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson getHeight() - mBackgroundPadding.bottom); 141fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 142fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground.draw(c); 143fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 144fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 145fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson super.onDraw(c); 146fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 147fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 148fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson @Override 149fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson protected boolean verifyDrawable(Drawable who) { 150fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson return who == mEmptySearchBackground || super.verifyDrawable(who); 151fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 152fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 153fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson @Override 154fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson protected void onSizeChanged(int w, int h, int oldw, int oldh) { 155fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson updateEmptySearchBackgroundBounds(); 156fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 157fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 158fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson @Override 159a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson public void fillInLaunchSourceData(View v, Bundle sourceData) { 1608f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS); 1618f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung if (mApps.hasFilter()) { 1628f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, 1638f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung Stats.SUB_CONTAINER_ALL_APPS_SEARCH); 1648f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung } else { 165a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson if (v instanceof BubbleTextView) { 166a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson BubbleTextView icon = (BubbleTextView) v; 167a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson int position = getChildPosition(icon); 168a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson if (position != NO_POSITION) { 169a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); 170a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson AlphabeticalAppsList.AdapterItem item = items.get(position); 171a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson if (item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) { 172a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, 173a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson Stats.SUB_CONTAINER_ALL_APPS_PREDICTION); 174a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson return; 175a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson } 176a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson } 177a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson } 1788f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, 1798f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung Stats.SUB_CONTAINER_ALL_APPS_A_Z); 1808f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung } 1818f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung } 1828f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung 183fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson public void onSearchResultsChanged() { 184fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson // Always scroll the view to the top so the user can see the changed results 185fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson scrollToTop(); 186fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 187fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson if (mApps.hasNoFilteredResults()) { 188fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson if (mEmptySearchBackground == null) { 189fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground = new AllAppsBackgroundDrawable(getContext()); 190fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground.setAlpha(0); 191fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground.setCallback(this); 192fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson updateEmptySearchBackgroundBounds(); 193fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 194fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground.animateBgAlpha(1f, 150); 195fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } else if (mEmptySearchBackground != null) { 196fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson // For the time being, we just immediately hide the background to ensure that it does 197fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson // not overlap with the results 198fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground.setBgAlpha(0f); 199fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 200fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 201fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 20293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 20399d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung * Maps the touch (from 0..1) to the adapter position that should be visible. 20493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 205ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song @Override 206ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song public String scrollToPositionAtProgress(float touchFraction) { 207b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int rowCount = mApps.getNumAppRows(); 208b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (rowCount == 0) { 2099121fbffafe0b90a23768e5ebc5fffab2a2175c2Winson Chung return ""; 2109121fbffafe0b90a23768e5ebc5fffab2a2175c2Winson Chung } 21193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 212208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung // Stop the scroller if it is scrolling 213208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung stopScroll(); 214208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung 215b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Find the fastscroll section that maps to this touch fraction 216b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections = 217b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mApps.getFastScrollerSections(); 218b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung AlphabeticalAppsList.FastScrollSectionInfo lastInfo = fastScrollSections.get(0); 219c088049113c261331b5685e64050d14a31cd72dfWinson for (int i = 1; i < fastScrollSections.size(); i++) { 220c088049113c261331b5685e64050d14a31cd72dfWinson AlphabeticalAppsList.FastScrollSectionInfo info = fastScrollSections.get(i); 221c088049113c261331b5685e64050d14a31cd72dfWinson if (info.touchFraction > touchFraction) { 222c088049113c261331b5685e64050d14a31cd72dfWinson break; 223208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung } 224c088049113c261331b5685e64050d14a31cd72dfWinson lastInfo = info; 225208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung } 226208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung 227c088049113c261331b5685e64050d14a31cd72dfWinson // Update the fast scroll 228c088049113c261331b5685e64050d14a31cd72dfWinson int scrollY = getScrollTop(mScrollPosState); 229c088049113c261331b5685e64050d14a31cd72dfWinson int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows()); 230c088049113c261331b5685e64050d14a31cd72dfWinson mFastScrollHelper.smoothScrollToSection(scrollY, availableScrollHeight, lastInfo); 231b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return lastInfo.sectionName; 232ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 233ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 234b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung @Override 235b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung public void onFastScrollCompleted() { 236b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung super.onFastScrollCompleted(); 237c088049113c261331b5685e64050d14a31cd72dfWinson mFastScrollHelper.onFastScrollCompleted(); 238c088049113c261331b5685e64050d14a31cd72dfWinson } 239c088049113c261331b5685e64050d14a31cd72dfWinson 240c088049113c261331b5685e64050d14a31cd72dfWinson @Override 241c088049113c261331b5685e64050d14a31cd72dfWinson public void setAdapter(Adapter adapter) { 242c088049113c261331b5685e64050d14a31cd72dfWinson super.setAdapter(adapter); 243c088049113c261331b5685e64050d14a31cd72dfWinson mFastScrollHelper.onSetAdapter((AllAppsGridAdapter) adapter); 244ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 245ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 246f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung /** 247515f2897e876ba52ed6371d22e6d0f7d1016aaf3Winson Chung * Updates the bounds for the scrollbar. 248f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung */ 249ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song @Override 250d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson public void onUpdateScrollbar(int dy) { 25183f59abc9c566da5deb98afe7ea35cfb061f2920Winson Chung List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); 25283f59abc9c566da5deb98afe7ea35cfb061f2920Winson Chung 253ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung // Skip early if there are no items or we haven't been measured 254ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung if (items.isEmpty() || mNumAppsPerRow == 0) { 255d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setThumbOffset(-1, -1); 256f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung return; 257f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung } 258f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung 259f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung // Find the index and height of the first visible row (all rows have the same height) 260b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int rowCount = mApps.getNumAppRows(); 261c088049113c261331b5685e64050d14a31cd72dfWinson getCurScrollState(mScrollPosState, -1); 262b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (mScrollPosState.rowIndex < 0) { 263d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setThumbOffset(-1, -1); 264b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return; 265b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 266b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 267d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // Only show the scrollbar if there is height to be scrolled 268d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int availableScrollBarHeight = getAvailableScrollBarHeight(); 269c088049113c261331b5685e64050d14a31cd72dfWinson int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows()); 270d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (availableScrollHeight <= 0) { 271d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setThumbOffset(-1, -1); 272d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson return; 273d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 274d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson 275d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // Calculate the current scroll position, the scrollY of the recycler view accounts for the 276d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // view padding, while the scrollBarY is drawn right up to the background padding (ignoring 277d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // padding) 278c088049113c261331b5685e64050d14a31cd72dfWinson int scrollY = getScrollTop(mScrollPosState); 279d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int scrollBarY = mBackgroundPadding.top + 280d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight); 281d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson 282d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (mScrollbar.isThumbDetached()) { 283d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int scrollBarX; 284d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (Utilities.isRtl(getResources())) { 285d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson scrollBarX = mBackgroundPadding.left; 286d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } else { 287d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson scrollBarX = getWidth() - mBackgroundPadding.right - mScrollbar.getThumbWidth(); 288d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 289d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson 290d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (mScrollbar.isDraggingThumb()) { 291d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // If the thumb is detached, then just update the thumb to the current 292d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // touch position 293d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setThumbOffset(scrollBarX, (int) mScrollbar.getLastTouchY()); 294d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } else { 295d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int thumbScrollY = mScrollbar.getThumbOffset().y; 296d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int diffScrollY = scrollBarY - thumbScrollY; 297d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (diffScrollY * dy > 0f) { 298d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // User is scrolling in the same direction the thumb needs to catch up to the 29951a7d965443373e3a9f4fce1c0895669ed4110bdWinson // current scroll position. We do this by mapping the difference in movement 30051a7d965443373e3a9f4fce1c0895669ed4110bdWinson // from the original scroll bar position to the difference in movement necessary 30151a7d965443373e3a9f4fce1c0895669ed4110bdWinson // in the detached thumb position to ensure that both speed towards the same 30251a7d965443373e3a9f4fce1c0895669ed4110bdWinson // position at either end of the list. 30351a7d965443373e3a9f4fce1c0895669ed4110bdWinson if (dy < 0) { 30451a7d965443373e3a9f4fce1c0895669ed4110bdWinson int offset = (int) ((dy * thumbScrollY) / (float) scrollBarY); 30551a7d965443373e3a9f4fce1c0895669ed4110bdWinson thumbScrollY += Math.max(offset, diffScrollY); 30651a7d965443373e3a9f4fce1c0895669ed4110bdWinson } else { 30751a7d965443373e3a9f4fce1c0895669ed4110bdWinson int offset = (int) ((dy * (availableScrollBarHeight - thumbScrollY)) / 30851a7d965443373e3a9f4fce1c0895669ed4110bdWinson (float) (availableScrollBarHeight - scrollBarY)); 30951a7d965443373e3a9f4fce1c0895669ed4110bdWinson thumbScrollY += Math.min(offset, diffScrollY); 31051a7d965443373e3a9f4fce1c0895669ed4110bdWinson } 311d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson thumbScrollY = Math.max(0, Math.min(availableScrollBarHeight, thumbScrollY)); 312d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setThumbOffset(scrollBarX, thumbScrollY); 313d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (scrollBarY == thumbScrollY) { 314d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.reattachThumbToScroll(); 315d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 316d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } else { 317d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // User is scrolling in an opposite direction to the direction that the thumb 318d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // needs to catch up to the scroll position. Do nothing except for updating 319d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // the scroll bar x to match the thumb width. 320d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setThumbOffset(scrollBarX, thumbScrollY); 321d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 322d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 323d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } else { 324d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount); 325d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 326b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 327f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung 328b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung /** 3291ae7a5018b48dba562bc18821f0f1e778192ee85Winson Chung * Returns the current scroll state of the apps rows. 330ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung */ 331c088049113c261331b5685e64050d14a31cd72dfWinson protected void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask) { 332ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowIndex = -1; 333ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowTopOffset = -1; 334c088049113c261331b5685e64050d14a31cd72dfWinson stateOut.itemPos = -1; 335532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung 336ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung // Return early if there are no items or we haven't been measured 337d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); 338ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung if (items.isEmpty() || mNumAppsPerRow == 0) { 339532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung return; 340532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung } 341532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung 342ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung int childCount = getChildCount(); 343ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung for (int i = 0; i < childCount; i++) { 344ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung View child = getChildAt(i); 345ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung int position = getChildPosition(child); 346ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung if (position != NO_POSITION) { 347ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung AlphabeticalAppsList.AdapterItem item = items.get(position); 348c088049113c261331b5685e64050d14a31cd72dfWinson if ((item.viewType & viewTypeMask) != 0) { 349b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung stateOut.rowIndex = item.rowIndex; 350ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); 351c088049113c261331b5685e64050d14a31cd72dfWinson stateOut.itemPos = position; 352c088049113c261331b5685e64050d14a31cd72dfWinson return; 353ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 354ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 355ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 356c088049113c261331b5685e64050d14a31cd72dfWinson return; 357ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 358b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 359646c236ad283c44e41747176fe1017d619b92d5aWinson @Override 360646c236ad283c44e41747176fe1017d619b92d5aWinson protected boolean supportsFastScrolling() { 361646c236ad283c44e41747176fe1017d619b92d5aWinson // Only allow fast scrolling when the user is not searching, since the results are not 362646c236ad283c44e41747176fe1017d619b92d5aWinson // grouped in a meaningful order 363646c236ad283c44e41747176fe1017d619b92d5aWinson return !mApps.hasFilter(); 364646c236ad283c44e41747176fe1017d619b92d5aWinson } 365646c236ad283c44e41747176fe1017d619b92d5aWinson 366c088049113c261331b5685e64050d14a31cd72dfWinson protected int getTop(int rowIndex) { 367c088049113c261331b5685e64050d14a31cd72dfWinson if (getChildCount() == 0 || rowIndex <= 0) { 368b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return 0; 369b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 370c088049113c261331b5685e64050d14a31cd72dfWinson 371c088049113c261331b5685e64050d14a31cd72dfWinson // The prediction bar icons have more padding, so account for that in the row offset 372c088049113c261331b5685e64050d14a31cd72dfWinson return mPredictionIconHeight + (rowIndex - 1) * mIconHeight; 373b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 374fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 375fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson /** 376fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson * Updates the bounds of the empty search background. 377fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson */ 378fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson private void updateEmptySearchBackgroundBounds() { 379fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson if (mEmptySearchBackground == null) { 380fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson return; 381fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 382fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 383fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson // Center the empty search background on this new view bounds 384fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson int x = (getMeasuredWidth() - mEmptySearchBackground.getIntrinsicWidth()) / 2; 385fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson int y = mEmptySearchBackgroundTopOffset; 386fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground.setBounds(x, y, 387fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson x + mEmptySearchBackground.getIntrinsicWidth(), 388fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson y + mEmptySearchBackground.getIntrinsicHeight()); 389fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 39093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung} 391