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