From 2b35905ea797821e99fe3f42ebcc6c80c972d9dc Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Wed, 5 Jul 2017 14:58:34 -0700 Subject: [PATCH] Add overscroll to the top of All Apps. * Overscroll at the top of all apps will occur when the user scrolls up, hits the top, and continues to scroll up. * Fixed bug where All Apps jumps when the user enters overscroll from a scroll that doesn't start at the bottom. * Fix bug where AllAppsRecyclerView stays translated even after the user has finished dragging. Bug: 62628421 Change-Id: Ia1d230a7cc07a7cf8c1a7c5211a025034ae5f6df --- .../allapps/AllAppsContainerView.java | 17 +++---- .../allapps/AllAppsRecyclerView.java | 45 +++++++++++++++---- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 4fb0b86c5a..ccef4f2f5b 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -410,22 +410,19 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - if (mScrollState == RecyclerView.SCROLL_STATE_DRAGGING - || (dx == 0 && dy == 0)) { + if (mScrollState == RecyclerView.SCROLL_STATE_DRAGGING || (dx == 0 && dy == 0)) { if (mSpringAnimationHandler.isRunning()){ mSpringAnimationHandler.skipToEnd(); } return; } - int first = mLayoutManager.findFirstVisibleItemPosition(); - int last = mLayoutManager.findLastVisibleItemPosition(); - - // We only show the spring animation when at the top or bottom, so we wait until the - // first or last row is visible to ensure that all animations run in sync. - boolean scrollUp = dy < 0; - if ((first == 0 && scrollUp) || (last == mAdapter.getItemCount() - 1 && dy > 0)) { - mSpringAnimationHandler.animateToFinalPosition(0, scrollUp ? 1 : -1); + // We only start the spring animation when we fling and hit the top/bottom, to ensure + // that all of the animations start at the same time. + if (dy < 0 && !mAppsRecyclerView.canScrollVertically(-1)) { + mSpringAnimationHandler.animateToFinalPosition(0, 1); + } else if (dy > 0 && !mAppsRecyclerView.canScrollVertically(1)) { + mSpringAnimationHandler.animateToFinalPosition(0, -1); } } diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index ff8de88eb8..fb785fbb07 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -96,8 +96,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView { mOverScrollHelper = new OverScrollHelper(); mPullDetector = new VerticalPullDetector(getContext()); mPullDetector.setListener(mOverScrollHelper); - mPullDetector.setDetectableScrollConditions(VerticalPullDetector.DIRECTION_UP - | VerticalPullDetector.DIRECTION_DOWN, true); + mPullDetector.setDetectableScrollConditions(VerticalPullDetector.DIRECTION_BOTH, true); } public void setSpringAnimationHandler(SpringAnimationHandler springAnimationHandler) { @@ -487,28 +486,53 @@ public class AllAppsRecyclerView extends BaseRecyclerView { private boolean mIsInOverScroll; + // We use this value to calculate the actual amount the user has overscrolled. + private float mFirstDisplacement = 0; + + private boolean mAlreadyScrollingUp; + private int mFirstScrollYOnScrollUp; + @Override public void onDragStart(boolean start) { } @Override public boolean onDrag(float displacement, float velocity) { - // We are in overscroll iff we are trying to drag further down when we're already at - // the bottom of All Apps. - mIsInOverScroll = !canScrollVertically(1) && displacement < 0 - && !mScrollbar.isDraggingThumb(); + boolean isScrollingUp = displacement > 0; + if (isScrollingUp) { + if (!mAlreadyScrollingUp) { + mFirstScrollYOnScrollUp = getCurrentScrollY(); + mAlreadyScrollingUp = true; + } + } else { + mAlreadyScrollingUp = false; + } + + // Only enter overscroll if the user is interacting with the RecyclerView directly + // and if one of the following criteria are met: + // - User scrolls down when they're already at the bottom. + // - User starts scrolling up, hits the top, and continues scrolling up. + mIsInOverScroll = !mScrollbar.isDraggingThumb() && + ((!canScrollVertically(1) && displacement < 0) || + (!canScrollVertically(-1) && isScrollingUp && mFirstScrollYOnScrollUp != 0)); if (mIsInOverScroll) { - displacement = getDampedOverScroll(displacement); - setContentTranslationY(displacement); + if (Float.compare(mFirstDisplacement, 0) == 0) { + // Because users can scroll before entering overscroll, we need to + // subtract the amount where the user was not in overscroll. + mFirstDisplacement = displacement; + } + float overscrollY = displacement - mFirstDisplacement; + setContentTranslationY(getDampedOverScroll(overscrollY)); } + return mIsInOverScroll; } @Override public void onDragEnd(float velocity, boolean fling) { float y = getContentTranslationY(); - if (mIsInOverScroll && Float.compare(y, 0) != 0) { + if (Float.compare(y, 0) != 0) { if (FeatureFlags.LAUNCHER3_PHYSICS) { // We calculate our own velocity to give the springs the desired effect. velocity = y / getDampedOverScroll(getHeight()) * MAX_RELEASE_VELOCITY; @@ -523,6 +547,9 @@ public class AllAppsRecyclerView extends BaseRecyclerView { .start(); } mIsInOverScroll = false; + mFirstDisplacement = 0; + mFirstScrollYOnScrollUp = 0; + mAlreadyScrollingUp = false; } public boolean isInOverScroll() {