Moves Search results into a separate RV (take 2).
Bug: 206905515
Test: Manually verified b/230648542 did not resurface. Tested
on phone and tablet with and without work profile.
Change-Id: If724f635286b9dff2c64255f9ece3568a5cb4ea9
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
new file mode 100644
index 0000000..f117069
--- /dev/null
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2015 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.views.RecyclerViewFastScroller;
+
+
+/**
+ * A base {@link RecyclerView}, which does the following:
+ * <ul>
+ * <li> NOT intercept a touch unless the scrolling velocity is below a predefined threshold.
+ * <li> Enable fast scroller.
+ * </ul>
+ */
+public abstract class FastScrollRecyclerView extends RecyclerView {
+
+ protected RecyclerViewFastScroller mScrollbar;
+
+ public FastScrollRecyclerView(Context context) {
+ this(context, null);
+ }
+
+ public FastScrollRecyclerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FastScrollRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ bindFastScrollbar();
+ }
+
+ public void bindFastScrollbar() {
+ ViewGroup parent = (ViewGroup) getParent().getParent();
+ mScrollbar = parent.findViewById(R.id.fast_scroller);
+ mScrollbar.setRecyclerView(this, parent.findViewById(R.id.fast_scroller_popup));
+ onUpdateScrollbar(0);
+ }
+
+ @Nullable
+ public RecyclerViewFastScroller getScrollbar() {
+ return mScrollbar;
+ }
+
+ public int getScrollBarTop() {
+ return getPaddingTop();
+ }
+
+ /**
+ * Returns the height of the fast scroll bar
+ */
+ public int getScrollbarTrackHeight() {
+ return mScrollbar.getHeight() - getScrollBarTop() - getPaddingBottom();
+ }
+
+ /**
+ * Returns the available scroll height:
+ * AvailableScrollHeight = Total height of the all items - last page height
+ */
+ protected abstract int getAvailableScrollHeight();
+
+ /**
+ * Returns the available scroll bar height:
+ * AvailableScrollBarHeight = Total height of the visible view - thumb height
+ */
+ protected int getAvailableScrollBarHeight() {
+ int availableScrollBarHeight = getScrollbarTrackHeight() - mScrollbar.getThumbHeight();
+ return availableScrollBarHeight;
+ }
+
+ /**
+ * Updates the scrollbar thumb offset to match the visible scroll of the recycler view. It does
+ * this by mapping the available scroll area of the recycler view to the available space for the
+ * scroll bar.
+ *
+ * @param scrollY the current scroll y
+ */
+ protected void synchronizeScrollBarThumbOffsetToViewScroll(int scrollY,
+ int availableScrollHeight) {
+ // Only show the scrollbar if there is height to be scrolled
+ if (availableScrollHeight <= 0) {
+ mScrollbar.setThumbOffsetY(-1);
+ return;
+ }
+
+ // Calculate the current scroll position, the scrollY of the recycler view accounts for the
+ // view padding, while the scrollBarY is drawn right up to the background padding (ignoring
+ // padding)
+ int scrollBarY =
+ (int) (((float) scrollY / availableScrollHeight) * getAvailableScrollBarHeight());
+
+ // Calculate the position and size of the scroll bar
+ mScrollbar.setThumbOffsetY(scrollBarY);
+ }
+
+ /**
+ * Returns whether the view itself will handle the touch event or not.
+ * @param ev MotionEvent in {@param eventSource}
+ */
+ public boolean shouldContainerScroll(MotionEvent ev, View eventSource) {
+ float[] point = new float[2];
+ point[0] = ev.getX();
+ point[1] = ev.getY();
+ Utilities.mapCoordInSelfToDescendant(mScrollbar, eventSource, point);
+ // IF the MotionEvent is inside the thumb, container should not be pulled down.
+ if (mScrollbar.shouldBlockIntercept((int) point[0], (int) point[1])) {
+ return false;
+ }
+
+ // IF scroller is at the very top OR there is no scroll bar because there is probably not
+ // enough items to scroll, THEN it's okay for the container to be pulled down.
+ if (getCurrentScrollY() == 0) {
+ return true;
+ }
+ return getAdapter() == null || getAdapter().getItemCount() == 0;
+ }
+
+ /**
+ * @return whether fast scrolling is supported in the current state.
+ */
+ public boolean supportsFastScrolling() {
+ return true;
+ }
+
+ /**
+ * Maps the touch (from 0..1) to the adapter position that should be visible.
+ * <p>Override in each subclass of this base class.
+ *
+ * @return the scroll top of this recycler view.
+ */
+ public abstract int getCurrentScrollY();
+
+ /**
+ * Maps the touch (from 0..1) to the adapter position that should be visible.
+ * <p>Override in each subclass of this base class.
+ */
+ public abstract String scrollToPositionAtProgress(float touchFraction);
+
+ /**
+ * Updates the bounds for the scrollbar.
+ * <p>Override in each subclass of this base class.
+ */
+ public abstract void onUpdateScrollbar(int dy);
+
+ /**
+ * <p>Override in each subclass of this base class.
+ */
+ public void onFastScrollCompleted() {}
+
+ @Override
+ public void onScrollStateChanged(int state) {
+ super.onScrollStateChanged(state);
+
+ if (state == SCROLL_STATE_IDLE) {
+ AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ if (isLayoutSuppressed()) info.setScrollable(false);
+ }
+
+ /**
+ * Scrolls this recycler view to the top.
+ */
+ public void scrollToTop() {
+ if (mScrollbar != null) {
+ mScrollbar.reattachThumbToScroll();
+ }
+ scrollToPosition(0);
+ }
+}