diff --git a/res/layout-land/all_apps_fast_scroller.xml b/res/layout-land/all_apps_fast_scroller.xml new file mode 100644 index 0000000000..957c331741 --- /dev/null +++ b/res/layout-land/all_apps_fast_scroller.xml @@ -0,0 +1,38 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/layout-sw720dp/all_apps_fast_scroller.xml b/res/layout-sw720dp/all_apps_fast_scroller.xml new file mode 100644 index 0000000000..12c15cca9a --- /dev/null +++ b/res/layout-sw720dp/all_apps_fast_scroller.xml @@ -0,0 +1,37 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml index a3c2535370..93662fc08f 100644 --- a/res/layout/all_apps.xml +++ b/res/layout/all_apps.xml @@ -16,7 +16,8 @@ - @@ -55,20 +58,14 @@ android:descendantFocusability="afterDescendants" android:focusable="true" /> - - - + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/all_apps_search_container.xml b/res/layout/all_apps_search_container.xml index c79360f667..2528034ebf 100644 --- a/res/layout/all_apps_search_container.xml +++ b/res/layout/all_apps_search_container.xml @@ -20,13 +20,24 @@ android:layout_height="@dimen/all_apps_search_bar_height" android:layout_gravity="center|top" android:gravity="center|bottom" - android:saveEnabled="false"> + android:saveEnabled="false" + android:paddingLeft="@dimen/dynamic_grid_edge_margin" + android:paddingRight="@dimen/dynamic_grid_edge_margin" + android:layout_marginBottom="-8dp" > + + + + + + \ No newline at end of file diff --git a/res/layout/all_apps_search_divider.xml b/res/layout/all_apps_search_divider.xml deleted file mode 100644 index c052c6635e..0000000000 --- a/res/layout/all_apps_search_divider.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - \ No newline at end of file diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml index 558556ad8a..4f3c7c8df0 100644 --- a/res/layout/widgets_view.xml +++ b/res/layout/widgets_view.xml @@ -23,25 +23,25 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:descendantFocusability="afterDescendants" - launcher:revealBackground="@drawable/round_rect_primary" - android:theme="?attr/widgetsTheme"> + android:theme="?attr/widgetsTheme" + launcher:revealBackground="@drawable/round_rect_primary"> + android:visibility="gone"> + + + - \ No newline at end of file diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 3839eb1309..7b52dae5d6 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -138,4 +138,8 @@ + + + + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 71f9edc919..a4dff71905 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -65,7 +65,15 @@ 32dp 19dp - + + 58dp + -26dp + + 0dp 48dp 60dp diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index 84358ea37b..3ee6e51b8d 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -22,8 +22,9 @@ import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.ViewGroup; +import android.widget.TextView; -import com.android.launcher3.util.Thunk; +import com.android.launcher3.views.RecyclerViewFastScroller; /** @@ -36,19 +37,7 @@ import com.android.launcher3.util.Thunk; public abstract class BaseRecyclerView extends RecyclerView implements RecyclerView.OnItemTouchListener { - private static final int SCROLL_DELTA_THRESHOLD_DP = 4; - - /** Keeps the last known scrolling delta/velocity along y-axis. */ - @Thunk int mDy = 0; - private float mDeltaThreshold; - - protected final BaseRecyclerViewFastScrollBar mScrollbar; - - private int mDownX; - private int mDownY; - private int mLastY; - - private boolean mScrollBarVisible = true; + protected RecyclerViewFastScroller mScrollbar; public BaseRecyclerView(Context context) { this(context, null); @@ -60,28 +49,6 @@ public abstract class BaseRecyclerView extends RecyclerView public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP; - mScrollbar = new BaseRecyclerViewFastScrollBar(this, getResources()); - - ScrollListener listener = new ScrollListener(); - setOnScrollListener(listener); - } - - private class ScrollListener extends OnScrollListener { - public ScrollListener() { - // Do nothing - } - - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - mDy = dy; - - // TODO(winsonc): If we want to animate the section heads while scrolling, we can - // initiate that here if the recycler view scroll state is not - // RecyclerView.SCROLL_STATE_IDLE. - - onUpdateScrollbar(dy); - } } @Override @@ -93,7 +60,9 @@ public abstract class BaseRecyclerView extends RecyclerView @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - mScrollbar.setPopupView(((ViewGroup) getParent()).findViewById(R.id.fast_scroller_popup)); + ViewGroup parent = (ViewGroup) getParent(); + mScrollbar = parent.findViewById(R.id.fast_scroller); + mScrollbar.setRecyclerView(this, (TextView) parent.findViewById(R.id.fast_scroller_popup)); } /** @@ -115,57 +84,25 @@ public abstract class BaseRecyclerView extends RecyclerView * it is already showing). */ private boolean handleTouchEvent(MotionEvent ev) { - ev.offsetLocation(0, -getPaddingTop()); - int action = ev.getAction(); - int x = (int) ev.getX(); - int y = (int) ev.getY(); - switch (action) { - case MotionEvent.ACTION_DOWN: - // Keep track of the down positions - mDownX = x; - mDownY = mLastY = y; - if (shouldStopScroll(ev)) { - stopScroll(); - } - mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY); - break; - case MotionEvent.ACTION_MOVE: - mLastY = y; - mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - onFastScrollCompleted(); - mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY); - break; + // Move to mScrollbar's coordinate system. + int left = getLeft() - mScrollbar.getLeft(); + int top = getTop() - mScrollbar.getTop(); + ev.offsetLocation(left, top); + try { + return mScrollbar.handleTouchEvent(ev); + } finally { + ev.offsetLocation(-left, -top); } - ev.offsetLocation(0, getPaddingTop()); - return mScrollbar.isDraggingThumb(); } public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS } - /** - * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped. - */ - protected boolean shouldStopScroll(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - if ((Math.abs(mDy) < mDeltaThreshold && - getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) { - // now the touch events are being passed to the {@link WidgetCell} until the - // touch sequence goes over the touch slop. - return true; - } - } - return false; - } - /** * Returns the height of the fast scroll bar */ - protected int getScrollbarTrackHeight() { + public int getScrollbarTrackHeight() { return getHeight() - getPaddingTop() - getPaddingBottom(); } @@ -187,25 +124,14 @@ public abstract class BaseRecyclerView extends RecyclerView /** * Returns the scrollbar for this recycler view. */ - public BaseRecyclerViewFastScrollBar getScrollBar() { + public RecyclerViewFastScroller getScrollBar() { return mScrollbar; } @Override protected void dispatchDraw(Canvas canvas) { + onUpdateScrollbar(0); super.dispatchDraw(canvas); - if (mScrollBarVisible) { - onUpdateScrollbar(0); - mScrollbar.draw(canvas); - } - } - - /** - * Sets the scrollbar visibility. The call does not refresh the UI, its the responsibility - * of the caller to call {@link #invalidate()}. - */ - public void setScrollBarVisible(boolean visible) { - mScrollBarVisible = visible; } /** @@ -236,7 +162,7 @@ public abstract class BaseRecyclerView extends RecyclerView /** * @return whether fast scrolling is supported in the current state. */ - protected boolean supportsFastScrolling() { + public boolean supportsFastScrolling() { return true; } @@ -252,16 +178,16 @@ public abstract class BaseRecyclerView extends RecyclerView * Maps the touch (from 0..1) to the adapter position that should be visible. *

Override in each subclass of this base class. */ - protected abstract String scrollToPositionAtProgress(float touchFraction); + public abstract String scrollToPositionAtProgress(float touchFraction); /** * Updates the bounds for the scrollbar. *

Override in each subclass of this base class. */ - protected abstract void onUpdateScrollbar(int dy); + public abstract void onUpdateScrollbar(int dy); /** *

Override in each subclass of this base class. */ - protected void onFastScrollCompleted() {} + public void onFastScrollCompleted() {} } \ No newline at end of file diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 4954e0c44c..47b68a2ee1 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -171,19 +171,19 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc * Returns whether the view itself will handle the touch event or not. */ public boolean shouldContainerScroll(MotionEvent ev) { - int[] point = new int[2]; - point[0] = (int) ev.getX(); - point[1] = (int) ev.getY(); - Utilities.mapCoordInSelfToDescendant(mAppsRecyclerView, this, point); - // IF the MotionEvent is inside the search box, and the container keeps on receiving // touch input, container should move down. if (mLauncher.getDragLayer().isEventOverView(mSearchContainer, ev)) { return true; } + int[] point = new int[2]; + point[0] = (int) ev.getX(); + point[1] = (int) ev.getY(); + Utilities.mapCoordInSelfToDescendant( + mAppsRecyclerView.getScrollBar(), mLauncher.getDragLayer(), point); // IF the MotionEvent is inside the thumb, container should not be pulled down. - if (mAppsRecyclerView.getScrollBar().isNearThumb(point[0], point[1])) { + if (mAppsRecyclerView.getScrollBar().shouldBlockIntercept(point[0], point[1])) { return false; } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index d6514a83f6..1054a56333 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -69,16 +69,13 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter= 0 && x < getWidth() && y >= 0 && y < getHeight(); + } +} diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java index 3f06ec9dd2..5cb12d5924 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -54,12 +54,13 @@ public class AppsSearchContainerLayout extends FrameLayout private final int mSearchBoxHeight; private final AllAppsSearchBarController mSearchBarController; private final SpannableStringBuilder mSearchQueryBuilder; - private final HeaderElevationController mElevationController; private ExtendedEditText mSearchInput; private AlphabeticalAppsList mApps; private AllAppsRecyclerView mAppsRecyclerView; private AllAppsGridAdapter mAdapter; + private View mDivider; + private HeaderElevationController mElevationController; public AppsSearchContainerLayout(Context context) { this(context, null); @@ -77,7 +78,6 @@ public class AppsSearchContainerLayout extends FrameLayout mSearchBoxHeight = getResources() .getDimensionPixelSize(R.dimen.all_apps_search_bar_field_height); mSearchBarController = new AllAppsSearchBarController(); - mElevationController = new HeaderElevationController(this); mSearchQueryBuilder = new SpannableStringBuilder(); Selection.setSelection(mSearchQueryBuilder, 0); @@ -87,6 +87,8 @@ public class AppsSearchContainerLayout extends FrameLayout protected void onFinishInflate() { super.onFinishInflate(); mSearchInput = findViewById(R.id.search_box_input); + mDivider = findViewById(R.id.search_divider); + mElevationController = new HeaderElevationController(mDivider); // Update the hint to contain the icon. // Prefix the original hint with two spaces. The first space gets replaced by the icon @@ -96,6 +98,12 @@ public class AppsSearchContainerLayout extends FrameLayout spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search), 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE); mSearchInput.setHint(spanned); + + DeviceProfile dp = mLauncher.getDeviceProfile(); + if (!dp.isVerticalBarLayout()) { + LayoutParams lp = (LayoutParams) mDivider.getLayoutParams(); + lp.leftMargin = lp.rightMargin = dp.edgeMarginPx; + } } @Override diff --git a/src/com/android/launcher3/allapps/search/HeaderElevationController.java b/src/com/android/launcher3/allapps/search/HeaderElevationController.java index ab4e88fc8b..7cd32b26e5 100644 --- a/src/com/android/launcher3/allapps/search/HeaderElevationController.java +++ b/src/com/android/launcher3/allapps/search/HeaderElevationController.java @@ -4,11 +4,11 @@ import android.content.res.Resources; import android.graphics.Outline; import android.support.v7.widget.RecyclerView; import android.view.View; +import android.view.ViewGroup; import android.view.ViewOutlineProvider; import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.R; -import com.android.launcher3.Utilities; /** * Helper class for controlling the header elevation in response to RecyclerView scroll. @@ -16,6 +16,7 @@ import com.android.launcher3.Utilities; public class HeaderElevationController extends RecyclerView.OnScrollListener { private final View mHeader; + private final View mHeaderChild; private final float mMaxElevation; private final float mScrollToElevation; @@ -28,23 +29,27 @@ public class HeaderElevationController extends RecyclerView.OnScrollListener { mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation); // We need to provide a custom outline so the shadow only appears on the bottom edge. - // The top, left and right edges are all extended out, and the shadow is clipped - // by the parent. + // The top, left and right edges are all extended out to match parent's edge, so that + // the shadow is clipped by the parent. final ViewOutlineProvider vop = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - final View parent = (View) mHeader.getParent(); + // Set the left and top to be at the parents edge. Since the coordinates are + // relative to this view, + // (x = -view.getLeft()) for this view => (x = 0) for parent + final int left = -view.getLeft(); + final int top = -view.getTop(); - final int left = parent.getLeft(); // Use the parent to account for offsets - final int top = view.getTop(); - final int right = left + view.getWidth(); - final int bottom = view.getBottom(); - - final int offset = Utilities.pxFromDp(mMaxElevation, res.getDisplayMetrics()); + // Since the view is centered align, the spacing on left and right are same. + // Add same spacing on the right to reach parent's edge. + final int right = view.getWidth() - left; + final int bottom = view.getHeight(); + final int offset = (int) mMaxElevation; outline.setRect(left - offset, top - offset, right + offset, bottom); } }; mHeader.setOutlineProvider(vop); + mHeaderChild = ((ViewGroup) mHeader).getChildAt(0); } public void reset() { @@ -63,6 +68,13 @@ public class HeaderElevationController extends RecyclerView.OnScrollListener { float newElevation = mMaxElevation * elevationPct; if (Float.compare(mHeader.getElevation(), newElevation) != 0) { mHeader.setElevation(newElevation); + + // To simulate a scrolling effect for the header, we translate the header down, and + // its content up by the same amount, so that it gets clipped by the parent, making it + // look like the content was scrolled out of the view. + int shift = Math.min(mHeader.getHeight(), scrollY); + mHeader.setTranslationY(-shift); + mHeaderChild.setTranslationY(shift); } } diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java similarity index 58% rename from src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java rename to src/com/android/launcher3/views/RecyclerViewFastScroller.java index 9e8d300ae1..7b5bcdbd42 100644 --- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java +++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,21 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.launcher3; + +package com.android.launcher3.views; import android.animation.ObjectAnimator; +import android.content.Context; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.Rect; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; import android.util.Property; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.widget.TextView; +import com.android.launcher3.BaseRecyclerView; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.FastScrollThumbDrawable; import com.android.launcher3.util.Themes; @@ -35,18 +40,20 @@ import com.android.launcher3.util.Themes; /** * The track and scrollbar that shows when you scroll the list. */ -public class BaseRecyclerViewFastScrollBar { +public class RecyclerViewFastScroller extends View { - private static final Property TRACK_WIDTH = - new Property(Integer.class, "width") { + private static final int SCROLL_DELTA_THRESHOLD_DP = 4; + + private static final Property TRACK_WIDTH = + new Property(Integer.class, "width") { @Override - public Integer get(BaseRecyclerViewFastScrollBar scrollBar) { + public Integer get(RecyclerViewFastScroller scrollBar) { return scrollBar.mWidth; } @Override - public void set(BaseRecyclerViewFastScrollBar scrollBar, Integer value) { + public void set(RecyclerViewFastScroller scrollBar, Integer value) { scrollBar.setTrackWidth(value); } }; @@ -55,93 +62,113 @@ public class BaseRecyclerViewFastScrollBar { private final static int SCROLL_BAR_VIS_DURATION = 150; private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 0.75f; - private final Rect mTmpRect = new Rect(); - private final BaseRecyclerView mRv; - - private final boolean mIsRtl; - - // The inset is the buffer around which a point will still register as a click on the scrollbar - private final int mTouchInset; - private final int mMinWidth; private final int mMaxWidth; private final int mThumbPadding; + /** Keeps the last known scrolling delta/velocity along y-axis. */ + private int mDy = 0; + private final float mDeltaThreshold; + + private final ViewConfiguration mConfig; + // Current width of the track private int mWidth; private ObjectAnimator mWidthAnimator; private final Paint mThumbPaint; - private final int mThumbHeight; + protected final int mThumbHeight; private final Paint mTrackPaint; private float mLastTouchY; private boolean mIsDragging; private boolean mIsThumbDetached; - private boolean mCanThumbDetach; + private final boolean mCanThumbDetach; private boolean mIgnoreDragGesture; // This is the offset from the top of the scrollbar when the user first starts touching. To // prevent jumping, this offset is applied as the user scrolls. - private int mTouchOffsetY; - private int mThumbOffsetY; + protected int mTouchOffsetY; + protected int mThumbOffsetY; // Fast scroller popup private TextView mPopupView; private boolean mPopupVisible; private String mPopupSectionName; - public BaseRecyclerViewFastScrollBar(BaseRecyclerView rv, Resources res) { - mRv = rv; + protected BaseRecyclerView mRv; + + private int mDownX; + private int mDownY; + private int mLastY; + + public RecyclerViewFastScroller(Context context) { + this(context, null); + } + + public RecyclerViewFastScroller(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public RecyclerViewFastScroller(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mTrackPaint = new Paint(); - mTrackPaint.setColor(Themes.getAttrColor(rv.getContext(), android.R.attr.textColorPrimary)); + mTrackPaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary)); mTrackPaint.setAlpha(MAX_TRACK_ALPHA); mThumbPaint = new Paint(); mThumbPaint.setAntiAlias(true); - mThumbPaint.setColor(Themes.getColorAccent(rv.getContext())); + mThumbPaint.setColor(Themes.getColorAccent(context)); mThumbPaint.setStyle(Paint.Style.FILL); + Resources res = getResources(); mWidth = mMinWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_min_width); mMaxWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_max_width); mThumbPadding = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_padding); mThumbHeight = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_height); - mTouchInset = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_touch_inset); - mIsRtl = Utilities.isRtl(res); + mConfig = ViewConfiguration.get(context); + mDeltaThreshold = res.getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP; + + TypedArray ta = + context.obtainStyledAttributes(attrs, R.styleable.RecyclerViewFastScroller, defStyleAttr, 0); + mCanThumbDetach = ta.getBoolean(R.styleable.RecyclerViewFastScroller_canThumbDetach, false); + ta.recycle(); } - public void setPopupView(View popup) { - mPopupView = (TextView) popup; - mPopupView.setBackground(new FastScrollThumbDrawable(mThumbPaint, mIsRtl)); - } + public void setRecyclerView(BaseRecyclerView rv, TextView popupView) { + mRv = rv; + mRv.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + mDy = dy; - public void setDetachThumbOnFastScroll() { - mCanThumbDetach = true; + // TODO(winsonc): If we want to animate the section heads while scrolling, we can + // initiate that here if the recycler view scroll state is not + // RecyclerView.SCROLL_STATE_IDLE. + + mRv.onUpdateScrollbar(dy); + } + }); + + mPopupView = popupView; + mPopupView.setBackground( + new FastScrollThumbDrawable(mThumbPaint, Utilities.isRtl(getResources()))); } public void reattachThumbToScroll() { mIsThumbDetached = false; } - private int getDrawLeft() { - return mIsRtl ? 0 : (mRv.getWidth() - mMaxWidth); - } - public void setThumbOffsetY(int y) { if (mThumbOffsetY == y) { return; } - - // Invalidate the previous and new thumb area - int drawLeft = getDrawLeft(); - mTmpRect.set(drawLeft, mThumbOffsetY, drawLeft + mMaxWidth, mThumbOffsetY + mThumbHeight); mThumbOffsetY = y; - mTmpRect.union(drawLeft, mThumbOffsetY, drawLeft + mMaxWidth, mThumbOffsetY + mThumbHeight); - mTmpRect.offset(0, mRv.getPaddingTop()); - mRv.invalidate(mTmpRect); + invalidate(); } public int getThumbOffsetY() { @@ -152,12 +179,8 @@ public class BaseRecyclerViewFastScrollBar { if (mWidth == width) { return; } - int left = getDrawLeft(); - int top = mRv.getPaddingTop(); - // Invalidate the whole scroll bar area. - mRv.invalidate(left, top, left + mMaxWidth, top + mRv.getScrollbarTrackHeight()); - mWidth = width; + invalidate(); } public int getThumbHeight() { @@ -176,37 +199,48 @@ public class BaseRecyclerViewFastScrollBar { * Handles the touch event and determines whether to show the fast scroller (or updates it if * it is already showing). */ - public void handleTouchEvent(MotionEvent ev, int downX, int downY, int lastY) { - ViewConfiguration config = ViewConfiguration.get(mRv.getContext()); - - int action = ev.getAction(); + public boolean handleTouchEvent(MotionEvent ev) { + int x = (int) ev.getX(); int y = (int) ev.getY(); - switch (action) { + switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: - if (isNearThumb(downX, downY)) { - mTouchOffsetY = downY - mThumbOffsetY; + // Keep track of the down positions + mDownX = x; + mDownY = mLastY = y; + + if ((Math.abs(mDy) < mDeltaThreshold && + mRv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) { + // now the touch events are being passed to the {@link WidgetCell} until the + // touch sequence goes over the touch slop. + mRv.stopScroll(); + } + if (isNearThumb(x, y)) { + mTouchOffsetY = mDownY - mThumbOffsetY; } else if (FeatureFlags.LAUNCHER3_DIRECT_SCROLL && mRv.supportsFastScrolling() - && isNearScrollBar(downX)) { - calcTouchOffsetAndPrepToFastScroll(downY, lastY); - updateFastScrollSectionNameAndThumbOffset(lastY, y); + && isNearScrollBar(mDownX)) { + calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY); + updateFastScrollSectionNameAndThumbOffset(mLastY, y); } break; case MotionEvent.ACTION_MOVE: + mLastY = y; + // Check if we should start scrolling, but ignore this fastscroll gesture if we have // exceeded some fixed movement - mIgnoreDragGesture |= Math.abs(y - downY) > config.getScaledPagingTouchSlop(); + mIgnoreDragGesture |= Math.abs(y - mDownY) > mConfig.getScaledPagingTouchSlop(); if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() && - isNearThumb(downX, lastY) && - Math.abs(y - downY) > config.getScaledTouchSlop()) { - calcTouchOffsetAndPrepToFastScroll(downY, lastY); + isNearThumb(mDownX, mLastY) && + Math.abs(y - mDownY) > mConfig.getScaledTouchSlop()) { + calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY); } if (mIsDragging) { - updateFastScrollSectionNameAndThumbOffset(lastY, y); + updateFastScrollSectionNameAndThumbOffset(mLastY, y); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: + mRv.onFastScrollCompleted(); mTouchOffsetY = 0; mLastTouchY = 0; mIgnoreDragGesture = false; @@ -217,6 +251,7 @@ public class BaseRecyclerViewFastScrollBar { } break; } + return mIsDragging; } private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) { @@ -245,25 +280,25 @@ public class BaseRecyclerViewFastScrollBar { setThumbOffsetY((int) mLastTouchY); } - public void draw(Canvas canvas) { + public void onDraw(Canvas canvas) { if (mThumbOffsetY < 0) { return; } int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - if (!mIsRtl) { - canvas.translate(mRv.getWidth() - mWidth, 0); - } - canvas.translate(0, mRv.getPaddingTop()); + canvas.translate(getWidth() / 2, mRv.getPaddingTop()); // Draw the track - canvas.drawRoundRect(0, 0, mWidth, mRv.getScrollbarTrackHeight(), + float halfW = mWidth / 2; + canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(), mWidth, mWidth, mTrackPaint); - canvas.translate(-mThumbPadding, mThumbOffsetY); + canvas.translate(0, mThumbOffsetY); + halfW += mThumbPadding; float r = mWidth + mThumbPadding + mThumbPadding; - canvas.drawRoundRect(0, 0, r, mThumbHeight, r, r, mThumbPaint); + canvas.drawRoundRect(-halfW, 0, halfW, mThumbHeight, r, r, mThumbPaint); canvas.restoreToCount(saveCount); } + /** * Animates the width of the scrollbar. */ @@ -281,19 +316,25 @@ public class BaseRecyclerViewFastScrollBar { /** * Returns whether the specified point is inside the thumb bounds. */ - public boolean isNearThumb(int x, int y) { - int left = getDrawLeft(); - mTmpRect.set(left, mThumbOffsetY, left + mMaxWidth, mThumbOffsetY + mThumbHeight); - mTmpRect.inset(mTouchInset, mTouchInset); - return mTmpRect.contains(x, y); + private boolean isNearThumb(int x, int y) { + int offset = y - mRv.getPaddingTop() - mThumbOffsetY; + + return x >= 0 && x < getWidth() && offset >= 0 && offset <= mThumbHeight; + } + + /** + * Returns true if AllAppsTransitionController can handle vertical motion + * beginning at this point. + */ + public boolean shouldBlockIntercept(int x, int y) { + return isNearThumb(x, y); } /** * Returns whether the specified x position is near the scroll bar. */ public boolean isNearScrollBar(int x) { - int left = getDrawLeft(); - return x >= left && x <= left + mMaxWidth; + return x >= (getWidth() - mMaxWidth) / 2 && x <= (getWidth() + mMaxWidth) / 2; } private void animatePopupVisibility(boolean visible) { @@ -306,7 +347,8 @@ public class BaseRecyclerViewFastScrollBar { private void updatePopupY(int lastTouchY) { int height = mPopupView.getHeight(); - float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height); + float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height) + + mRv.getPaddingTop(); top = Utilities.boundToRange(top, mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height); mPopupView.setTranslationY(top);