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