Refactors Search results into separate RV for Toast.

This will help enable transitions between A-Z apps lists and
search results because both can be seen simultaneously and
manipulated independently.

Some high level items of the refactor:
 - SearchRecyclerView is added; logic that populated the main
   (personal) tab with search results was simply redirected to
   this RV instead.
 - BaseAllAppsContainerView added isSearching() method. Returns
   false, and ActivityAllAppsContainerView overrides (as search
   is handled there).
 - Renamed BaseRecyclerView to FastScrollRecyclerView to better
   describe what it does. SearchRecyclerView extends this, but
   returns false for supportsFastScrolling().
 - AlphabeticalAppsList#mAllAppsStore is now optional, so the
   Search RV doesn't need to store/listen to apps. Note this
   doesn't affect the predicted app row which is still updated
   if one of the predicted apps is uninstalled (I tested this).

Future work:
 - Determine why dispatchRestoreInstanceState is not called for
   BaseAllAppsContainerView. Save is called, e.g. on rotation.
   Effect of restore not called: rotating while searching goes
   back to A-Z list.
 - Keep suggested apps in Header while searching. Currently they
   are rendered in the SearchRV above search results, as before.
 - Potentially extract Personal/Work tabs to move independently of
   header.
 - AlphabeticalAppsList is a misleading name because it can also
   contains search results. However, things are pretty intertwined
   between that and BaseAllAppsAdapter (effectively a circular
   dependency), so I figured cleaning all that up was out of the
   immediate scope of this refactor, which is mainly meant to
   unblock transition work.

Bug: 206905515
Test: Manually checked for regressions, ran tests.
Change-Id: I4d3757c8a8f9b774956ca6be541dd4fcdad1de13
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
similarity index 95%
rename from src/com/android/launcher3/BaseRecyclerView.java
rename to src/com/android/launcher3/FastScrollRecyclerView.java
index 9369bdc..a60d143 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -37,19 +37,19 @@
  *   <li> Enable fast scroller.
  * </ul>
  */
