AllAppsRecyclerView.java revision ac5f6af1488ec1cf0b73aa0848a675764c2f652b
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;
1993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.graphics.Rect;
2093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.graphics.drawable.Drawable;
2124cf70092b8b0281df071891573642f56e34f9e5Winson Chungimport android.support.v7.widget.LinearLayoutManager;
2293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.support.v7.widget.RecyclerView;
2393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.util.AttributeSet;
24f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chungimport android.view.View;
25ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song
265f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungimport com.android.launcher3.BaseRecyclerView;
275f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungimport com.android.launcher3.DeviceProfile;
285f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungimport com.android.launcher3.Launcher;
295f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungimport com.android.launcher3.Utilities;
3093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
3193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport java.util.List;
3293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
3393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung/**
34ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song * A RecyclerView with custom fast scroll support for the all apps view.
3593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */
365f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungpublic class AllAppsRecyclerView extends BaseRecyclerView {
3793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
3893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private AlphabeticalAppsList mApps;
3993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private int mNumAppsPerRow;
40208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung    private int mNumPredictedAppsPerRow;
4193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
42208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung    private int mPredictionBarHeight;
43f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung    private int mScrollbarMinHeight;
44ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song
45ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung    private Rect mBackgroundPadding = new Rect();
4693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
472e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen    private Launcher mLauncher;
482e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen
495f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung    public AllAppsRecyclerView(Context context) {
5093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        this(context, null);
5193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
5293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
535f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung    public AllAppsRecyclerView(Context context, AttributeSet attrs) {
5493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        this(context, attrs, 0);
5593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
5693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
575f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung    public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
5893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        this(context, attrs, defStyleAttr, 0);
5993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
6093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
615f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung    public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
6293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            int defStyleRes) {
6393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        super(context, attrs, defStyleAttr);
642e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen        mLauncher = (Launcher) context;
6593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
6693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
6793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
6893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * Sets the list of apps in this view, used to determine the fastscroll position.
6993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     */
7093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    public void setApps(AlphabeticalAppsList apps) {
7193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        mApps = apps;
7293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
7393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
7493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
7593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * Sets the number of apps per row in this recycler view.
7693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     */
77208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung    public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) {
78208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        mNumAppsPerRow = numAppsPerRow;
79208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        mNumPredictedAppsPerRow = numPredictedAppsPerRow;
80da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung
812e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen        DeviceProfile grid = mLauncher.getDeviceProfile();
82da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung        RecyclerView.RecycledViewPool pool = getRecycledViewPool();
83da25da3d557177febb9c7afb78e04b1ef4a45f17Winson Chung        int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
845f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung        pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_BAR_SPACER_TYPE, 1);
855f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung        pool.setMaxRecycledViews(AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE, 1);
865f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung        pool.setMaxRecycledViews(AllAppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow);
875f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung        pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows);
8893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
8993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
9012fb2cc8ec0dd915259beadacf91a131c6a090b9Sunny Goyal    public void updateBackgroundPadding(Drawable background) {
91ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung        background.getPadding(mBackgroundPadding);
92ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung    }
93ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung
9493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
95208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung     * Sets the prediction bar height.
96208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung     */
97208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung    public void setPredictionBarHeight(int height) {
98208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        mPredictionBarHeight = height;
99208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung    }
100208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung
101208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung    /**
102ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung     * Scrolls this recycler view to the top.
103ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung     */
104ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung    public void scrollToTop() {
105ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung        scrollToPosition(0);
10632f14071cc5808737c88da7b5f3047a8222f8843Winson Chung    }
10732f14071cc5808737c88da7b5f3047a8222f8843Winson Chung
10832f14071cc5808737c88da7b5f3047a8222f8843Winson Chung    /**
10932f14071cc5808737c88da7b5f3047a8222f8843Winson Chung     * Returns the current scroll position.
11032f14071cc5808737c88da7b5f3047a8222f8843Winson Chung     */
11132f14071cc5808737c88da7b5f3047a8222f8843Winson Chung    public int getScrollPosition() {
11232f14071cc5808737c88da7b5f3047a8222f8843Winson Chung        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
113ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        getCurScrollState(scrollPosState, items);
114ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        if (scrollPosState.rowIndex != -1) {
11532f14071cc5808737c88da7b5f3047a8222f8843Winson Chung            int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
116ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song            return getPaddingTop() + (scrollPosState.rowIndex * scrollPosState.rowHeight) +
117ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song                    predictionBarHeight - scrollPosState.rowTopOffset;
11832f14071cc5808737c88da7b5f3047a8222f8843Winson Chung        }
11932f14071cc5808737c88da7b5f3047a8222f8843Winson Chung        return 0;
120ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung    }
121ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung
12293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    @Override
12393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    protected void onFinishInflate() {
124ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song        super.onFinishInflate();
12593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        addOnItemTouchListener(this);
12693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
12793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
12893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
12999d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung     * Maps the touch (from 0..1) to the adapter position that should be visible.
13093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     */
131ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song    @Override
132ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song    public String scrollToPositionAtProgress(float touchFraction) {
13399d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung        // Ensure that we have any sections
13499d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung        List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections =
13599d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung                mApps.getFastScrollerSections();
13699d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung        if (fastScrollSections.isEmpty()) {
1379121fbffafe0b90a23768e5ebc5fffab2a2175c2Winson Chung            return "";
1389121fbffafe0b90a23768e5ebc5fffab2a2175c2Winson Chung        }
13993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
140208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        // Stop the scroller if it is scrolling
141208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
142208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        stopScroll();
143208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung
144208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        // If there is a prediction bar, then capture the appropriate area for the prediction bar
145208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        float predictionBarFraction = 0f;
1468c01f810ebcddeb2e9f3a6599c62f701e9c3cac9Winson Chung        if (!mApps.getPredictedApps().isEmpty()) {
147208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung            predictionBarFraction = (float) mNumPredictedAppsPerRow / mApps.getSize();
148208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung            if (touchFraction <= predictionBarFraction) {
149208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung                // Scroll to the top of the view, where the prediction bar is
150208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung                layoutManager.scrollToPositionWithOffset(0, 0);
151208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung                return "";
152208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung            }
153208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        }
154208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung
155208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        // Since the app ranges are from 0..1, we need to map the touch fraction back to 0..1 from
156208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        // predictionBarFraction..1
157208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        touchFraction = (touchFraction - predictionBarFraction) *
158208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung                (1f / (1f - predictionBarFraction));
15999d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung        AlphabeticalAppsList.FastScrollSectionInfo lastScrollSection = fastScrollSections.get(0);
16099d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung        for (int i = 1; i < fastScrollSections.size(); i++) {
16199d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung            AlphabeticalAppsList.FastScrollSectionInfo scrollSection = fastScrollSections.get(i);
16299d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung            if (lastScrollSection.appRangeFraction <= touchFraction &&
16399d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung                    touchFraction < scrollSection.appRangeFraction) {
16493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                break;
16593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            }
16699d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung            lastScrollSection = scrollSection;
16793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        }
16893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
169208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        // Scroll to the view at the position, anchored at the top of the screen. We call the scroll
170208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        // method on the LayoutManager directly since it is not exposed by RecyclerView.
171208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung        layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0);
172208ed75cfdb02e571273d73d056d8ed7f6f756ebWinson Chung
17399d96ba6c8e3258f7d99a33d49da2aeb0da5d862Winson Chung        return lastScrollSection.sectionName;
17493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
175f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung
176ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song
177ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song    /**
178ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song     * Returns the row index for a app index in the list.
179ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song     */
180ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song    private int findRowForAppIndex(int index) {
181ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections();
182ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        int appIndex = 0;
183ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        int rowCount = 0;
184ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        for (AlphabeticalAppsList.SectionInfo info : sections) {
185ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song            int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow);
186ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song            if (appIndex + info.numApps > index) {
187ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song                return rowCount + ((index - appIndex) / mNumAppsPerRow);
188ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song            }
189ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song            appIndex += info.numApps;
190ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song            rowCount += numRowsInSection;
191ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        }
192ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        return appIndex;
193ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song    }
194ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song
195ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song    /**
196ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song     * Returns the total number of rows in the list.
197ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song     */
198ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song    private int getNumRows() {
199ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections();
200ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        int rowCount = 0;
201ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        for (AlphabeticalAppsList.SectionInfo info : sections) {
202ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song            int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow);
203ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song            rowCount += numRowsInSection;
204ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        }
205ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        return rowCount;
206ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song    }
207ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song
208ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song
209f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung    /**
210515f2897e876ba52ed6371d22e6d0f7d1016aaf3Winson Chung     * Updates the bounds for the scrollbar.
211f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung     */
212ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song    @Override
213ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song    public void updateVerticalScrollbarBounds() {
21483f59abc9c566da5deb98afe7ea35cfb061f2920Winson Chung        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
21583f59abc9c566da5deb98afe7ea35cfb061f2920Winson Chung
216ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        // Skip early if there are no items.
21783f59abc9c566da5deb98afe7ea35cfb061f2920Winson Chung        if (items.isEmpty()) {
218ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song            verticalScrollbarBounds.setEmpty();
219f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung            return;
220f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung        }
221f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung
222f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung        // Find the index and height of the first visible row (all rows have the same height)
223ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        int x, y;
2248c01f810ebcddeb2e9f3a6599c62f701e9c3cac9Winson Chung        int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
225f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung        int rowCount = getNumRows();
226ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        getCurScrollState(scrollPosState, items);
227ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        if (scrollPosState.rowIndex != -1) {
228f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung            int height = getHeight() - getPaddingTop() - getPaddingBottom();
229ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song            int totalScrollHeight = rowCount * scrollPosState.rowHeight + predictionBarHeight;
230f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung            if (totalScrollHeight > height) {
231f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung                int scrollbarHeight = Math.max(mScrollbarMinHeight,
232f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung                        (int) (height / ((float) totalScrollHeight / height)));
233f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung
234f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung                // Calculate the position and size of the scroll bar
23566a90e701ff6f7002fd3ed290ffc25d9c5ff3a9aWinson Chung                if (Utilities.isRtl(getResources())) {
236ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung                    x = mBackgroundPadding.left;
237f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung                } else {
238ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song                    x = getWidth() - mBackgroundPadding.right - getScrollbarWidth();
239f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung                }
240f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung
241f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung                // To calculate the offset, we compute the percentage of the total scrollable height
242f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung                // that the user has already scrolled and then map that to the scroll bar bounds
243f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung                int availableY = totalScrollHeight - height;
244f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung                int availableScrollY = height - scrollbarHeight;
245ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song                y = (scrollPosState.rowIndex * scrollPosState.rowHeight) + predictionBarHeight
246ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song                        - scrollPosState.rowTopOffset;
247f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung                y = getPaddingTop() +
248f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung                        (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY);
249f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung
250ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song                verticalScrollbarBounds.set(x, y, x + getScrollbarWidth(), y + scrollbarHeight);
251f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung                return;
252f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung            }
253f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung        }
254ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song        verticalScrollbarBounds.setEmpty();
255f819dc2bc782e93ac9ecc163a99af0da62821d31Winson Chung    }
256ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung
257ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung    /**
258ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung     * Returns the current scroll state.
259ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung     */
260ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung    private void getCurScrollState(ScrollPositionState stateOut,
261ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung            List<AlphabeticalAppsList.AdapterItem> items) {
262ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung        stateOut.rowIndex = -1;
263ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung        stateOut.rowTopOffset = -1;
264ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung        stateOut.rowHeight = -1;
265532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung
266532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung        // Return early if there are no items
267532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung        if (items.isEmpty()) {
268532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung            return;
269532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung        }
270532e6ea2e6e4b5f968038b52db9bb998cb9fe84aWinson Chung
271ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung        int childCount = getChildCount();
272ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung        for (int i = 0; i < childCount; i++) {
273ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung            View child = getChildAt(i);
274ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung            int position = getChildPosition(child);
275ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung            if (position != NO_POSITION) {
276ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung                AlphabeticalAppsList.AdapterItem item = items.get(position);
2775f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung                if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
278ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung                    stateOut.rowIndex = findRowForAppIndex(item.appIndex);
279ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung                    stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
280ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung                    stateOut.rowHeight = child.getHeight();
281ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung                    break;
282ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung                }
283ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung            }
284ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung        }
285ed0c1cc739d3b3d82c301299db8abc3146de49bbWinson Chung    }
28693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung}
287