AllAppsRecyclerView.java revision 6c46a6d324310bd2fc0ea223c9782ba6394512e7
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; 2293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.support.v7.widget.RecyclerView; 2393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.util.AttributeSet; 24b655b8850064cc4f8e59652b4e3bffc09090769aWinsonimport android.util.SparseIntArray; 25f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chungimport android.view.View; 26b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyal 275f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungimport com.android.launcher3.BaseRecyclerView; 28a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinsonimport com.android.launcher3.BubbleTextView; 295f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungimport com.android.launcher3.DeviceProfile; 304ebc3d070ffa5ffda469aebcece20efda52d2028Hyunyoung Songimport com.android.launcher3.Launcher; 31fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winsonimport com.android.launcher3.R; 326c46a6d324310bd2fc0ea223c9782ba6394512e7Sunny Goyalimport com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; 3355c6691e3a2e953dc9882ad0bc0a4c1ee2d42e67Tony Wickham 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 */ 3959a238095e82fd02355f4cb53abe01655a50b051Hyunyoung Songpublic class AllAppsRecyclerView extends BaseRecyclerView { 4093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 4193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung private AlphabeticalAppsList mApps; 42c088049113c261331b5685e64050d14a31cd72dfWinson private AllAppsFastScrollHelper mFastScrollHelper; 4393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung private int mNumAppsPerRow; 44b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 45b655b8850064cc4f8e59652b4e3bffc09090769aWinson // The specific view heights that we use to calculate scroll 46b655b8850064cc4f8e59652b4e3bffc09090769aWinson private SparseIntArray mViewHeights = new SparseIntArray(); 47b655b8850064cc4f8e59652b4e3bffc09090769aWinson private SparseIntArray mCachedScrollPositions = new SparseIntArray(); 482e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen 49c088049113c261331b5685e64050d14a31cd72dfWinson // The empty-search result background 50fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson private AllAppsBackgroundDrawable mEmptySearchBackground; 51fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson private int mEmptySearchBackgroundTopOffset; 52fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 530ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal private HeaderElevationController mElevationController; 540ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal 555f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public AllAppsRecyclerView(Context context) { 5693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung this(context, null); 5793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 5893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 595f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public AllAppsRecyclerView(Context context, AttributeSet attrs) { 6093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung this(context, attrs, 0); 6193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 6293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 635f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { 6493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung this(context, attrs, defStyleAttr, 0); 6593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 6693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 675f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, 6893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung int defStyleRes) { 6993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung super(context, attrs, defStyleAttr); 70fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson Resources res = getResources(); 71c088049113c261331b5685e64050d14a31cd72dfWinson addOnItemTouchListener(this); 72d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.setDetachThumbOnFastScroll(); 73fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize( 74fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson R.dimen.all_apps_empty_search_bg_top_offset); 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; 82c088049113c261331b5685e64050d14a31cd72dfWinson mFastScrollHelper = new AllAppsFastScrollHelper(this, apps); 8393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 8493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 850ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal public void setElevationController(HeaderElevationController elevationController) { 860ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal mElevationController = elevationController; 870ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal } 880ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal 8993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 9093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * Sets the number of apps per row in this recycler view. 9193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 921cddad6c6bff0b271e8ebde44013a5a09f273436Winson Chung public void setNumAppsPerRow(DeviceProfile grid, int numAppsPerRow) { 93208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung mNumAppsPerRow = numAppsPerRow; 94da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung 95da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung RecyclerView.RecycledViewPool pool = getRecycledViewPool(); 96da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx); 97c2a20ff0a87f58b2be66f067b0024db7ff5185d3Peter Schiller pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1); 98c2a20ff0a87f58b2be66f067b0024db7ff5185d3Peter Schiller pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER, 1); 99c2a20ff0a87f58b2be66f067b0024db7ff5185d3Peter Schiller pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, 1); 100c2a20ff0a87f58b2be66f067b0024db7ff5185d3Peter Schiller pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 1); 101c2a20ff0a87f58b2be66f067b0024db7ff5185d3Peter Schiller pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows * mNumAppsPerRow); 102c2a20ff0a87f58b2be66f067b0024db7ff5185d3Peter Schiller pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, mNumAppsPerRow); 103c2a20ff0a87f58b2be66f067b0024db7ff5185d3Peter Schiller pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER, 1); 10493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung } 10593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 10693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 107b655b8850064cc4f8e59652b4e3bffc09090769aWinson * Ensures that we can present a stable scrollbar for views of varying types by pre-measuring 108b655b8850064cc4f8e59652b4e3bffc09090769aWinson * all the different view types. 109c088049113c261331b5685e64050d14a31cd72dfWinson */ 110b655b8850064cc4f8e59652b4e3bffc09090769aWinson public void preMeasureViews(AllAppsGridAdapter adapter) { 111b655b8850064cc4f8e59652b4e3bffc09090769aWinson final int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec( 112b655b8850064cc4f8e59652b4e3bffc09090769aWinson getResources().getDisplayMetrics().widthPixels, View.MeasureSpec.AT_MOST); 113b655b8850064cc4f8e59652b4e3bffc09090769aWinson final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec( 114b655b8850064cc4f8e59652b4e3bffc09090769aWinson getResources().getDisplayMetrics().heightPixels, View.MeasureSpec.AT_MOST); 115b655b8850064cc4f8e59652b4e3bffc09090769aWinson 116b655b8850064cc4f8e59652b4e3bffc09090769aWinson // Icons 117b655b8850064cc4f8e59652b4e3bffc09090769aWinson BubbleTextView icon = (BubbleTextView) adapter.onCreateViewHolder(this, 1182a65020da135037b8ee9408e7efb992eef02a896Sunny Goyal AllAppsGridAdapter.VIEW_TYPE_ICON).itemView; 1191f06427266c0cb5de4561fc7c620ff542f625300Winson int iconHeight = icon.getLayoutParams().height; 1201f06427266c0cb5de4561fc7c620ff542f625300Winson mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, iconHeight); 1211f06427266c0cb5de4561fc7c620ff542f625300Winson mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, iconHeight); 122b655b8850064cc4f8e59652b4e3bffc09090769aWinson 123b655b8850064cc4f8e59652b4e3bffc09090769aWinson // Search divider 124b655b8850064cc4f8e59652b4e3bffc09090769aWinson View searchDivider = adapter.onCreateViewHolder(this, 1252a65020da135037b8ee9408e7efb992eef02a896Sunny Goyal AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER).itemView; 126b655b8850064cc4f8e59652b4e3bffc09090769aWinson searchDivider.measure(widthMeasureSpec, heightMeasureSpec); 127b655b8850064cc4f8e59652b4e3bffc09090769aWinson int searchDividerHeight = searchDivider.getMeasuredHeight(); 128b655b8850064cc4f8e59652b4e3bffc09090769aWinson mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER, searchDividerHeight); 129b655b8850064cc4f8e59652b4e3bffc09090769aWinson 130b655b8850064cc4f8e59652b4e3bffc09090769aWinson // Generic dividers 131b655b8850064cc4f8e59652b4e3bffc09090769aWinson View divider = adapter.onCreateViewHolder(this, 1322a65020da135037b8ee9408e7efb992eef02a896Sunny Goyal AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER).itemView; 133b655b8850064cc4f8e59652b4e3bffc09090769aWinson divider.measure(widthMeasureSpec, heightMeasureSpec); 134b655b8850064cc4f8e59652b4e3bffc09090769aWinson int dividerHeight = divider.getMeasuredHeight(); 135b655b8850064cc4f8e59652b4e3bffc09090769aWinson mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER, dividerHeight); 136b655b8850064cc4f8e59652b4e3bffc09090769aWinson mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, dividerHeight); 137b655b8850064cc4f8e59652b4e3bffc09090769aWinson 138b655b8850064cc4f8e59652b4e3bffc09090769aWinson // Search views 139b655b8850064cc4f8e59652b4e3bffc09090769aWinson View emptySearch = adapter.onCreateViewHolder(this, 1402a65020da135037b8ee9408e7efb992eef02a896Sunny Goyal AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH).itemView; 141b655b8850064cc4f8e59652b4e3bffc09090769aWinson emptySearch.measure(widthMeasureSpec, heightMeasureSpec); 142b655b8850064cc4f8e59652b4e3bffc09090769aWinson mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 143b655b8850064cc4f8e59652b4e3bffc09090769aWinson emptySearch.getMeasuredHeight()); 144b655b8850064cc4f8e59652b4e3bffc09090769aWinson View searchMarket = adapter.onCreateViewHolder(this, 1452a65020da135037b8ee9408e7efb992eef02a896Sunny Goyal AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET).itemView; 146b655b8850064cc4f8e59652b4e3bffc09090769aWinson searchMarket.measure(widthMeasureSpec, heightMeasureSpec); 147b655b8850064cc4f8e59652b4e3bffc09090769aWinson mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 148b655b8850064cc4f8e59652b4e3bffc09090769aWinson searchMarket.getMeasuredHeight()); 149c088049113c261331b5685e64050d14a31cd72dfWinson } 150c088049113c261331b5685e64050d14a31cd72dfWinson 151c088049113c261331b5685e64050d14a31cd72dfWinson /** 152ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung * Scrolls this recycler view to the top. 153ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung */ 154ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung public void scrollToTop() { 1552605900854a64eef49380332de17935d8204597dWinson // Ensure we reattach the scrollbar if it was previously detached while fast-scrolling 1562605900854a64eef49380332de17935d8204597dWinson if (mScrollbar.isThumbDetached()) { 1572605900854a64eef49380332de17935d8204597dWinson mScrollbar.reattachThumbToScroll(); 1582605900854a64eef49380332de17935d8204597dWinson } 159ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung scrollToPosition(0); 1600ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal if (mElevationController != null) { 1610ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal mElevationController.reset(); 1620ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal } 16332f14071cc5808737c88da7b5f3047a8222f8843Winson Chung } 16432f14071cc5808737c88da7b5f3047a8222f8843Winson Chung 16593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung @Override 166fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson public void onDraw(Canvas c) { 167fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson // Draw the background 168fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) { 169fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground.draw(c); 170fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 171fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 172fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson super.onDraw(c); 173fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 174fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 175fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson @Override 176fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson protected boolean verifyDrawable(Drawable who) { 177fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson return who == mEmptySearchBackground || super.verifyDrawable(who); 178fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 179fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 180fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson @Override 181fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson protected void onSizeChanged(int w, int h, int oldw, int oldh) { 182fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson updateEmptySearchBackgroundBounds(); 183fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 184fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 18559a238095e82fd02355f4cb53abe01655a50b051Hyunyoung Song public int getContainerType(View v) { 1868f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung if (mApps.hasFilter()) { 1876c46a6d324310bd2fc0ea223c9782ba6394512e7Sunny Goyal return ContainerType.SEARCHRESULT; 1888f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung } else { 189a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson if (v instanceof BubbleTextView) { 190a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson BubbleTextView icon = (BubbleTextView) v; 191a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson int position = getChildPosition(icon); 192a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson if (position != NO_POSITION) { 193a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); 194a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson AlphabeticalAppsList.AdapterItem item = items.get(position); 195c2a20ff0a87f58b2be66f067b0024db7ff5185d3Peter Schiller if (item.viewType == AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON) { 1966c46a6d324310bd2fc0ea223c9782ba6394512e7Sunny Goyal return ContainerType.PREDICTION; 197a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson } 198a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson } 199a49b1f75cdf8efe88408173737a7b3c8f4b58a4cWinson } 2006c46a6d324310bd2fc0ea223c9782ba6394512e7Sunny Goyal return ContainerType.ALLAPPS; 2018f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung } 2028f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung } 2038f1eff7b6cc8621888ee46605c32e601f80a890bWinson Chung 204fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson public void onSearchResultsChanged() { 205fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson // Always scroll the view to the top so the user can see the changed results 206fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson scrollToTop(); 207fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 208fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson if (mApps.hasNoFilteredResults()) { 209fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson if (mEmptySearchBackground == null) { 210fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground = new AllAppsBackgroundDrawable(getContext()); 211fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground.setAlpha(0); 212fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground.setCallback(this); 213fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson updateEmptySearchBackgroundBounds(); 214fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 215fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground.animateBgAlpha(1f, 150); 216fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } else if (mEmptySearchBackground != null) { 217fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson // For the time being, we just immediately hide the background to ensure that it does 218fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson // not overlap with the results 219fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground.setBgAlpha(0f); 220fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 221fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 222fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 22393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung /** 22499d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung * Maps the touch (from 0..1) to the adapter position that should be visible. 22593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */ 226ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song @Override 227ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song public String scrollToPositionAtProgress(float touchFraction) { 228b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int rowCount = mApps.getNumAppRows(); 229b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung if (rowCount == 0) { 2309121fbffafe0b90a23768e5ebc5fffab2a2175c2Winson Chung return ""; 2319121fbffafe0b90a23768e5ebc5fffab2a2175c2Winson Chung } 23293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung 233208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung // Stop the scroller if it is scrolling 234208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung stopScroll(); 235208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung 236b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Find the fastscroll section that maps to this touch fraction 237b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections = 238b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung mApps.getFastScrollerSections(); 239b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung AlphabeticalAppsList.FastScrollSectionInfo lastInfo = fastScrollSections.get(0); 240c088049113c261331b5685e64050d14a31cd72dfWinson for (int i = 1; i < fastScrollSections.size(); i++) { 241c088049113c261331b5685e64050d14a31cd72dfWinson AlphabeticalAppsList.FastScrollSectionInfo info = fastScrollSections.get(i); 242c088049113c261331b5685e64050d14a31cd72dfWinson if (info.touchFraction > touchFraction) { 243c088049113c261331b5685e64050d14a31cd72dfWinson break; 244208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung } 245c088049113c261331b5685e64050d14a31cd72dfWinson lastInfo = info; 246208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung } 247208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung 248c088049113c261331b5685e64050d14a31cd72dfWinson // Update the fast scroll 249b655b8850064cc4f8e59652b4e3bffc09090769aWinson int scrollY = getCurrentScrollY(); 250b655b8850064cc4f8e59652b4e3bffc09090769aWinson int availableScrollHeight = getAvailableScrollHeight(); 251c088049113c261331b5685e64050d14a31cd72dfWinson mFastScrollHelper.smoothScrollToSection(scrollY, availableScrollHeight, lastInfo); 252b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return lastInfo.sectionName; 253ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 254ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 255b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung @Override 256b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung public void onFastScrollCompleted() { 257b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung super.onFastScrollCompleted(); 258c088049113c261331b5685e64050d14a31cd72dfWinson mFastScrollHelper.onFastScrollCompleted(); 259c088049113c261331b5685e64050d14a31cd72dfWinson } 260c088049113c261331b5685e64050d14a31cd72dfWinson 261c088049113c261331b5685e64050d14a31cd72dfWinson @Override 262c088049113c261331b5685e64050d14a31cd72dfWinson public void setAdapter(Adapter adapter) { 263c088049113c261331b5685e64050d14a31cd72dfWinson super.setAdapter(adapter); 264b655b8850064cc4f8e59652b4e3bffc09090769aWinson adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { 265b655b8850064cc4f8e59652b4e3bffc09090769aWinson public void onChanged() { 266b655b8850064cc4f8e59652b4e3bffc09090769aWinson mCachedScrollPositions.clear(); 267b655b8850064cc4f8e59652b4e3bffc09090769aWinson } 268b655b8850064cc4f8e59652b4e3bffc09090769aWinson }); 269c088049113c261331b5685e64050d14a31cd72dfWinson mFastScrollHelper.onSetAdapter((AllAppsGridAdapter) adapter); 270ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 271ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 272f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung /** 273515f2897e876ba52ed6371d22e6d0f7d1016aaf3Winson Chung * Updates the bounds for the scrollbar. 274f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung */ 275ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song @Override 276d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson public void onUpdateScrollbar(int dy) { 27783f59abc9c566da5deb98afe7ea35cfb061f2920Winson Chung List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); 27883f59abc9c566da5deb98afe7ea35cfb061f2920Winson Chung 279ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung // Skip early if there are no items or we haven't been measured 280ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung if (items.isEmpty() || mNumAppsPerRow == 0) { 2815d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal mScrollbar.setThumbOffsetY(-1); 282f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung return; 283f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung } 284f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung 285b655b8850064cc4f8e59652b4e3bffc09090769aWinson // Skip early if, there no child laid out in the container. 286b655b8850064cc4f8e59652b4e3bffc09090769aWinson int scrollY = getCurrentScrollY(); 287b655b8850064cc4f8e59652b4e3bffc09090769aWinson if (scrollY < 0) { 2885d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal mScrollbar.setThumbOffsetY(-1); 289b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return; 290b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 291b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 292d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // Only show the scrollbar if there is height to be scrolled 293d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int availableScrollBarHeight = getAvailableScrollBarHeight(); 294b655b8850064cc4f8e59652b4e3bffc09090769aWinson int availableScrollHeight = getAvailableScrollHeight(); 295d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (availableScrollHeight <= 0) { 2965d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal mScrollbar.setThumbOffsetY(-1); 297d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson return; 298d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 299d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson 300d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (mScrollbar.isThumbDetached()) { 301e8eaa9c94c5bfb9f103b486b24213e7dd86e8429Winson if (!mScrollbar.isDraggingThumb()) { 302e8eaa9c94c5bfb9f103b486b24213e7dd86e8429Winson // Calculate the current scroll position, the scrollY of the recycler view accounts 303e8eaa9c94c5bfb9f103b486b24213e7dd86e8429Winson // for the view padding, while the scrollBarY is drawn right up to the background 304e8eaa9c94c5bfb9f103b486b24213e7dd86e8429Winson // padding (ignoring padding) 30500e106880d33d2fc173c38e6d9da98762ccee478Sunny Goyal int scrollBarY = (int) 30600e106880d33d2fc173c38e6d9da98762ccee478Sunny Goyal (((float) scrollY / availableScrollHeight) * availableScrollBarHeight); 307d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson 3085d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal int thumbScrollY = mScrollbar.getThumbOffsetY(); 309d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson int diffScrollY = scrollBarY - thumbScrollY; 310d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (diffScrollY * dy > 0f) { 311d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // User is scrolling in the same direction the thumb needs to catch up to the 31251a7d965443373e3a9f4fce1c0895669ed4110bdWinson // current scroll position. We do this by mapping the difference in movement 31351a7d965443373e3a9f4fce1c0895669ed4110bdWinson // from the original scroll bar position to the difference in movement necessary 31451a7d965443373e3a9f4fce1c0895669ed4110bdWinson // in the detached thumb position to ensure that both speed towards the same 31551a7d965443373e3a9f4fce1c0895669ed4110bdWinson // position at either end of the list. 31651a7d965443373e3a9f4fce1c0895669ed4110bdWinson if (dy < 0) { 31751a7d965443373e3a9f4fce1c0895669ed4110bdWinson int offset = (int) ((dy * thumbScrollY) / (float) scrollBarY); 31851a7d965443373e3a9f4fce1c0895669ed4110bdWinson thumbScrollY += Math.max(offset, diffScrollY); 31951a7d965443373e3a9f4fce1c0895669ed4110bdWinson } else { 32051a7d965443373e3a9f4fce1c0895669ed4110bdWinson int offset = (int) ((dy * (availableScrollBarHeight - thumbScrollY)) / 32151a7d965443373e3a9f4fce1c0895669ed4110bdWinson (float) (availableScrollBarHeight - scrollBarY)); 32251a7d965443373e3a9f4fce1c0895669ed4110bdWinson thumbScrollY += Math.min(offset, diffScrollY); 32351a7d965443373e3a9f4fce1c0895669ed4110bdWinson } 324d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson thumbScrollY = Math.max(0, Math.min(availableScrollBarHeight, thumbScrollY)); 3255d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal mScrollbar.setThumbOffsetY(thumbScrollY); 326d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson if (scrollBarY == thumbScrollY) { 327d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson mScrollbar.reattachThumbToScroll(); 328d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 329d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } else { 330d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // User is scrolling in an opposite direction to the direction that the thumb 331d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // needs to catch up to the scroll position. Do nothing except for updating 332d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson // the scroll bar x to match the thumb width. 3335d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal mScrollbar.setThumbOffsetY(thumbScrollY); 334d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 335d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 336d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } else { 337b655b8850064cc4f8e59652b4e3bffc09090769aWinson synchronizeScrollBarThumbOffsetToViewScroll(scrollY, availableScrollHeight); 338d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson } 339b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 340f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung 341b655b8850064cc4f8e59652b4e3bffc09090769aWinson @Override 342b655b8850064cc4f8e59652b4e3bffc09090769aWinson protected boolean supportsFastScrolling() { 343b655b8850064cc4f8e59652b4e3bffc09090769aWinson // Only allow fast scrolling when the user is not searching, since the results are not 344b655b8850064cc4f8e59652b4e3bffc09090769aWinson // grouped in a meaningful order 345b655b8850064cc4f8e59652b4e3bffc09090769aWinson return !mApps.hasFilter(); 346b655b8850064cc4f8e59652b4e3bffc09090769aWinson } 347532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung 348b655b8850064cc4f8e59652b4e3bffc09090769aWinson @Override 3492e22b5d2de066245f8c8f52343bb2a8b4e8eb563Peter Schiller public int getCurrentScrollY() { 350ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung // Return early if there are no items or we haven't been measured 351d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); 352b655b8850064cc4f8e59652b4e3bffc09090769aWinson if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) { 353b655b8850064cc4f8e59652b4e3bffc09090769aWinson return -1; 354b655b8850064cc4f8e59652b4e3bffc09090769aWinson } 355b655b8850064cc4f8e59652b4e3bffc09090769aWinson 356b655b8850064cc4f8e59652b4e3bffc09090769aWinson // Calculate the y and offset for the item 357b655b8850064cc4f8e59652b4e3bffc09090769aWinson View child = getChildAt(0); 358b655b8850064cc4f8e59652b4e3bffc09090769aWinson int position = getChildPosition(child); 359b655b8850064cc4f8e59652b4e3bffc09090769aWinson if (position == NO_POSITION) { 360b655b8850064cc4f8e59652b4e3bffc09090769aWinson return -1; 361532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung } 362b655b8850064cc4f8e59652b4e3bffc09090769aWinson return getCurrentScrollY(position, getLayoutManager().getDecoratedTop(child)); 363b655b8850064cc4f8e59652b4e3bffc09090769aWinson } 364532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung 365b655b8850064cc4f8e59652b4e3bffc09090769aWinson public int getCurrentScrollY(int position, int offset) { 366b655b8850064cc4f8e59652b4e3bffc09090769aWinson List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); 367b655b8850064cc4f8e59652b4e3bffc09090769aWinson AlphabeticalAppsList.AdapterItem posItem = position < items.size() ? 368b655b8850064cc4f8e59652b4e3bffc09090769aWinson items.get(position) : null; 369b655b8850064cc4f8e59652b4e3bffc09090769aWinson int y = mCachedScrollPositions.get(position, -1); 370b655b8850064cc4f8e59652b4e3bffc09090769aWinson if (y < 0) { 371b655b8850064cc4f8e59652b4e3bffc09090769aWinson y = 0; 372b655b8850064cc4f8e59652b4e3bffc09090769aWinson for (int i = 0; i < position; i++) { 373b655b8850064cc4f8e59652b4e3bffc09090769aWinson AlphabeticalAppsList.AdapterItem item = items.get(i); 374b655b8850064cc4f8e59652b4e3bffc09090769aWinson if (AllAppsGridAdapter.isIconViewType(item.viewType)) { 375b655b8850064cc4f8e59652b4e3bffc09090769aWinson // Break once we reach the desired row 376b655b8850064cc4f8e59652b4e3bffc09090769aWinson if (posItem != null && posItem.viewType == item.viewType && 377b655b8850064cc4f8e59652b4e3bffc09090769aWinson posItem.rowIndex == item.rowIndex) { 378b655b8850064cc4f8e59652b4e3bffc09090769aWinson break; 379b655b8850064cc4f8e59652b4e3bffc09090769aWinson } 380b655b8850064cc4f8e59652b4e3bffc09090769aWinson // Otherwise, only account for the first icon in the row since they are the same 381b655b8850064cc4f8e59652b4e3bffc09090769aWinson // size within a row 382b655b8850064cc4f8e59652b4e3bffc09090769aWinson if (item.rowAppIndex == 0) { 383b655b8850064cc4f8e59652b4e3bffc09090769aWinson y += mViewHeights.get(item.viewType, 0); 384b655b8850064cc4f8e59652b4e3bffc09090769aWinson } 385b655b8850064cc4f8e59652b4e3bffc09090769aWinson } else { 386b655b8850064cc4f8e59652b4e3bffc09090769aWinson // Rest of the views span the full width 387b655b8850064cc4f8e59652b4e3bffc09090769aWinson y += mViewHeights.get(item.viewType, 0); 388ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 389ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 390b655b8850064cc4f8e59652b4e3bffc09090769aWinson mCachedScrollPositions.put(position, y); 391ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung } 392b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 393b655b8850064cc4f8e59652b4e3bffc09090769aWinson return getPaddingTop() + y - offset; 394646c236ad283c44e41747176fe1017d619b92d5aWinson } 395646c236ad283c44e41747176fe1017d619b92d5aWinson 3964ebc3d070ffa5ffda469aebcece20efda52d2028Hyunyoung Song @Override 39700e106880d33d2fc173c38e6d9da98762ccee478Sunny Goyal protected int getScrollbarTrackHeight() { 39800e106880d33d2fc173c38e6d9da98762ccee478Sunny Goyal return super.getScrollbarTrackHeight() 3994ebc3d070ffa5ffda469aebcece20efda52d2028Hyunyoung Song - Launcher.getLauncher(getContext()).getDragLayer().getInsets().bottom; 4004ebc3d070ffa5ffda469aebcece20efda52d2028Hyunyoung Song } 4014ebc3d070ffa5ffda469aebcece20efda52d2028Hyunyoung Song 402b655b8850064cc4f8e59652b4e3bffc09090769aWinson /** 403b655b8850064cc4f8e59652b4e3bffc09090769aWinson * Returns the available scroll height: 404b655b8850064cc4f8e59652b4e3bffc09090769aWinson * AvailableScrollHeight = Total height of the all items - last page height 405b655b8850064cc4f8e59652b4e3bffc09090769aWinson */ 406b655b8850064cc4f8e59652b4e3bffc09090769aWinson @Override 407b655b8850064cc4f8e59652b4e3bffc09090769aWinson protected int getAvailableScrollHeight() { 408b655b8850064cc4f8e59652b4e3bffc09090769aWinson int paddedHeight = getCurrentScrollY(mApps.getAdapterItems().size(), 0); 409b655b8850064cc4f8e59652b4e3bffc09090769aWinson int totalHeight = paddedHeight + getPaddingBottom(); 41000e106880d33d2fc173c38e6d9da98762ccee478Sunny Goyal return totalHeight - getScrollbarTrackHeight(); 411b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 412fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 413fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson /** 414fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson * Updates the bounds of the empty search background. 415fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson */ 416fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson private void updateEmptySearchBackgroundBounds() { 417fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson if (mEmptySearchBackground == null) { 418fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson return; 419fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 420fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson 421fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson // Center the empty search background on this new view bounds 422fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson int x = (getMeasuredWidth() - mEmptySearchBackground.getIntrinsicWidth()) / 2; 423fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson int y = mEmptySearchBackgroundTopOffset; 424fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson mEmptySearchBackground.setBounds(x, y, 425fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson x + mEmptySearchBackground.getIntrinsicWidth(), 426fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson y + mEmptySearchBackground.getIntrinsicHeight()); 427fee206f2419a0ffa90abf4f9f3f9e0b6d0b74906Winson } 42893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung} 429