-public abstract class BaseRecyclerView extends RecyclerView  {
+public abstract class FastScrollRecyclerView extends RecyclerView  {
 
     protected RecyclerViewFastScroller mScrollbar;
 
-    public BaseRecyclerView(Context context) {
+    public FastScrollRecyclerView(Context context) {
         this(context, null);
     }
 
-    public BaseRecyclerView(Context context, AttributeSet attrs) {
+    public FastScrollRecyclerView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public FastScrollRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
     }
 
@@ -206,4 +206,4 @@
         }
         scrollToPosition(0);
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4b42ecb..463280b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2750,7 +2750,7 @@
                         packageName);
 
         if (supportsAllAppsState && isInState(LauncherState.ALL_APPS)) {
-            return getFirstMatch(Collections.singletonList(mAppsView.getActiveRecyclerView()),
+            return getFirstMatch(Collections.singletonList(mAppsView.getActiveAppsRecyclerView()),
                     preferredItem, packageAndUserAndApp);
         } else {
             List<ViewGroup> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index e279f59..16a2823 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -80,7 +80,7 @@
         OnClickListener marketSearchClickListener = (v) -> mActivityContext.startActivitySafely(v,
                 marketSearchIntent, null);
         for (int i = 0; i < mAH.size(); i++) {
-            mAH.get(i).adapter.setLastSearchQuery(query, marketSearchClickListener);
+            mAH.get(i).mAdapter.setLastSearchQuery(query, marketSearchClickListener);
         }
         mIsSearching = true;
         rebindAdapters();
@@ -142,7 +142,7 @@
 
     @Override
     public String getDescription() {
-        if (!mUsingTabs && mIsSearching) {
+        if (!mUsingTabs && isSearching()) {
             return getContext().getString(R.string.all_apps_search_results);
         } else {
             return super.getDescription();
@@ -150,8 +150,13 @@
     }
 
     @Override
-    protected boolean showTabs() {
-        return super.showTabs() && !mIsSearching;
+    protected boolean shouldShowTabs() {
+        return super.shouldShowTabs() && !isSearching();
+    }
+
+    @Override
+    public boolean isSearching() {
+        return mIsSearching;
     }
 
     @Override
@@ -173,15 +178,19 @@
     }
 
     @Override
-    protected View replaceRVContainer(boolean showTabs) {
-        View rvContainer = super.replaceRVContainer(showTabs);
+    protected View replaceAppsRVContainer(boolean showTabs) {
+        View rvContainer = super.replaceAppsRVContainer(showTabs);
 
         removeCustomRules(rvContainer);
+        removeCustomRules(getSearchRecyclerView());
         if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
             alignParentTop(rvContainer, showTabs);
+            alignParentTop(getSearchRecyclerView(), showTabs);
             layoutAboveSearchContainer(rvContainer);
+            layoutAboveSearchContainer(getSearchRecyclerView());
         } else {
             layoutBelowSearchContainer(rvContainer, showTabs);
+            layoutBelowSearchContainer(getSearchRecyclerView(), showTabs);
         }
 
         return rvContainer;
@@ -208,7 +217,7 @@
 
         float prog = Utilities.boundToRange((float) scrolledOffset / mHeaderThreshold, 0f, 1f);
         boolean bgVisible = mSearchUiManager.getBackgroundVisibility();
-        if (scrolledOffset == 0 && !mIsSearching) {
+        if (scrolledOffset == 0 && !isSearching()) {
             bgVisible = true;
         } else if (scrolledOffset > mHeaderThreshold) {
             bgVisible = false;
@@ -242,7 +251,7 @@
         int topMargin = getContext().getResources().getDimensionPixelSize(
                 R.dimen.all_apps_header_top_margin);
         if (includeTabsMargin) {
-            topMargin = topMargin + getContext().getResources().getDimensionPixelSize(
+            topMargin += getContext().getResources().getDimensionPixelSize(
                     R.dimen.all_apps_header_pill_height);
         }
         layoutParams.topMargin = topMargin;
@@ -283,9 +292,9 @@
     }
 
     @Override
-    protected BaseAllAppsAdapter getAdapter(AlphabeticalAppsList<T> mAppsList,
+    protected BaseAllAppsAdapter<T> createAdapter(AlphabeticalAppsList<T> appsList,
             BaseAdapterProvider[] adapterProviders) {
-        return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), mAppsList,
+        return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
                 adapterProviders);
     }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 7dbe711..18c6788 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -37,8 +37,8 @@
 
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.FastScrollRecyclerView;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -53,13 +53,13 @@
 /**
  * A RecyclerView with custom fast scroll support for the all apps view.
  */
-public class AllAppsRecyclerView extends BaseRecyclerView {
-    private static final String TAG = "AllAppsContainerView";
+public class AllAppsRecyclerView extends FastScrollRecyclerView {
+    protected static final String TAG = "AllAppsRecyclerView";
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_LATENCY = Utilities.isPropertyEnabled(SEARCH_LOGGING);
 
-    private AlphabeticalAppsList<?> mApps;
-    private final int mNumAppsPerRow;
+    protected AlphabeticalAppsList<?> mApps;
+    protected final int mNumAppsPerRow;
 
     // The specific view heights that we use to calculate scroll
     private final SparseIntArray mViewHeights = new SparseIntArray();
@@ -74,8 +74,8 @@
     };
 
     // The empty-search result background
-    private AllAppsBackgroundDrawable mEmptySearchBackground;
-    private int mEmptySearchBackgroundTopOffset;
+    protected AllAppsBackgroundDrawable mEmptySearchBackground;
+    protected int mEmptySearchBackgroundTopOffset;
 
     private ArrayList<View> mAutoSizedOverlays = new ArrayList<>();
 
@@ -112,7 +112,7 @@
         return mApps;
     }
 
-    private void updatePoolSize() {
+    protected void updatePoolSize() {
         DeviceProfile grid = ActivityContext.lookupContext(getContext()).getDeviceProfile();
         RecyclerView.RecycledViewPool pool = getRecycledViewPool();
         int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
@@ -137,8 +137,8 @@
             Log.d(TAG, "onDraw at = " + System.currentTimeMillis());
         }
         if (DEBUG_LATENCY) {
-            Log.d(SEARCH_LOGGING,
-                    "-- Recycle view onDraw, time stamp = " + System.currentTimeMillis());
+            Log.d(SEARCH_LOGGING,  getClass().getSimpleName() + " onDraw; time stamp = "
+                    + System.currentTimeMillis());
         }
         super.onDraw(c);
     }
@@ -223,8 +223,7 @@
                 && mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
             mEmptySearchBackground.setHotspot(e.getX(), e.getY());
         }
-        hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
-                getApplicationWindowToken());
+        hideKeyboardAsync(ActivityContext.lookupContext(getContext()), getApplicationWindowToken());
         return result;
     }
 
@@ -360,13 +359,6 @@
     }
 
     @Override
-    public boolean supportsFastScrolling() {
-        // Only allow fast scrolling when the user is not searching, since the results are not
-        // grouped in a meaningful order
-        return !mApps.hasFilter();
-    }
-
-    @Override
     public int getCurrentScrollY() {
         // Return early if there are no items or we haven't been measured
         List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
@@ -376,7 +368,7 @@
 
         // Calculate the y and offset for the item
         View child = getChildAt(0);
-        int position = getChildPosition(child);
+        int position = getChildAdapterPosition(child);
         if (position == NO_POSITION) {
             return -1;
         }
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 637a418..723bc65 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -84,7 +84,8 @@
                 @Override
                 public Float get(AllAppsTransitionController controller) {
                     if (controller.mIsTablet) {
-                        return controller.mAppsView.getRecyclerViewContainer().getTranslationY();
+                        return controller.mAppsView.getAppsRecyclerViewContainer()
+                                .getTranslationY();
                     } else {
                         return controller.getAppsViewPullbackTranslationY().get(
                                 controller.mAppsView);
@@ -94,7 +95,7 @@
                 @Override
                 public void setValue(AllAppsTransitionController controller, float translation) {
                     if (controller.mIsTablet) {
-                        controller.mAppsView.getRecyclerViewContainer().setTranslationY(
+                        controller.mAppsView.getAppsRecyclerViewContainer().setTranslationY(
                                 translation);
                     } else {
                         controller.getAppsViewPullbackTranslationY().set(controller.mAppsView,
@@ -109,7 +110,7 @@
                 @Override
                 public Float get(AllAppsTransitionController controller) {
                     if (controller.mIsTablet) {
-                        return controller.mAppsView.getRecyclerViewContainer().getAlpha();
+                        return controller.mAppsView.getAppsRecyclerViewContainer().getAlpha();
                     } else {
                         return controller.getAppsViewPullbackAlpha().getValue();
                     }
@@ -118,7 +119,7 @@
                 @Override
                 public void setValue(AllAppsTransitionController controller, float alpha) {
                     if (controller.mIsTablet) {
-                        controller.mAppsView.getRecyclerViewContainer().setAlpha(alpha);
+                        controller.mAppsView.getAppsRecyclerViewContainer().setAlpha(alpha);
                     } else {
                         controller.getAppsViewPullbackAlpha().setValue(alpha);
                     }
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 7687fea..42374b8 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
@@ -71,6 +73,7 @@
 
     // The set of apps from the system
     private final List<AppInfo> mApps = new ArrayList<>();
+    @Nullable
     private final AllAppsStore mAllAppsStore;
 
     // The number of results in current adapter
@@ -88,14 +91,16 @@
     private int mNumAppRowsInAdapter;
     private ItemInfoMatcher mItemFilter;
 
-    public AlphabeticalAppsList(Context context, AllAppsStore appsStore,
+    public AlphabeticalAppsList(Context context, @Nullable AllAppsStore appsStore,
             WorkAdapterProvider adapterProvider) {
         mAllAppsStore = appsStore;
         mActivityContext = ActivityContext.lookupContext(context);
         mAppNameComparator = new AppInfoComparator(context);
         mWorkAdapterProvider = adapterProvider;
         mNumAppsPerRowAllApps = mActivityContext.getDeviceProfile().inv.numAllAppsColumns;
-        mAllAppsStore.addUpdateListener(this);
+        if (mAllAppsStore != null) {
+            mAllAppsStore.addUpdateListener(this);
+        }
     }
 
     public void updateItemFilter(ItemInfoMatcher itemFilter) {
@@ -168,9 +173,9 @@
     }
 
     /**
-     * Returns whether there are is a filter set.
+     * Returns whether there are search results which will hide the A-Z list.
      */
-    public boolean hasFilter() {
+    public boolean hasSearchResults() {
         return !mSearchResults.isEmpty();
     }
 
@@ -178,7 +183,7 @@
      * Returns whether there are no filtered results.
      */
     public boolean hasNoFilteredResults() {
-        return hasFilter() && mAccessibilityResultsCount == 0;
+        return hasSearchResults() && mAccessibilityResultsCount == 0;
     }
 
     /**
@@ -196,13 +201,13 @@
         return true;
     }
 
-    public boolean appendSearchResults(ArrayList<AdapterItem> results) {
-        if (hasFilter() && results != null && results.size() > 0) {
+    /** Appends results to search. */
+    public void appendSearchResults(ArrayList<AdapterItem> results) {
+        if (hasSearchResults() && results != null && results.size() > 0) {
             updateSearchAdapterItems(results, mSearchResults.size());
+            mSearchResults.addAll(results);
             refreshRecyclerView();
-            return true;
         }
-        return false;
     }
 
     void updateSearchAdapterItems(ArrayList<AdapterItem> list, int offset) {
@@ -222,11 +227,14 @@
      */
     @Override
     public void onAppsUpdated() {
+        if (mAllAppsStore == null) {
+            return;
+        }
         // Sort the list of apps
         mApps.clear();
 
         for (AppInfo app : mAllAppsStore.getApps()) {
-            if (mItemFilter == null || mItemFilter.matches(app, null) || hasFilter()) {
+            if (mItemFilter == null || mItemFilter.matches(app, null) || hasSearchResults()) {
                 mApps.add(app);
             }
         }
@@ -296,7 +304,18 @@
         // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
         // ordered set of sections
 
-        if (!hasFilter()) {
+        if (hasSearchResults()) {
+            if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+                // Append the search market item
+                if (hasNoFilteredResults()) {
+                    mSearchResults.add(AdapterItem.asEmptySearch(position++));
+                } else {
+                    mSearchResults.add(AdapterItem.asAllAppsDivider(position++));
+                }
+                mSearchResults.add(AdapterItem.asMarketSearch(position++));
+            }
+            updateSearchAdapterItems(mSearchResults, 0);
+        } else {
             mAccessibilityResultsCount = mApps.size();
             if (mWorkAdapterProvider != null) {
                 position += mWorkAdapterProvider.addWorkItems(mAdapterItems);
@@ -323,18 +342,6 @@
 
                 mAdapterItems.add(appItem);
             }
-        } else {
-            updateSearchAdapterItems(mSearchResults, 0);
-            if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
-                // Append the search market item
-                if (hasNoFilteredResults()) {
-                    mAdapterItems.add(AdapterItem.asEmptySearch(position++));
-                } else {
-                    mAdapterItems.add(AdapterItem.asAllAppsDivider(position++));
-                }
-                mAdapterItems.add(AdapterItem.asMarketSearch(position++));
-
-            }
         }
         if (mNumAppsPerRowAllApps != 0) {
             // Update the number of rows in the adapter after we do all the merging (otherwise, we
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index f913aa9..6a44989 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -80,7 +80,7 @@
         OnDeviceProfileChangeListener, OnActivePageChangedListener,
         ScrimView.ScrimDrawingController {
 
-    private static final String BUNDLE_KEY_CURRENT_PAGE = "launcher.allapps.current_page";
+    protected static final String BUNDLE_KEY_CURRENT_PAGE = "launcher.allapps.current_page";
 
     public static final float PULL_MULTIPLIER = .02f;
     public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
@@ -109,6 +109,7 @@
     private int mNavBarScrimHeight = 0;
 
     private AllAppsPagedView mViewPager;
+    private SearchRecyclerView mSearchRecyclerView;
 
     protected FloatingHeaderView mHeader;
     private View mBottomSheetBackground;
@@ -141,9 +142,10 @@
                 mActivityContext.getSystemService(UserManager.class),
                 this,
                 Utilities.getPrefs(mActivityContext));
-        mAH = Arrays.asList(null, null);
-        mAH.set(AdapterHolder.MAIN, new AdapterHolder(false /* isWork */));
-        mAH.set(AdapterHolder.WORK, new AdapterHolder(true /* isWork */));
+        mAH = Arrays.asList(null, null, null);
+        mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN));
+        mAH.set(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
+        mAH.set(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
 
         mNavBarScrimPaint = new Paint();
         mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
@@ -175,7 +177,7 @@
         Bundle state = (Bundle) sparseArray.get(R.id.work_tab_state_id, null);
         if (state != null) {
             int currentPage = state.getInt(BUNDLE_KEY_CURRENT_PAGE, 0);
-            if (currentPage != 0 && mViewPager != null) {
+            if (currentPage == AdapterHolder.WORK && mViewPager != null) {
                 mViewPager.setCurrentPage(currentPage);
                 rebindAdapters();
             } else {
@@ -198,7 +200,7 @@
      */
     public void setOnIconLongClickListener(OnLongClickListener listener) {
         for (AdapterHolder holder : mAH) {
-            holder.adapter.setOnIconLongClickListener(listener);
+            holder.mAdapter.setOnIconLongClickListener(listener);
         }
     }
 
@@ -213,7 +215,7 @@
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
         for (AdapterHolder holder : mAH) {
-            holder.adapter.setAppsPerRow(dp.numShownAllAppsColumns);
+            holder.mAdapter.setAppsPerRow(dp.numShownAllAppsColumns);
             if (holder.mRecyclerView != null) {
                 // Remove all views and clear the pool, while keeping the data same. After this
                 // call, all the viewHolders will be recreated.
@@ -237,7 +239,7 @@
             }
         }
         mHasWorkApps = hasWorkApps;
-        if (!mAH.get(AdapterHolder.MAIN).mAppsList.hasFilter()) {
+        if (!mAH.get(AdapterHolder.MAIN).mAppsList.hasSearchResults()) {
             rebindAdapters();
             if (hasWorkApps) {
                 mWorkManager.reset();
@@ -256,7 +258,11 @@
         if (mActivityContext.getDragLayer().isEventOverView(mBottomSheetHandleArea, ev)) {
             return true;
         }
-        AllAppsRecyclerView rv = getActiveRecyclerView();
+        if (isSearching()) {
+            return mAH.get(AdapterHolder.SEARCH).mRecyclerView
+                    .shouldContainerScroll(ev, mActivityContext.getDragLayer());
+        }
+        AllAppsRecyclerView rv = getActiveAppsRecyclerView();
         if (rv == null) {
             return true;
         }
@@ -270,7 +276,7 @@
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            AllAppsRecyclerView rv = getActiveRecyclerView();
+            AllAppsRecyclerView rv = getActiveAppsRecyclerView();
             if (rv != null && rv.getScrollbar().isHitInParent(ev.getX(), ev.getY(),
                     mFastScrollerOffset)) {
                 mTouchHandler = rv.getScrollbar();
@@ -287,7 +293,7 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            AllAppsRecyclerView rv = getActiveRecyclerView();
+            AllAppsRecyclerView rv = getActiveAppsRecyclerView();
             if (rv != null && rv.getScrollbar().isHitInParent(ev.getX(), ev.getY(),
                     mFastScrollerOffset)) {
                 mTouchHandler = rv.getScrollbar();
@@ -320,8 +326,8 @@
         return getContext().getString(R.string.all_apps_button_label);
     }
 
-    /** The current recycler view visible in the container. */
-    public AllAppsRecyclerView getActiveRecyclerView() {
+    /** The current apps recycler view in the container (may be hidden for search results). */
+    public AllAppsRecyclerView getActiveAppsRecyclerView() {
         if (!mUsingTabs || isPersonalTab()) {
             return mAH.get(AdapterHolder.MAIN).mRecyclerView;
         } else {
@@ -370,12 +376,15 @@
         // This is a focus listener that proxies focus from a view into the list view.  This is to
         // work around the search box from getting first focus and showing the cursor.
         setOnFocusChangeListener((v, hasFocus) -> {
-            if (hasFocus && getActiveRecyclerView() != null) {
-                getActiveRecyclerView().requestFocus();
+            if (hasFocus && getActiveAppsRecyclerView() != null) {
+                getActiveAppsRecyclerView().requestFocus();
             }
         });
 
         mHeader = findViewById(R.id.all_apps_header);
+        mSearchRecyclerView = findViewById(R.id.search_results_list_view);
+        mAH.get(AdapterHolder.SEARCH).setup(mSearchRecyclerView,
+                /* Filter out A-Z apps */ (itemInfo, componentName) -> false);
         rebindAdapters(true /* force */);
 
         mBottomSheetBackground = findViewById(R.id.bottom_sheet_background);
@@ -438,13 +447,19 @@
     }
 
     protected void rebindAdapters(boolean force) {
-        boolean showTabs = showTabs();
+        updateSearchResultsVisibility();
+
+        boolean showTabs = shouldShowTabs();
         if (showTabs == mUsingTabs && !force) {
             return;
         }
         mUsingTabs = showTabs;
-        replaceRVContainer(mUsingTabs);
 
+        if (isSearching()) {
+            return;
+        }
+
+        replaceAppsRVContainer(mUsingTabs);
         mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
         mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
 
@@ -479,6 +494,17 @@
         mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
     }
 
+    private void updateSearchResultsVisibility() {
+        if (isSearching()) {
+            getSearchRecyclerView().setVisibility(VISIBLE);
+            getAppsRecyclerViewContainer().setVisibility(GONE);
+        } else {
+            getSearchRecyclerView().setVisibility(GONE);
+            getAppsRecyclerViewContainer().setVisibility(VISIBLE);
+        }
+        mHeader.setActiveRV(getCurrentPage());
+    }
+
     private void setDeviceManagementResources() {
         if (mActivityContext.getStringCache() != null) {
             Button personalTab = findViewById(R.id.tab_personal);
@@ -489,18 +515,23 @@
         }
     }
 
-    protected boolean showTabs() {
+    protected boolean shouldShowTabs() {
         return mHasWorkApps;
     }
 
-    protected View replaceRVContainer(boolean showTabs) {
-        for (AdapterHolder adapterHolder : mAH) {
+    protected boolean isSearching() {
+        return false;
+    }
+
+    protected View replaceAppsRVContainer(boolean showTabs) {
+        for (int i = AdapterHolder.MAIN; i <= AdapterHolder.WORK; i++) {
+            AdapterHolder adapterHolder = mAH.get(i);
             if (adapterHolder.mRecyclerView != null) {
                 adapterHolder.mRecyclerView.setLayoutManager(null);
                 adapterHolder.mRecyclerView.setAdapter(null);
             }
         }
-        View oldView = getRecyclerViewContainer();
+        View oldView = getAppsRecyclerViewContainer();
         int index = indexOfChild(oldView);
         removeView(oldView);
         int layout = showTabs ? R.layout.all_apps_tabs : R.layout.all_apps_rv_layout;
@@ -521,13 +552,17 @@
         return newView;
     }
 
-    public View getRecyclerViewContainer() {
+    public View getAppsRecyclerViewContainer() {
         return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view);
     }
 
+    public SearchRecyclerView getSearchRecyclerView() {
+        return mSearchRecyclerView;
+    }
+
     @Override
     public void onActivePageChanged(int currentActivePage) {
-        mHeader.setMainActive(currentActivePage == AdapterHolder.MAIN);
+        mHeader.setActiveRV(currentActivePage);
         if (mAH.get(currentActivePage).mRecyclerView != null) {
             mAH.get(currentActivePage).mRecyclerView.bindFastScrollbar();
         }
@@ -556,8 +591,8 @@
         return isDescendantViewVisible(R.id.tab_work);
     }
 
-    public AlphabeticalAppsList<T> getApps() {
-        return mAH.get(AdapterHolder.MAIN).mAppsList;
+    public AlphabeticalAppsList<T> getSearchResultList() {
+        return mAH.get(AdapterHolder.SEARCH).mAppsList;
     }
 
     public FloatingHeaderView getFloatingHeaderView() {
@@ -566,17 +601,19 @@
 
     @VisibleForTesting
     public View getContentView() {
-        return mViewPager == null ? getActiveRecyclerView() : mViewPager;
+        return mViewPager == null ? getActiveAppsRecyclerView() : mViewPager;
     }
 
     /** The current page visible in all apps. */
     public int getCurrentPage() {
-        return mViewPager != null ? mViewPager.getCurrentPage() : AdapterHolder.MAIN;
+        return isSearching()
+                ? AdapterHolder.SEARCH
+                : mViewPager == null ? AdapterHolder.MAIN : mViewPager.getCurrentPage();
     }
 
     /** The scroll bar for the active recycler view. */
     public RecyclerViewFastScroller getScrollBar() {
-        AllAppsRecyclerView rv = getActiveRecyclerView();
+        AllAppsRecyclerView rv = getActiveAppsRecyclerView();
         return rv == null ? null : rv.getScrollbar();
     }
 
@@ -585,7 +622,9 @@
         mHeader.setup(
                 mAH.get(AdapterHolder.MAIN).mRecyclerView,
                 mAH.get(AdapterHolder.WORK).mRecyclerView,
-                mAH.get(AdapterHolder.WORK).mRecyclerView == null);
+                (SearchRecyclerView) mAH.get(AdapterHolder.SEARCH).mRecyclerView,
+                getCurrentPage(),
+                /* tabsHidden= */ mAH.get(AdapterHolder.WORK).mRecyclerView == null);
 
         int padding = mHeader.getMaxTranslation();
         for (int i = 0; i < mAH.size(); i++) {
@@ -696,39 +735,48 @@
         return ColorUtils.blendARGB(mScrimColor, mHeaderProtectionColor, blendRatio);
     }
 
-    protected abstract BaseAllAppsAdapter getAdapter(AlphabeticalAppsList<T> mAppsList,
+    protected abstract BaseAllAppsAdapter<T> createAdapter(AlphabeticalAppsList<T> mAppsList,
             BaseAdapterProvider[] adapterProviders);
 
     protected int getHeaderBottom() {
         return (int) getTranslationY();
     }
 
+    /**
+     * Returns a view that denotes the visible part of all apps container view.
+     */
+    public View getVisibleContainerView() {
+        return mActivityContext.getDeviceProfile().isTablet ? mBottomSheetBackground : this;
+    }
+
     /** Holds a {@link BaseAllAppsAdapter} and related fields. */
     public class AdapterHolder {
         public static final int MAIN = 0;
         public static final int WORK = 1;
+        public static final int SEARCH = 2;
 
-        private final boolean mIsWork;
-        public final BaseAllAppsAdapter<T> adapter;
+        private final int mType;
+        public final BaseAllAppsAdapter<T> mAdapter;
         final RecyclerView.LayoutManager mLayoutManager;
         final AlphabeticalAppsList<T> mAppsList;
         final Rect mPadding = new Rect();
         AllAppsRecyclerView mRecyclerView;
         boolean mVerticalFadingEdge;
 
-        AdapterHolder(boolean isWork) {
-            mIsWork = isWork;
-            mAppsList = new AlphabeticalAppsList<>(mActivityContext, mAllAppsStore,
-                    isWork ? mWorkManager.getAdapterProvider() : null);
+        AdapterHolder(int type) {
+            mType = type;
+            mAppsList = new AlphabeticalAppsList<>(mActivityContext,
+                    isSearch() ? null : mAllAppsStore,
+                    isWork() ? mWorkManager.getAdapterProvider() : null);
 
             BaseAdapterProvider[] adapterProviders =
-                    isWork ? new BaseAdapterProvider[]{mMainAdapterProvider,
+                    isWork() ? new BaseAdapterProvider[]{mMainAdapterProvider,
                             mWorkManager.getAdapterProvider()}
                             : new BaseAdapterProvider[]{mMainAdapterProvider};
 
-            adapter = getAdapter(mAppsList, adapterProviders);
-            mAppsList.setAdapter(adapter);
-            mLayoutManager = adapter.getLayoutManager();
+            mAdapter = createAdapter(mAppsList, adapterProviders);
+            mAppsList.setAdapter(mAdapter);
+            mLayoutManager = mAdapter.getLayoutManager();
         }
 
         void setup(@NonNull View rv, @Nullable ItemInfoMatcher matcher) {
@@ -737,14 +785,14 @@
             mRecyclerView.setEdgeEffectFactory(createEdgeEffectFactory());
             mRecyclerView.setApps(mAppsList);
             mRecyclerView.setLayoutManager(mLayoutManager);
-            mRecyclerView.setAdapter(adapter);
+            mRecyclerView.setAdapter(mAdapter);
             mRecyclerView.setHasFixedSize(true);
             // No animations will occur when changes occur to the items in this RecyclerView.
             mRecyclerView.setItemAnimator(null);
             mRecyclerView.addOnScrollListener(mScrollListener);
             FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mRecyclerView);
             mRecyclerView.addItemDecoration(focusedItemDecorator);
-            adapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
+            mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
             applyVerticalFadingEdgeEnabled(mVerticalFadingEdge);
             applyPadding();
         }
@@ -752,7 +800,7 @@
         void applyPadding() {
             if (mRecyclerView != null) {
                 int bottomOffset = 0;
-                if (mIsWork && mWorkManager.getWorkModeSwitch() != null) {
+                if (isWork() && mWorkManager.getWorkModeSwitch() != null) {
                     bottomOffset = mInsets.bottom + mWorkManager.getWorkModeSwitch().getHeight();
                 }
                 mRecyclerView.setPadding(mPadding.left, mPadding.top, mPadding.right,
@@ -762,15 +810,15 @@
 
         private void applyVerticalFadingEdgeEnabled(boolean enabled) {
             mVerticalFadingEdge = enabled;
-            mAH.get(AdapterHolder.MAIN).mRecyclerView.setVerticalFadingEdgeEnabled(!mUsingTabs
-                    && mVerticalFadingEdge);
+            mRecyclerView.setVerticalFadingEdgeEnabled(!mUsingTabs && mVerticalFadingEdge);
         }
-    }
 
-    /**
-     * Returns a view that denotes the visible part of all apps container view.
-     */
-    public View getVisibleContainerView() {
-        return mActivityContext.getDeviceProfile().isTablet ? mBottomSheetBackground : this;
+        private boolean isWork() {
+            return mType == WORK;
+        }
+
+        private boolean isSearch() {
+            return mType == SEARCH;
+        }
     }
 }
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 72f14a8..3e717bd 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
+import com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.views.ActivityContext;
@@ -90,8 +91,8 @@
     protected ViewGroup mTabLayout;
     private AllAppsRecyclerView mMainRV;
     private AllAppsRecyclerView mWorkRV;
+    private SearchRecyclerView mSearchRV;
     private AllAppsRecyclerView mCurrentRV;
-    private ViewGroup mParent;
     public boolean mHeaderCollapsed;
     protected int mSnappedScrolledY;
     private int mTranslationY;
@@ -100,7 +101,6 @@
 
     protected boolean mTabsHidden;
     protected int mMaxTranslation;
-    private boolean mMainRVActive = true;
 
     private boolean mCollapsed = false;
 
@@ -232,7 +232,8 @@
         return super.getFocusedChild();
     }
 
-    void setup(AllAppsRecyclerView mainRV, AllAppsRecyclerView workRV, boolean tabsHidden) {
+    void setup(AllAppsRecyclerView mainRV, AllAppsRecyclerView workRV, SearchRecyclerView searchRV,
+            int activeRV, boolean tabsHidden) {
         for (FloatingHeaderRow row : mAllRows) {
             row.setup(this, mAllRows, tabsHidden);
         }
@@ -242,8 +243,8 @@
         mTabLayout.setVisibility(tabsHidden ? View.GONE : View.VISIBLE);
         mMainRV = setupRV(mMainRV, mainRV);
         mWorkRV = setupRV(mWorkRV, workRV);
-        mParent = (ViewGroup) mMainRV.getParent();
-        setMainActive(mMainRVActive || mWorkRV == null);
+        mSearchRV = (SearchRecyclerView) setupRV(mSearchRV, searchRV);
+        setActiveRV(activeRV);
         reset(false);
     }
 
@@ -267,9 +268,10 @@
         }
     }
 
-    public void setMainActive(boolean active) {
-        mCurrentRV = active ? mMainRV : mWorkRV;
-        mMainRVActive = active;
+    public void setActiveRV(int rvType) {
+        mCurrentRV =
+                rvType == AdapterHolder.MAIN ? mMainRV
+                : rvType == AdapterHolder.WORK ? mWorkRV : mSearchRV;
     }
 
     public int getMaxTranslation() {
@@ -332,10 +334,15 @@
         mHeaderClip.top = clipTop;
         // clipping on a draw might cause additional redraw
         setClipBounds(mHeaderClip);
-        mMainRV.setClipBounds(mRVClip);
+        if (mMainRV != null) {
+            mMainRV.setClipBounds(mRVClip);
+        }
         if (mWorkRV != null) {
             mWorkRV.setClipBounds(mRVClip);
         }
+        if (mSearchRV != null) {
+            mSearchRV.setClipBounds(mRVClip);
+        }
     }
 
     /**
@@ -402,8 +409,8 @@
     }
 
     private void calcOffset(Point p) {
-        p.x = getLeft() - mCurrentRV.getLeft() - mParent.getLeft();
-        p.y = getTop() - mCurrentRV.getTop() - mParent.getTop();
+        p.x = getLeft() - mCurrentRV.getLeft() - ((ViewGroup) mCurrentRV.getParent()).getLeft();
+        p.y = getTop() - mCurrentRV.getTop() - ((ViewGroup) mCurrentRV.getParent()).getTop();
     }
 
     public boolean hasVisibleContent() {
diff --git a/src/com/android/launcher3/allapps/SearchRecyclerView.java b/src/com/android/launcher3/allapps/SearchRecyclerView.java
new file mode 100644
index 0000000..435668a
--- /dev/null
+++ b/src/com/android/launcher3/allapps/SearchRecyclerView.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 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.allapps;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/** A RecyclerView for AllApps Search results. */
+public class SearchRecyclerView extends AllAppsRecyclerView {
+    private static final String TAG = "SearchRecyclerView";
+
+    public SearchRecyclerView(Context context) {
+        this(context, null);
+    }
+
+    public SearchRecyclerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SearchRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public SearchRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void updatePoolSize() {
+        RecycledViewPool pool = getRecycledViewPool();
+        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, mNumAppsPerRow);
+        // TODO(b/206905515): Add maxes for other View types.
+    }
+
+    @Override
+    public boolean supportsFastScrolling() {
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/allapps/WorkEduCard.java b/src/com/android/launcher3/allapps/WorkEduCard.java
index c336496..fa9a8f6 100644
--- a/src/com/android/launcher3/allapps/WorkEduCard.java
+++ b/src/com/android/launcher3/allapps/WorkEduCard.java
@@ -71,7 +71,7 @@
         super.onFinishInflate();
         findViewById(R.id.action_btn).setOnClickListener(this);
         MarginLayoutParams lp = ((MarginLayoutParams) findViewById(R.id.wrapper).getLayoutParams());
-        lp.width = mActivityContext.getAppsView().getActiveRecyclerView().getTabWidth();
+        lp.width = mActivityContext.getAppsView().getActiveAppsRecyclerView().getTabWidth();
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 893e547..4bd106f 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -57,7 +57,7 @@
     private final AllAppsSearchBarController mSearchBarController;
     private final SpannableStringBuilder mSearchQueryBuilder;
 
-    private AlphabeticalAppsList<?> mApps;
+    private AlphabeticalAppsList<?> mSearchResultsList;
     private ActivityAllAppsContainerView<?> mAppsView;
 
     // The amount of pixels to shift down and overlap with the rest of the content.
@@ -102,8 +102,8 @@
         // Update the width to match the grid padding
         DeviceProfile dp = mLauncher.getDeviceProfile();
         int myRequestedWidth = getSize(widthMeasureSpec);
-        int rowWidth = myRequestedWidth - mAppsView.getActiveRecyclerView().getPaddingLeft()
-                - mAppsView.getActiveRecyclerView().getPaddingRight();
+        int rowWidth = myRequestedWidth - mAppsView.getActiveAppsRecyclerView().getPaddingLeft()
+                - mAppsView.getActiveAppsRecyclerView().getPaddingRight();
 
         int cellWidth = DeviceProfile.calculateCellWidth(rowWidth,
                 dp.cellLayoutBorderSpacePx.x, dp.numShownHotseatIcons);
@@ -131,7 +131,7 @@
 
     @Override
     public void initializeSearch(ActivityAllAppsContainerView<?> appsView) {
-        mApps = appsView.getApps();
+        mSearchResultsList = appsView.getSearchResultList();
         mAppsView = appsView;
         mSearchBarController.initialize(
                 new DefaultAppSearchAlgorithm(getContext()),
@@ -170,7 +170,7 @@
     @Override
     public void onSearchResult(String query, ArrayList<AdapterItem> items) {
         if (items != null) {
-            mApps.setSearchResults(items);
+            mSearchResultsList.setSearchResults(items);
             notifyResultChanged();
             mAppsView.setLastSearchQuery(query);
         }
@@ -179,14 +179,14 @@
     @Override
     public void onAppendSearchResult(String query, ArrayList<AdapterItem> items) {
         if (items != null) {
-            mApps.appendSearchResults(items);
+            mSearchResultsList.appendSearchResults(items);
             notifyResultChanged();
         }
     }
 
     @Override
     public void clearSearchResult() {
-        if (mApps.setSearchResults(null)) {
+        if (mSearchResultsList.setSearchResults(null)) {
             notifyResultChanged();
         }
 
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index a6b481a..ee8f85d 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -108,7 +108,7 @@
 
             case TestProtocol.REQUEST_APPS_LIST_SCROLL_Y: {
                 return getLauncherUIProperty(Bundle::putInt,
-                        l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
+                        l -> l.getAppsView().getActiveAppsRecyclerView().getCurrentScrollY());
             }
 
             case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index a982786..5f6e453 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -43,7 +43,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.FastScrollRecyclerView;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.FastScrollThumbDrawable;
@@ -127,7 +127,7 @@
     private String mPopupSectionName;
     private Insets mSystemGestureInsets;
 
-    protected BaseRecyclerView mRv;
+    protected FastScrollRecyclerView mRv;
     private RecyclerView.OnScrollListener mOnScrollListener;
 
     private int mDownX;
@@ -172,7 +172,7 @@
         ta.recycle();
     }
 
-    public void setRecyclerView(BaseRecyclerView rv, TextView popupView) {
+    public void setRecyclerView(FastScrollRecyclerView rv, TextView popupView) {
         if (mRv != null && mOnScrollListener != null) {
             mRv.removeOnScrollListener(mOnScrollListener);
         }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index f780f03..ea62c17 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -27,8 +27,8 @@
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
 
-import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.FastScrollRecyclerView;
 import com.android.launcher3.R;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.widget.model.WidgetListSpaceEntry;
@@ -41,7 +41,7 @@
 /**
  * The widgets recycler view.
  */
-public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouchListener {
+public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnItemTouchListener {
 
     private WidgetsListAdapter mAdapter;