1b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung/*
289d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal * Copyright (C) 2017 The Android Open Source Project
3b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung *
4b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * Licensed under the Apache License, Version 2.0 (the "License");
5b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * you may not use this file except in compliance with the License.
6b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * You may obtain a copy of the License at
7b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung *
8b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung *      http://www.apache.org/licenses/LICENSE-2.0
9b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung *
10b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * Unless required by applicable law or agreed to in writing, software
11b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * distributed under the License is distributed on an "AS IS" BASIS,
12b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * See the License for the specific language governing permissions and
14b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * limitations under the License.
15b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung */
1689d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
1789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyalpackage com.android.launcher3.views;
18b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
19b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chungimport android.animation.ObjectAnimator;
2089d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyalimport android.content.Context;
21b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chungimport android.content.res.Resources;
2289d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyalimport android.content.res.TypedArray;
23b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chungimport android.graphics.Canvas;
24b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chungimport android.graphics.Paint;
2589d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyalimport android.support.v7.widget.RecyclerView;
2689d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyalimport android.util.AttributeSet;
275d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyalimport android.util.Property;
28b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chungimport android.view.MotionEvent;
295d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyalimport android.view.View;
30b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chungimport android.view.ViewConfiguration;
315d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyalimport android.widget.TextView;
32b713ad4adb7d7dbc926ee2ab793ee6ef38cd2520Sunny Goyal
3389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyalimport com.android.launcher3.BaseRecyclerView;
3489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyalimport com.android.launcher3.R;
3589d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyalimport com.android.launcher3.Utilities;
36ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschlerimport com.android.launcher3.config.FeatureFlags;
371a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyalimport com.android.launcher3.graphics.FastScrollThumbDrawable;
381f3f07d47c29cba3b70bcd15ebb65a077f55a558Sunny Goyalimport com.android.launcher3.util.Themes;
39ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler
40b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung/**
41b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung * The track and scrollbar that shows when you scroll the list.
42b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung */
4389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyalpublic class RecyclerViewFastScroller extends View {
4489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
4589d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
46b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
4789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    private static final Property<RecyclerViewFastScroller, Integer> TRACK_WIDTH =
4889d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal            new Property<RecyclerViewFastScroller, Integer>(Integer.class, "width") {
495d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
505d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal                @Override
5189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                public Integer get(RecyclerViewFastScroller scrollBar) {
525d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal                    return scrollBar.mWidth;
535d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal                }
545d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
555d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal                @Override
5689d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                public void set(RecyclerViewFastScroller scrollBar, Integer value) {
575d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal                    scrollBar.setTrackWidth(value);
585d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal                }
595d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal            };
605d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
61b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    private final static int MAX_TRACK_ALPHA = 30;
62b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    private final static int SCROLL_BAR_VIS_DURATION = 150;
631a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyal    private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 0.75f;
645d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
655d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private final int mMinWidth;
665d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private final int mMaxWidth;
671a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyal    private final int mThumbPadding;
685d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
6989d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    /** Keeps the last known scrolling delta/velocity along y-axis. */
7089d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    private int mDy = 0;
7189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    private final float mDeltaThreshold;
7289d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
7389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    private final ViewConfiguration mConfig;
7489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
755d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    // Current width of the track
765d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private int mWidth;
775d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private ObjectAnimator mWidthAnimator;
785d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
795d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private final Paint mThumbPaint;
8089d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    protected final int mThumbHeight;
815d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
825d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private final Paint mTrackPaint;
835d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
845d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private float mLastTouchY;
85b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    private boolean mIsDragging;
86d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson    private boolean mIsThumbDetached;
8789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    private final boolean mCanThumbDetach;
88ec4845b3b19c1496cd67f58f1d027bcd97e1dcb0Winson    private boolean mIgnoreDragGesture;
89b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
90b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    // This is the offset from the top of the scrollbar when the user first starts touching.  To
91b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    // prevent jumping, this offset is applied as the user scrolls.
9289d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    protected int mTouchOffsetY;
9389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    protected int mThumbOffsetY;
94b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
955d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    // Fast scroller popup
965d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private TextView mPopupView;
975d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private boolean mPopupVisible;
985d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private String mPopupSectionName;
99b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
10089d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    protected BaseRecyclerView mRv;
10189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
10289d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    private int mDownX;
10389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    private int mDownY;
10489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    private int mLastY;
10589d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
10689d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    public RecyclerViewFastScroller(Context context) {
10789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        this(context, null);
10889d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    }
10989d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
11089d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    public RecyclerViewFastScroller(Context context, AttributeSet attrs) {
11189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        this(context, attrs, 0);
11289d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    }
11389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
11489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    public RecyclerViewFastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
11589d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        super(context, attrs, defStyleAttr);
11689d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
117b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        mTrackPaint = new Paint();
11889d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        mTrackPaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
1196779595bec64fa4503c22f75c8e245a449fe665dWinson        mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
1205d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
121b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        mThumbPaint = new Paint();
1226779595bec64fa4503c22f75c8e245a449fe665dWinson        mThumbPaint.setAntiAlias(true);
12389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        mThumbPaint.setColor(Themes.getColorAccent(context));
1246779595bec64fa4503c22f75c8e245a449fe665dWinson        mThumbPaint.setStyle(Paint.Style.FILL);
1255d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
12689d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        Resources res = getResources();
1271a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyal        mWidth = mMinWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_min_width);
1281a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyal        mMaxWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_max_width);
1291a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyal
1301a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyal        mThumbPadding = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_padding);
1311a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyal        mThumbHeight = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_height);
1321a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyal
13389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        mConfig = ViewConfiguration.get(context);
13489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        mDeltaThreshold = res.getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
1355d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
13689d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        TypedArray ta =
13789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                context.obtainStyledAttributes(attrs, R.styleable.RecyclerViewFastScroller, defStyleAttr, 0);
13889d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        mCanThumbDetach = ta.getBoolean(R.styleable.RecyclerViewFastScroller_canThumbDetach, false);
13989d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        ta.recycle();
140b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    }
141b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
14289d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    public void setRecyclerView(BaseRecyclerView rv, TextView popupView) {
14389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        mRv = rv;
14489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        mRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
14589d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal            @Override
14689d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
14789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                mDy = dy;
14889d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
14989d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                // TODO(winsonc): If we want to animate the section heads while scrolling, we can
15089d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                //                initiate that here if the recycler view scroll state is not
15189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                //                RecyclerView.SCROLL_STATE_IDLE.
15289d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
15389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                mRv.onUpdateScrollbar(dy);
15489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal            }
15589d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        });
15689d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
15789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        mPopupView = popupView;
15889d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        mPopupView.setBackground(
15989d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                new FastScrollThumbDrawable(mThumbPaint, Utilities.isRtl(getResources())));
160d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson    }
161d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson
162d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson    public void reattachThumbToScroll() {
163d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson        mIsThumbDetached = false;
164d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson    }
165d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson
1665d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    public void setThumbOffsetY(int y) {
1675d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        if (mThumbOffsetY == y) {
168b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung            return;
169b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        }
1705d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        mThumbOffsetY = y;
17189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        invalidate();
172d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson    }
173d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson
1745d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    public int getThumbOffsetY() {
1755d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        return mThumbOffsetY;
176b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    }
177b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
1785d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private void setTrackWidth(int width) {
1795d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        if (mWidth == width) {
1805d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal            return;
1815d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        }
1825d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        mWidth = width;
18389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        invalidate();
184b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    }
185b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
186b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    public int getThumbHeight() {
187b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        return mThumbHeight;
188b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    }
189b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
190d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson    public boolean isDraggingThumb() {
191b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        return mIsDragging;
192b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    }
193b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
194d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson    public boolean isThumbDetached() {
195d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson        return mIsThumbDetached;
196d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson    }
197d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson
198b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    /**
199b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung     * Handles the touch event and determines whether to show the fast scroller (or updates it if
200b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung     * it is already showing).
201b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung     */
20289d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    public boolean handleTouchEvent(MotionEvent ev) {
20389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        int x = (int) ev.getX();
204b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        int y = (int) ev.getY();
20589d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        switch (ev.getAction()) {
206b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung            case MotionEvent.ACTION_DOWN:
20789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                // Keep track of the down positions
20889d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                mDownX = x;
20989d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                mDownY = mLastY = y;
21089d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
21189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                if ((Math.abs(mDy) < mDeltaThreshold &&
21289d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                        mRv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
21389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                    // now the touch events are being passed to the {@link WidgetCell} until the
21489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                    // touch sequence goes over the touch slop.
21589d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                    mRv.stopScroll();
21689d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                }
21789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                if (isNearThumb(x, y)) {
21889d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                    mTouchOffsetY = mDownY - mThumbOffsetY;
219ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler                } else if (FeatureFlags.LAUNCHER3_DIRECT_SCROLL
220ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler                        && mRv.supportsFastScrolling()
22189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                        && isNearScrollBar(mDownX)) {
22289d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                    calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
22389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                    updateFastScrollSectionNameAndThumbOffset(mLastY, y);
224b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung                }
225b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung                break;
226b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung            case MotionEvent.ACTION_MOVE:
22789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                mLastY = y;
22889d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
229ec4845b3b19c1496cd67f58f1d027bcd97e1dcb0Winson                // Check if we should start scrolling, but ignore this fastscroll gesture if we have
230ec4845b3b19c1496cd67f58f1d027bcd97e1dcb0Winson                // exceeded some fixed movement
23189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                mIgnoreDragGesture |= Math.abs(y - mDownY) > mConfig.getScaledPagingTouchSlop();
232646c236ad283c44e41747176fe1017d619b92d5aWinson                if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
23389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                        isNearThumb(mDownX, mLastY) &&
23489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                        Math.abs(y - mDownY) > mConfig.getScaledTouchSlop()) {
23589d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                    calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
236b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung                }
237b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung                if (mIsDragging) {
23889d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                    updateFastScrollSectionNameAndThumbOffset(mLastY, y);
239b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung                }
240b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung                break;
241b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung            case MotionEvent.ACTION_UP:
242b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung            case MotionEvent.ACTION_CANCEL:
24389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                mRv.onFastScrollCompleted();
2445d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal                mTouchOffsetY = 0;
245d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson                mLastTouchY = 0;
246ec4845b3b19c1496cd67f58f1d027bcd97e1dcb0Winson                mIgnoreDragGesture = false;
2474c7fc62ae2f8c7bee397815deaa488c3cb6a2f76Winson Chung                if (mIsDragging) {
2484c7fc62ae2f8c7bee397815deaa488c3cb6a2f76Winson Chung                    mIsDragging = false;
2495d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal                    animatePopupVisibility(false);
250c088049113c261331b5685e64050d14a31cd72dfWinson                    showActiveScrollbar(false);
2514c7fc62ae2f8c7bee397815deaa488c3cb6a2f76Winson Chung                }
252b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung                break;
253b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        }
25489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        return mIsDragging;
255b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    }
256b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
257ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler    private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
258ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        mRv.getParent().requestDisallowInterceptTouchEvent(true);
259ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        mIsDragging = true;
260ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        if (mCanThumbDetach) {
261ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler            mIsThumbDetached = true;
262ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        }
263ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        mTouchOffsetY += (lastY - downY);
264ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        animatePopupVisibility(true);
265ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        showActiveScrollbar(true);
266ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler    }
267ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler
268ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler    private void updateFastScrollSectionNameAndThumbOffset(int lastY, int y) {
269ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        // Update the fastscroller section name at this touch position
270ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        int bottom = mRv.getScrollbarTrackHeight() - mThumbHeight;
271ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        float boundedY = (float) Math.max(0, Math.min(bottom, y - mTouchOffsetY));
272ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        String sectionName = mRv.scrollToPositionAtProgress(boundedY / bottom);
273ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        if (!sectionName.equals(mPopupSectionName)) {
274ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler            mPopupSectionName = sectionName;
275ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler            mPopupView.setText(sectionName);
276ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        }
277ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        animatePopupVisibility(!sectionName.isEmpty());
278ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        updatePopupY(lastY);
279ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        mLastTouchY = boundedY;
280ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler        setThumbOffsetY((int) mLastTouchY);
281ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler    }
282ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler
28389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    public void onDraw(Canvas canvas) {
2845d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        if (mThumbOffsetY < 0) {
285b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung            return;
286b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung        }
2875d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
28889d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        canvas.translate(getWidth() / 2, mRv.getPaddingTop());
2895d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        // Draw the track
29089d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        float halfW = mWidth / 2;
29189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(),
2921a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyal                mWidth, mWidth, mTrackPaint);
293b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
29489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        canvas.translate(0, mThumbOffsetY);
29589d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        halfW += mThumbPadding;
2961a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyal        float r = mWidth + mThumbPadding + mThumbPadding;
29789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        canvas.drawRoundRect(-halfW, 0, halfW, mThumbHeight, r, r, mThumbPaint);
2985d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        canvas.restoreToCount(saveCount);
299b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    }
300b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
30189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
302b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    /**
3035d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal     * Animates the width of the scrollbar.
304b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung     */
305c088049113c261331b5685e64050d14a31cd72dfWinson    private void showActiveScrollbar(boolean isScrolling) {
3065d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        if (mWidthAnimator != null) {
3075d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal            mWidthAnimator.cancel();
3086779595bec64fa4503c22f75c8e245a449fe665dWinson        }
309b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung
3105d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        mWidthAnimator = ObjectAnimator.ofInt(this, TRACK_WIDTH,
3115d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal                isScrolling ? mMaxWidth : mMinWidth);
3125d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        mWidthAnimator.setDuration(SCROLL_BAR_VIS_DURATION);
3135d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        mWidthAnimator.start();
3146779595bec64fa4503c22f75c8e245a449fe665dWinson    }
3156779595bec64fa4503c22f75c8e245a449fe665dWinson
3166779595bec64fa4503c22f75c8e245a449fe665dWinson    /**
317ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler     * Returns whether the specified point is inside the thumb bounds.
318b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung     */
31989d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    private boolean isNearThumb(int x, int y) {
32089d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        int offset = y - mRv.getPaddingTop() - mThumbOffsetY;
32189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
32289d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        return x >= 0 && x < getWidth() && offset >= 0 && offset <= mThumbHeight;
32389d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    }
32489d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal
32589d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    /**
32689d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal     * Returns true if AllAppsTransitionController can handle vertical motion
32789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal     * beginning at this point.
32889d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal     */
32989d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal    public boolean shouldBlockIntercept(int x, int y) {
33089d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        return isNearThumb(x, y);
331b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung    }
3325d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
333ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler    /**
334ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler     * Returns whether the specified x position is near the scroll bar.
335ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler     */
336ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler    public boolean isNearScrollBar(int x) {
33789d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        return x >= (getWidth() - mMaxWidth) / 2 && x <= (getWidth() + mMaxWidth) / 2;
338ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler    }
339ee4ee424b42f76ce41dfd659a92f249af8ccd0e1Mario Bertschler
3405d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private void animatePopupVisibility(boolean visible) {
3415d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        if (mPopupVisible != visible) {
3425d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal            mPopupVisible = visible;
3435d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal            mPopupView.animate().cancel();
3445d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal            mPopupView.animate().alpha(visible ? 1f : 0f).setDuration(visible ? 200 : 150).start();
3455d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        }
3465d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    }
3475d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal
3485d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    private void updatePopupY(int lastTouchY) {
3495d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        int height = mPopupView.getHeight();
35089d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal        float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height)
35189d5c5a31bd6cf4caf815b680ec670896b91803dSunny Goyal                + mRv.getPaddingTop();
3521a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyal        top = Utilities.boundToRange(top,
3531a8f6fb736c36548a44db6c7018771e3a895dda0Sunny Goyal                mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height);
3545d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal        mPopupView.setTranslationY(top);
3555d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal    }
356b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung}
357