AllAppsRecyclerView.java revision 93f98eaf1800024cb2f28379bdd997f3debae63a
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 */
1693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungpackage com.android.launcher3;
1793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
1893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.animation.ObjectAnimator;
1993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.content.Context;
2093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.content.res.Resources;
2193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.graphics.Canvas;
2293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.graphics.Color;
2393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.graphics.Paint;
2493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.graphics.Rect;
2593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.graphics.drawable.Drawable;
2693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.support.v7.widget.RecyclerView;
2793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.util.AttributeSet;
2893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.view.MotionEvent;
2993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport android.view.ViewConfiguration;
3093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
3193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungimport java.util.List;
3293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
3393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung/**
3493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * A RecyclerView with custom fastscroll support.  This is the main container for the all apps
3593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung * icons.
3693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung */
3793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chungpublic class AppsContainerRecyclerView extends RecyclerView
3893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        implements RecyclerView.OnItemTouchListener {
3993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
4093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private AlphabeticalAppsList mApps;
4193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private int mNumAppsPerRow;
4293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
4393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private Drawable mFastScrollerBg;
4493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private boolean mDraggingFastScroller;
4593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private String mFastScrollSectionName;
4693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private Paint mFastScrollTextPaint;
4793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private Rect mFastScrollTextBounds = new Rect();
4893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private float mFastScrollAlpha;
4993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private int mDownX;
5093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private int mDownY;
5193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private int mLastX;
5293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private int mLastY;
5393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private int mGutterSize;
5493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
5593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    public AppsContainerRecyclerView(Context context) {
5693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        this(context, null);
5793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
5893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
5993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    public AppsContainerRecyclerView(Context context, AttributeSet attrs) {
6093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        this(context, attrs, 0);
6193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
6293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
6393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    public AppsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
6493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        this(context, attrs, defStyleAttr, 0);
6593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
6693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
6793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    public AppsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
6893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            int defStyleRes) {
6993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        super(context, attrs, defStyleAttr);
7093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
7193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        Resources res = context.getResources();
7293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        int fastScrollerSize = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_popup_size);
7393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        mFastScrollerBg = res.getDrawable(R.drawable.apps_list_fastscroll_bg);
7493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        mFastScrollerBg.setBounds(0, 0, fastScrollerSize, fastScrollerSize);
7593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        mFastScrollTextPaint = new Paint();
7693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        mFastScrollTextPaint.setColor(Color.WHITE);
7793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        mFastScrollTextPaint.setAntiAlias(true);
7893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize(
7993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                R.dimen.apps_view_fast_scroll_text_size));
8093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        mGutterSize = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_gutter_size);
8193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        setFastScrollerAlpha(getFastScrollerAlpha());
8293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
8393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
8493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
8593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * Sets the list of apps in this view, used to determine the fastscroll position.
8693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     */
8793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    public void setApps(AlphabeticalAppsList apps) {
8893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        mApps = apps;
8993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
9093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
9193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
9293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * Sets the number of apps per row in this recycler view.
9393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     */
9493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    public void setNumAppsPerRow(int rowSize) {
9593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        mNumAppsPerRow = rowSize;
9693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
9793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
9893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
9993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * Sets the fast scroller alpha.
10093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     */
10193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    public void setFastScrollerAlpha(float alpha) {
10293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        mFastScrollAlpha = alpha;
10393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        invalidateFastScroller();
10493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
10593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
10693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
10793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * Gets the fast scroller alpha.
10893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     */
10993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    public float getFastScrollerAlpha() {
11093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        return mFastScrollAlpha;
11193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
11293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
11393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    @Override
11493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    protected void onFinishInflate() {
11593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        addOnItemTouchListener(this);
11693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
11793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
11893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    @Override
11993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    protected void dispatchDraw(Canvas canvas) {
12093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        super.dispatchDraw(canvas);
12193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
12293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        if (mFastScrollAlpha > 0f) {
12393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
12493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    LAYOUT_DIRECTION_RTL);
12593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            Rect bgBounds = mFastScrollerBg.getBounds();
12693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
12793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            int x;
12893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            if (isRtl) {
12993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                x = getPaddingLeft() + getScrollBarSize();
13093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            } else {
13193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width();
13293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            }
13393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            int y = mLastY - bgBounds.height() / 2;
13493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() -
13593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    bgBounds.height()));
13693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            canvas.translate(x, y);
13793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            mFastScrollerBg.setAlpha((int) (mFastScrollAlpha * 255));
13893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            mFastScrollerBg.draw(canvas);
13993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255));
14093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0,
14193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    mFastScrollSectionName.length(), mFastScrollTextBounds);
14293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            canvas.drawText(mFastScrollSectionName,
14393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    (bgBounds.width() - mFastScrollTextBounds.width()) / 2,
14493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    bgBounds.height() -  (bgBounds.height() - mFastScrollTextBounds.height()) / 2,
14593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    mFastScrollTextPaint);
14693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            canvas.restoreToCount(restoreCount);
14793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        }
14893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
14993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
15093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
15193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * We intercept the touch handling only to support fast scrolling when initiated from the
15293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * gutter.  Otherwise, we fall back to the default RecyclerView touch handling.
15393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     */
15493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    @Override
15593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {
15693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        return handleTouchEvent(ev);
15793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
15893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
15993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    @Override
16093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    public void onTouchEvent(RecyclerView rv, MotionEvent ev) {
16193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        handleTouchEvent(ev);
16293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
16393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
16493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
16593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * Handles the touch event and determines whether to show the fast scroller (or updates it if
16693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * it is already showing).
16793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     */
16893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private boolean handleTouchEvent(MotionEvent ev) {
16993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        ViewConfiguration config = ViewConfiguration.get(getContext());
17093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
17193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        int action = ev.getAction();
17293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        int x = (int) ev.getX();
17393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        int y = (int) ev.getY();
17493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        switch (action) {
17593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            case MotionEvent.ACTION_DOWN:
17693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                // Keep track of the down positions
17793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                mDownX = mLastX = x;
17893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                mDownY = mLastY = y;
17993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                stopScroll();
18093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                break;
18193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            case MotionEvent.ACTION_MOVE:
18293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                // Check if we are scrolling
18393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
18493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                        LAYOUT_DIRECTION_RTL);
18593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                boolean isInGutter;
18693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                if (isRtl) {
18793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    isInGutter = mDownX < mGutterSize;
18893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                } else {
18993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    isInGutter = mDownX >= (getWidth() - mGutterSize);
19093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                }
19193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                if (!mDraggingFastScroller && isInGutter &&
19293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                        Math.abs(y - mDownY) > config.getScaledTouchSlop()) {
19393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    getParent().requestDisallowInterceptTouchEvent(true);
19493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    mDraggingFastScroller = true;
19593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    animateFastScrollerVisibility(true);
19693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                }
19793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                if (mDraggingFastScroller) {
19893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    mLastX = x;
19993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    mLastY = y;
20093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
20193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    // Scroll to the right position, and update the section name
20293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    int top = getPaddingTop() + (mFastScrollerBg.getBounds().height() / 2);
20393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    int bottom = getHeight() - getPaddingBottom() -
20493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                            (mFastScrollerBg.getBounds().height() / 2);
20593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    float boundedY = (float) Math.max(top, Math.min(bottom, y));
20693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    mFastScrollSectionName = scrollToPositionAtProgress((boundedY - top) /
20793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                            (bottom - top));
20893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                    invalidateFastScroller();
20993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                }
21093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                break;
21193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            case MotionEvent.ACTION_UP:
21293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            case MotionEvent.ACTION_CANCEL:
21393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                mDraggingFastScroller = false;
21493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                animateFastScrollerVisibility(false);
21593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                break;
21693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        }
21793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        return mDraggingFastScroller;
21893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
21993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
22093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
22193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
22293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * Animates the visibility of the fast scroller popup.
22393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     */
22493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private void animateFastScrollerVisibility(boolean visible) {
22593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        ObjectAnimator anim = ObjectAnimator.ofFloat(this, "fastScrollerAlpha", visible ? 1f : 0f);
22693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        anim.setDuration(visible ? 200 : 150);
22793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        anim.start();
22893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
22993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
23093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
23193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * Invalidates the fast scroller popup.
23293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     */
23393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private void invalidateFastScroller() {
23493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        invalidate(getWidth() - getPaddingRight() - getScrollBarSize() -
23593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                mFastScrollerBg.getIntrinsicWidth(), 0, getWidth(), getHeight());
23693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
23793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
23893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    /**
23993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     * Maps the progress (from 0..1) to the position that should be visible
24093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung     */
24193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    private String scrollToPositionAtProgress(float progress) {
24293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections();
24393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        // Get the total number of rows
24493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        int rowCount = 0;
24593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        for (AlphabeticalAppsList.SectionInfo info : sections) {
24693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow);
24793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            rowCount += numRowsInSection;
24893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        }
24993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
25093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        // Find the index of the first app in that row and scroll to that position
25193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        int rowAtProgress = (int) (progress * rowCount);
25293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        int appIndex = 0;
25393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        rowCount = 0;
25493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        for (AlphabeticalAppsList.SectionInfo info : sections) {
25593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow);
25693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            if (rowCount + numRowsInSection > rowAtProgress) {
25793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                appIndex += (rowAtProgress - rowCount) * mNumAppsPerRow;
25893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung                break;
25993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            }
26093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            rowCount += numRowsInSection;
26193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung            appIndex += info.numAppsInSection;
26293f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        }
26393f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        appIndex = Math.max(0, Math.min(mApps.getAppsWithoutSectionBreaks().size() - 1, appIndex));
26493f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        AppInfo appInfo = mApps.getAppsWithoutSectionBreaks().get(appIndex);
26593f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        int sectionedAppIndex = mApps.getApps().indexOf(appInfo);
26693f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        scrollToPosition(sectionedAppIndex);
26793f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung
26893f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        // Returns the section name of the row
26993f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung        return mApps.getSectionNameForApp(appInfo);
27093f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung    }
27193f98eaf1800024cb2f28379bdd997f3debae63aWinson Chung}
272