Merge "Using inbuild smooth scroller instead of custom fastscrolling logic" into ub-launcher3-rvc-dev
diff --git a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
index 3ee1293..f97eb28 100644
--- a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
+++ b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
@@ -15,206 +15,90 @@
*/
package com.android.launcher3.allapps;
-import com.android.launcher3.util.Thunk;
+import androidx.recyclerview.widget.LinearSmoothScroller;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
-import java.util.HashSet;
-import java.util.List;
+import com.android.launcher3.allapps.AlphabeticalAppsList.FastScrollSectionInfo;
-import androidx.recyclerview.widget.RecyclerView;
+public class AllAppsFastScrollHelper {
-public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallback {
+ private static final int NO_POSITION = -1;
- private static final int INITIAL_TOUCH_SETTLING_DURATION = 100;
- private static final int REPEAT_TOUCH_SETTLING_DURATION = 200;
+ private int mTargetFastScrollPosition = NO_POSITION;
private AllAppsRecyclerView mRv;
- private AlphabeticalAppsList mApps;
+ private ViewHolder mLastSelectedViewHolder;
- // Keeps track of the current and targeted fast scroll section (the section to scroll to after
- // the initial delay)
- int mTargetFastScrollPosition = -1;
- @Thunk String mCurrentFastScrollSection;
- @Thunk String mTargetFastScrollSection;
-
- // The settled states affect the delay before the fast scroll animation is applied
- private boolean mHasFastScrollTouchSettled;
- private boolean mHasFastScrollTouchSettledAtLeastOnce;
-
- // Set of all views animated during fast scroll. We keep track of these ourselves since there
- // is no way to reset a view once it gets scrapped or recycled without other hacks
- private HashSet<RecyclerView.ViewHolder> mTrackedFastScrollViews = new HashSet<>();
-
- // Smooth fast-scroll animation frames
- @Thunk int mFastScrollFrameIndex;
- @Thunk final int[] mFastScrollFrames = new int[10];
-
- /**
- * This runnable runs a single frame of the smooth scroll animation and posts the next frame
- * if necessary.
- */
- @Thunk Runnable mSmoothSnapNextFrameRunnable = new Runnable() {
- @Override
- public void run() {
- if (mFastScrollFrameIndex < mFastScrollFrames.length) {
- mRv.scrollBy(0, mFastScrollFrames[mFastScrollFrameIndex]);
- mFastScrollFrameIndex++;
- mRv.postOnAnimation(mSmoothSnapNextFrameRunnable);
- }
- }
- };
-
- /**
- * This runnable updates the current fast scroll section to the target fastscroll section.
- */
- Runnable mFastScrollToTargetSectionRunnable = new Runnable() {
- @Override
- public void run() {
- // Update to the target section
- mCurrentFastScrollSection = mTargetFastScrollSection;
- mHasFastScrollTouchSettled = true;
- mHasFastScrollTouchSettledAtLeastOnce = true;
- updateTrackedViewsFastScrollFocusState();
- }
- };
-
- public AllAppsFastScrollHelper(AllAppsRecyclerView rv, AlphabeticalAppsList apps) {
+ public AllAppsFastScrollHelper(AllAppsRecyclerView rv) {
mRv = rv;
- mApps = apps;
- }
-
- public void onSetAdapter(AllAppsGridAdapter adapter) {
- adapter.setBindViewCallback(this);
}
/**
* Smooth scrolls the recycler view to the given section.
- *
- * @return whether the fastscroller can scroll to the new section.
*/
- public boolean smoothScrollToSection(int scrollY, int availableScrollHeight,
- AlphabeticalAppsList.FastScrollSectionInfo info) {
- if (mTargetFastScrollPosition != info.fastScrollToItem.position) {
- mTargetFastScrollPosition = info.fastScrollToItem.position;
- smoothSnapToPosition(scrollY, availableScrollHeight, info);
- return true;
+ public void smoothScrollToSection(FastScrollSectionInfo info) {
+ if (mTargetFastScrollPosition == info.fastScrollToItem.position) {
+ return;
}
- return false;
- }
-
- /**
- * Smoothly snaps to a given position. We do this manually by calculating the keyframes
- * ourselves and animating the scroll on the recycler view.
- */
- private void smoothSnapToPosition(int scrollY, int availableScrollHeight,
- AlphabeticalAppsList.FastScrollSectionInfo info) {
- mRv.removeCallbacks(mSmoothSnapNextFrameRunnable);
- mRv.removeCallbacks(mFastScrollToTargetSectionRunnable);
-
- trackAllChildViews();
- if (mHasFastScrollTouchSettled) {
- // In this case, the user has already settled once (and the fast scroll state has
- // animated) and they are just fine-tuning their section from the last section, so
- // we should make it feel fast and update immediately.
- mCurrentFastScrollSection = info.sectionName;
- mTargetFastScrollSection = null;
- updateTrackedViewsFastScrollFocusState();
- } else {
- // Otherwise, the user has scrubbed really far, and we don't want to distract the user
- // with the flashing fast scroll state change animation in addition to the fast scroll
- // section popup, so reset the views to normal, and wait for the touch to settle again
- // before animating the fast scroll state.
- mCurrentFastScrollSection = null;
- mTargetFastScrollSection = info.sectionName;
- mHasFastScrollTouchSettled = false;
- updateTrackedViewsFastScrollFocusState();
-
- // Delay scrolling to a new section until after some duration. If the user has been
- // scrubbing a while and makes multiple big jumps, then reduce the time needed for the
- // fast scroll to settle so it doesn't feel so long.
- mRv.postDelayed(mFastScrollToTargetSectionRunnable,
- mHasFastScrollTouchSettledAtLeastOnce ?
- REPEAT_TOUCH_SETTLING_DURATION :
- INITIAL_TOUCH_SETTLING_DURATION);
- }
-
- // Calculate the full animation from the current scroll position to the final scroll
- // position, and then run the animation for the duration. If we are scrolling to the
- // first fast scroll section, then just scroll to the top of the list itself.
- List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections =
- mApps.getFastScrollerSections();
- int newPosition = info.fastScrollToItem.position;
- int newScrollY = fastScrollSections.size() > 0 && fastScrollSections.get(0) == info
- ? 0
- : Math.min(availableScrollHeight, mRv.getCurrentScrollY(newPosition, 0));
- int numFrames = mFastScrollFrames.length;
- int deltaY = newScrollY - scrollY;
- float ySign = Math.signum(deltaY);
- int step = (int) (ySign * Math.ceil((float) Math.abs(deltaY) / numFrames));
- for (int i = 0; i < numFrames; i++) {
- // TODO(winsonc): We can interpolate this as well.
- mFastScrollFrames[i] = (int) (ySign * Math.min(Math.abs(step), Math.abs(deltaY)));
- deltaY -= step;
- }
- mFastScrollFrameIndex = 0;
- mRv.postOnAnimation(mSmoothSnapNextFrameRunnable);
+ mTargetFastScrollPosition = info.fastScrollToItem.position;
+ mRv.getLayoutManager().startSmoothScroll(new MyScroller(mTargetFastScrollPosition));
}
public void onFastScrollCompleted() {
- // TODO(winsonc): Handle the case when the user scrolls and releases before the animation
- // runs
-
- // Stop animating the fast scroll position and state
- mRv.removeCallbacks(mSmoothSnapNextFrameRunnable);
- mRv.removeCallbacks(mFastScrollToTargetSectionRunnable);
-
- // Reset the tracking variables
- mHasFastScrollTouchSettled = false;
- mHasFastScrollTouchSettledAtLeastOnce = false;
- mCurrentFastScrollSection = null;
- mTargetFastScrollSection = null;
- mTargetFastScrollPosition = -1;
-
- updateTrackedViewsFastScrollFocusState();
- mTrackedFastScrollViews.clear();
+ mTargetFastScrollPosition = NO_POSITION;
+ setLastHolderSelected(false);
+ mLastSelectedViewHolder = null;
}
- @Override
- public void onBindView(AllAppsGridAdapter.ViewHolder holder) {
- // Update newly bound views to the current fast scroll state if we are fast scrolling
- if (mCurrentFastScrollSection != null || mTargetFastScrollSection != null) {
- mTrackedFastScrollViews.add(holder);
+
+ private void setLastHolderSelected(boolean isSelected) {
+ if (mLastSelectedViewHolder != null) {
+ mLastSelectedViewHolder.itemView.setActivated(isSelected);
+ mLastSelectedViewHolder.setIsRecyclable(!isSelected);
}
}
- /**
- * Starts tracking all the recycler view's children which are FastScrollFocusableViews.
- */
- private void trackAllChildViews() {
- int childCount = mRv.getChildCount();
- for (int i = 0; i < childCount; i++) {
- RecyclerView.ViewHolder viewHolder = mRv.getChildViewHolder(mRv.getChildAt(i));
- if (viewHolder != null) {
- mTrackedFastScrollViews.add(viewHolder);
- }
- }
- }
+ private class MyScroller extends LinearSmoothScroller {
- /**
- * Updates the fast scroll focus on all the children.
- */
- private void updateTrackedViewsFastScrollFocusState() {
- for (RecyclerView.ViewHolder viewHolder : mTrackedFastScrollViews) {
- int pos = viewHolder.getAdapterPosition();
- boolean isActive = false;
- if (mCurrentFastScrollSection != null
- && pos > RecyclerView.NO_POSITION
- && pos < mApps.getAdapterItems().size()) {
- AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(pos);
- isActive = item != null &&
- mCurrentFastScrollSection.equals(item.sectionName) &&
- item.position == mTargetFastScrollPosition;
+ private final int mTargetPosition;
+
+ public MyScroller(int targetPosition) {
+ super(mRv.getContext());
+
+ mTargetPosition = targetPosition;
+ setTargetPosition(targetPosition);
+ }
+
+ @Override
+ protected int getVerticalSnapPreference() {
+ return SNAP_TO_START;
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (mTargetPosition != mTargetFastScrollPosition) {
+ // Target changed, before the last scroll can finish
+ return;
}
- viewHolder.itemView.setActivated(isActive);
+
+ ViewHolder currentHolder = mRv.findViewHolderForAdapterPosition(mTargetPosition);
+ if (currentHolder == mLastSelectedViewHolder) {
+ return;
+ }
+
+ setLastHolderSelected(false);
+ mLastSelectedViewHolder = currentHolder;
+ setLastHolderSelected(true);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (mTargetPosition != mTargetFastScrollPosition) {
+ setLastHolderSelected(false);
+ mLastSelectedViewHolder = null;
+ }
}
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 4aebec0..3afa756 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -71,11 +71,6 @@
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
-
- public interface BindViewCallback {
- void onBindView(ViewHolder holder);
- }
-
/**
* ViewHolder for each icon.
*/
@@ -186,7 +181,6 @@
private int mAppsPerRow;
- private BindViewCallback mBindViewCallback;
private OnFocusChangeListener mIconFocusListener;
// The text to show when there are no search results and no market search handler.
@@ -248,13 +242,6 @@
}
/**
- * Sets the callback for when views are bound.
- */
- public void setBindViewCallback(BindViewCallback cb) {
- mBindViewCallback = cb;
- }
-
- /**
* Returns the grid layout manager.
*/
public GridLayoutManager getLayoutManager() {
@@ -319,9 +306,6 @@
// nothing to do
break;
}
- if (mBindViewCallback != null) {
- mBindViewCallback.onBindView(holder);
- }
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 069472f..cbf02b7 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -53,12 +53,12 @@
public class AllAppsRecyclerView extends BaseRecyclerView implements LogContainerProvider {
private AlphabeticalAppsList mApps;
- private AllAppsFastScrollHelper mFastScrollHelper;
private final int mNumAppsPerRow;
// The specific view heights that we use to calculate scroll
- private SparseIntArray mViewHeights = new SparseIntArray();
- private SparseIntArray mCachedScrollPositions = new SparseIntArray();
+ private final SparseIntArray mViewHeights = new SparseIntArray();
+ private final SparseIntArray mCachedScrollPositions = new SparseIntArray();
+ private final AllAppsFastScrollHelper mFastScrollHelper;
// The empty-search result background
private AllAppsBackgroundDrawable mEmptySearchBackground;
@@ -85,6 +85,7 @@
mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
R.dimen.all_apps_empty_search_bg_top_offset);
mNumAppsPerRow = LauncherAppState.getIDP(context).numColumns;
+ mFastScrollHelper = new AllAppsFastScrollHelper(this);
}
/**
@@ -92,7 +93,6 @@
*/
public void setApps(AlphabeticalAppsList apps) {
mApps = apps;
- mFastScrollHelper = new AllAppsFastScrollHelper(this, apps);
}
public AlphabeticalAppsList getApps() {
@@ -221,9 +221,6 @@
return "";
}
- // Stop the scroller if it is scrolling
- stopScroll();
-
// Find the fastscroll section that maps to this touch fraction
List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections =
mApps.getFastScrollerSections();
@@ -236,10 +233,7 @@
lastInfo = info;
}
- // Update the fast scroll
- int scrollY = getCurrentScrollY();
- int availableScrollHeight = getAvailableScrollHeight();
- mFastScrollHelper.smoothScrollToSection(scrollY, availableScrollHeight, lastInfo);
+ mFastScrollHelper.smoothScrollToSection(lastInfo);
return lastInfo.sectionName;
}
@@ -257,7 +251,6 @@
mCachedScrollPositions.clear();
}
});
- mFastScrollHelper.onSetAdapter((AllAppsGridAdapter) adapter);
}
@Override
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 5653801..6a83332 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -67,7 +67,6 @@
private final static int MAX_TRACK_ALPHA = 30;
private final static int SCROLL_BAR_VIS_DURATION = 150;
- private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 0.75f;
private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
Collections.singletonList(new Rect());
@@ -184,7 +183,7 @@
if (mThumbOffsetY == y) {
return;
}
- updatePopupY((int) y);
+ updatePopupY(y);
mThumbOffsetY = y;
invalidate();
}
@@ -237,7 +236,7 @@
} else if (mRv.supportsFastScrolling()
&& isNearScrollBar(mDownX)) {
calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
- updateFastScrollSectionNameAndThumbOffset(mLastY, y);
+ updateFastScrollSectionNameAndThumbOffset(y);
}
break;
case MotionEvent.ACTION_MOVE:
@@ -252,7 +251,7 @@
calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
}
if (mIsDragging) {
- updateFastScrollSectionNameAndThumbOffset(mLastY, y);
+ updateFastScrollSectionNameAndThumbOffset(y);
}
break;
case MotionEvent.ACTION_UP:
@@ -281,7 +280,7 @@
showActiveScrollbar(true);
}
- private void updateFastScrollSectionNameAndThumbOffset(int lastY, int y) {
+ private void updateFastScrollSectionNameAndThumbOffset(int y) {
// Update the fastscroller section name at this touch position
int bottom = mRv.getScrollbarTrackHeight() - mThumbHeight;
float boundedY = (float) Math.max(0, Math.min(bottom, y - mTouchOffsetY));