Merge "Add a header container view to the widgets full sheet" into sc-dev
diff --git a/res/drawable/bg_widgets_searchbox.xml b/res/drawable/bg_widgets_searchbox.xml
new file mode 100644
index 0000000..81dd2aa
--- /dev/null
+++ b/res/drawable/bg_widgets_searchbox.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="#FFFFF7" />
+    <corners android:radius="24dp" />
+</shape>
\ No newline at end of file
diff --git a/res/layout/personal_work_tabs.xml b/res/layout/personal_work_tabs.xml
index 8f29997..5fb5bcb 100644
--- a/res/layout/personal_work_tabs.xml
+++ b/res/layout/personal_work_tabs.xml
@@ -23,6 +23,7 @@
     android:layout_marginLeft="@dimen/all_apps_tabs_side_padding"
     android:layout_marginRight="@dimen/all_apps_tabs_side_padding"
     android:orientation="horizontal"
+    android:elevation="2dp"
     style="@style/TextHeadline">
 
     <Button
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index cfbb6dd..8125db8 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -16,13 +16,15 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto">
 
-    <include layout="@layout/personal_work_tabs" />
+    <include layout="@layout/personal_work_tabs"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="16dp" />
 
     <com.android.launcher3.workprofile.PersonalWorkPagedView
         android:id="@+id/widgets_view_pager"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_below="@+id/tabs"
         android:clipToPadding="false"
         android:descendantFocusability="afterDescendants"
         launcher:pageIndicator="@+id/tabs">
diff --git a/res/layout/widgets_full_sheet_search_and_recommendations.xml b/res/layout/widgets_full_sheet_search_and_recommendations.xml
new file mode 100644
index 0000000..9a6f922
--- /dev/null
+++ b/res/layout/widgets_full_sheet_search_and_recommendations.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/search_and_recommendations_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="16dp"
+    android:orientation="vertical">
+    <View
+        android:id="@+id/collapse_handle"
+        android:layout_width="48dp"
+        android:layout_height="2dp"
+        android:layout_gravity="center_horizontal"
+        android:background="@color/popup_color_primary_dark"/>
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:textSize="24sp"
+        android:layout_marginTop="16dp"
+        android:text="@string/widget_button_text"/>
+    <!-- Disable the search bar because it has not been implemented. -->
+    <EditText
+        android:id="@+id/widgets_search_bar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:layout_marginTop="16dp"
+        android:background="@drawable/bg_widgets_searchbox"
+        android:drawablePadding="8dp"
+        android:drawableStart="@drawable/ic_allapps_search"
+        android:hint="@string/widgets_full_sheet_search_bar_hint"
+        android:padding="12dp" />
+</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5a9def7..73f9e53 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -57,6 +57,12 @@
         <item quantity="one"><xliff:g id="widget_count" example="1">%1$d</xliff:g> widget</item>
         <item quantity="other"><xliff:g id="widget_count" example="2">%1$d</xliff:g> widgets</item>
     </plurals>
+    <!-- Text for both the tile of a popup view, which shows all available widgets installed on
+         the device, and the text of a button, which opens this popup view. [CHAR LIMIT=30]-->
+    <string name="widget_button_text">Widgets</string>
+    <!-- Search bar text shown in the popup view showing all available widgets installed on the
+         device. [CHAR_LIMIT=50] -->
+    <string name="widgets_full_sheet_search_bar_hint">Search</string>
 
     <!-- All Apps -->
     <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
@@ -185,8 +191,6 @@
     <string name="folder_name_format_overflow">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g>, <xliff:g id="size" example="2">%2$d</xliff:g> or more items</string>
 
     <!-- Strings for the customization mode -->
-    <!-- Text for widget add button [CHAR LIMIT=30]-->
-    <string name="widget_button_text">Widgets</string>
     <!-- Text for wallpaper change button [CHAR LIMIT=30]-->
     <string name="wallpaper_button_text">Wallpapers</string>
     <!-- Text for wallpaper change button [CHAR LIMIT=30]-->
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index c55b46b..9369bdc 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -23,6 +23,7 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -188,4 +189,21 @@
         super.onInitializeAccessibilityNodeInfo(info);
         if (isLayoutSuppressed()) info.setScrollable(false);
     }
+
+    /**
+     * Scrolls this recycler view to the top.
+     */
+    public void scrollToTop() {
+        if (mScrollbar != null) {
+            mScrollbar.reattachThumbToScroll();
+        }
+        if (getLayoutManager() instanceof LinearLayoutManager) {
+            LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
+            if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
+                // We are at the top, so don't scrollToPosition (would cause unnecessary relayout).
+                return;
+            }
+        }
+        scrollToPosition(0);
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index e61b95d..ace9938 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -36,7 +36,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 
 import java.util.ArrayList;
@@ -109,23 +108,6 @@
         mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, grid.allAppsCellHeightPx);
     }
 
-    /**
-     * Scrolls this recycler view to the top.
-     */
-    public void scrollToTop() {
-        // Ensure we reattach the scrollbar if it was previously detached while fast-scrolling
-        if (mScrollbar != null) {
-            mScrollbar.reattachThumbToScroll();
-        }
-        if (getLayoutManager() instanceof AppsGridLayoutManager) {
-            AppsGridLayoutManager layoutManager = (AppsGridLayoutManager) getLayoutManager();
-            if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
-                // We are at the top, so don't scrollToPosition (would cause unnecessary relayout).
-                return;
-            }
-        }
-        scrollToPosition(0);
-    }
 
     @Override
     public void onDraw(Canvas c) {
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 804fb3e..ae34257 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -34,6 +34,7 @@
 import android.view.ViewConfiguration;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.BaseRecyclerView;
@@ -99,6 +100,7 @@
     private boolean mIsThumbDetached;
     private final boolean mCanThumbDetach;
     private boolean mIgnoreDragGesture;
+    private boolean mIsRecyclerViewFirstChildInParent = true;
 
     // 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.
@@ -112,6 +114,7 @@
 
     protected BaseRecyclerView mRv;
     private RecyclerView.OnScrollListener mOnScrollListener;
+    @Nullable private OnFastScrollChangeListener mOnFastScrollChangeListener;
 
     private int mDownX;
     private int mDownY;
@@ -188,6 +191,9 @@
         updatePopupY(y);
         mThumbOffsetY = y;
         invalidate();
+        if (mOnFastScrollChangeListener != null) {
+            mOnFastScrollChangeListener.onThumbOffsetYChanged(mThumbOffsetY);
+        }
     }
 
     public int getThumbOffsetY() {
@@ -391,7 +397,9 @@
             return false;
         }
         getHitRect(sTempRect);
-        sTempRect.top += mRv.getScrollBarTop();
+        if (mIsRecyclerViewFirstChildInParent) {
+            sTempRect.top += mRv.getScrollBarTop();
+        }
         if (outOffset != null) {
             outOffset.set(sTempRect.left, sTempRect.top);
         }
@@ -404,4 +412,23 @@
         // alpha is so low, it does not matter.
         return false;
     }
+
+    public void setIsRecyclerViewFirstChildInParent(boolean isRecyclerViewFirstChildInParent) {
+        mIsRecyclerViewFirstChildInParent = isRecyclerViewFirstChildInParent;
+    }
+
+    public void setOnFastScrollChangeListener(
+            @Nullable OnFastScrollChangeListener onFastScrollChangeListener) {
+        mOnFastScrollChangeListener = onFastScrollChangeListener;
+    }
+
+    /**
+     * A callback that is invoked when there is a scroll change in {@link RecyclerViewFastScroller}.
+     */
+    public interface OnFastScrollChangeListener {
+        /**
+         * Called when the thumb offset vertical position, in pixels, has changed to {@code y}.
+         */
+        void onThumbOffsetYChanged(int y);
+    }
 }
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
new file mode 100644
index 0000000..a5ed20a
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 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.widget.picker;
+
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.views.RecyclerViewFastScroller;
+import com.android.launcher3.widget.picker.WidgetsFullSheet.SearchAndRecommendationViewHolder;
+import com.android.launcher3.workprofile.PersonalWorkPagedView;
+
+/**
+ * A controller which measures & updates {@link WidgetsFullSheet}'s views padding, margin and
+ * vertical displacement upon scrolling.
+ */
+final class SearchAndRecommendationsScrollController implements
+        RecyclerViewFastScroller.OnFastScrollChangeListener {
+    private final boolean mHasWorkProfile;
+    private final SearchAndRecommendationViewHolder mViewHolder;
+    private final RecyclerView mPrimaryRecyclerView;
+
+    // The following are only non null if mHasWorkProfile is true.
+    @Nullable private final RecyclerView mWorkRecyclerView;
+    @Nullable private final View mPrimaryWorkTabsView;
+    @Nullable private final PersonalWorkPagedView mPrimaryWorkViewPager;
+
+    private int mMaxCollapsibleHeight = 0;
+
+    SearchAndRecommendationsScrollController(
+            boolean hasWorkProfile,
+            SearchAndRecommendationViewHolder viewHolder,
+            RecyclerView primaryRecyclerView,
+            @Nullable RecyclerView workRecyclerView,
+            @Nullable View personalWorkTabsView,
+            @Nullable PersonalWorkPagedView primaryWorkViewPager) {
+        mHasWorkProfile = hasWorkProfile;
+        mViewHolder = viewHolder;
+        mPrimaryRecyclerView = primaryRecyclerView;
+        mWorkRecyclerView = workRecyclerView;
+        mPrimaryWorkTabsView = personalWorkTabsView;
+        mPrimaryWorkViewPager = primaryWorkViewPager;
+    }
+
+    /**
+     * Updates the margin and padding of {@link WidgetsFullSheet} to accumulate collapsible views.
+     */
+    public void updateMarginAndPadding() {
+        // The maximum vertical distance, in pixels, until the last collapsible element is not
+        // visible from the screen when the user scrolls down the recycler view.
+        mMaxCollapsibleHeight = mViewHolder.mContainer.getPaddingTop()
+                + mViewHolder.mCollapseHandle.getMeasuredHeight()
+                + mViewHolder.mHeaderTitle.getMeasuredHeight();
+
+        int topContainerHeight = mViewHolder.mContainer.getMeasuredHeight();
+        if (mHasWorkProfile) {
+            // In a work profile setup, the full widget sheet contains the following views:
+            //           -------               -|
+            //           Widgets               -|---> LinearLayout for search & recommendations
+            //          Search bar             -|
+            //      Personal | Work
+            //           View Pager
+            //
+            // Views after the search & recommendations are not bound by RelativelyLayout param.
+            // To position them on the expected location, padding & margin are added to these views
+
+            // Tabs should have a padding of the height of the search & recommendations container.
+            mPrimaryWorkTabsView.setPadding(
+                    mPrimaryWorkTabsView.getPaddingLeft(),
+                    topContainerHeight,
+                    mPrimaryWorkTabsView.getPaddingRight(),
+                    mPrimaryWorkTabsView.getPaddingBottom());
+
+            // Instead of setting the top offset directly, we split the top offset into two values:
+            // 1. topOffsetAfterAllViewsCollapsed: this is the top offset after all collapsible
+            //    views are no longer visible on the screen.
+            //    This value is set as the margin for the view pager.
+            // 2. mMaxCollapsibleDistance
+            //    This value is set as the padding for the recycler views in order to work with
+            //    clipToPadding="false", which is an attribute for not showing top / bottom padding
+            //    when a recycler view has not reached the top or bottom of the list.
+            //    e.g. a list of 10 entries, only 3 entries are visible at a time.
+            //         case 1: recycler view is scrolled to the top. Top padding is visible/
+            //         (top padding)
+            //         item 1
+            //         item 2
+            //         item 3
+            //
+            //         case 2: recycler view is scrolled to the middle. No padding is visible.
+            //         item 4
+            //         item 5
+            //         item 6
+            //
+            //         case 3: recycler view is scrolled to the end. bottom padding is visible.
+            //         item 8
+            //         item 9
+            //         item 10
+            //         (bottom padding): not set in this case.
+            //
+            // When the views are first inflated, the sum of topOffsetAfterAllViewsCollapsed and
+            // mMaxCollapsibleDistance should equal to the top container height.
+            int tabsViewActualHeight =
+                    mPrimaryWorkTabsView.getMeasuredHeight() - mPrimaryWorkTabsView.getPaddingTop();
+            int topOffsetAfterAllViewsCollapsed =
+                    topContainerHeight + tabsViewActualHeight - mMaxCollapsibleHeight;
+
+            RelativeLayout.LayoutParams layoutParams =
+                    (RelativeLayout.LayoutParams) mPrimaryWorkViewPager.getLayoutParams();
+            layoutParams.setMargins(0, topOffsetAfterAllViewsCollapsed, 0, 0);
+            mPrimaryWorkViewPager.setLayoutParams(layoutParams);
+            mPrimaryWorkViewPager.requestLayout();
+
+            mPrimaryRecyclerView.setPadding(
+                    mPrimaryRecyclerView.getPaddingLeft(),
+                    mMaxCollapsibleHeight,
+                    mPrimaryRecyclerView.getPaddingRight(),
+                    mPrimaryRecyclerView.getPaddingBottom());
+            mWorkRecyclerView.setPadding(
+                    mWorkRecyclerView.getPaddingLeft(),
+                    mMaxCollapsibleHeight,
+                    mWorkRecyclerView.getPaddingRight(),
+                    mWorkRecyclerView.getPaddingBottom());
+        } else {
+            mPrimaryRecyclerView.setPadding(
+                    mPrimaryRecyclerView.getPaddingLeft(),
+                    topContainerHeight,
+                    mPrimaryRecyclerView.getPaddingRight(),
+                    mPrimaryRecyclerView.getPaddingBottom());
+        }
+    }
+
+    /**
+     * Changes the displacement of collapsible views (e.g. title & widget recommendations) and fixed
+     * views (e.g. recycler views, tabs) upon scrolling.
+     */
+    @Override
+    public void onThumbOffsetYChanged(int y) {
+        if (mMaxCollapsibleHeight > 0) {
+            int yDisplacement = Math.max(-y, -mMaxCollapsibleHeight);
+            mViewHolder.mHeaderTitle.setTranslationY(yDisplacement);
+            mViewHolder.mSearchBar.setTranslationY(yDisplacement);
+            if (mHasWorkProfile) {
+                mPrimaryWorkTabsView.setTranslationY(yDisplacement);
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 39953b1..5a5c2ef 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -34,6 +34,8 @@
 import android.view.View;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.widget.EditText;
+import android.widget.TextView;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -61,7 +63,8 @@
  * Popup for showing the full list of available widgets
  */
 public class WidgetsFullSheet extends BaseWidgetSheet
-        implements Insettable, ProviderChangedListener, OnActivePageChangedListener {
+        implements Insettable, ProviderChangedListener, OnActivePageChangedListener,
+        WidgetsRecyclerView.HeaderViewDimensionsProvider {
 
     private static final long DEFAULT_OPEN_DURATION = 267;
     private static final long FADE_IN_DURATION = 150;
@@ -77,6 +80,10 @@
             mPrimaryWidgetsFilter.negate();
 
     @Nullable private PersonalWorkPagedView mViewPager;
+    private int mInitialTabsHeight = 0;
+    private View mTabsView;
+    private SearchAndRecommendationViewHolder mSearchAndRecommendationViewHolder;
+    private SearchAndRecommendationsScrollController mSearchAndRecommendationsScrollController;
 
     public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
@@ -98,8 +105,9 @@
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
         int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view
                 : R.layout.widgets_full_sheet_recyclerview;
-        layoutInflater.inflate(contentLayoutRes,  springLayout, true);
+        layoutInflater.inflate(contentLayoutRes, springLayout, true);
 
+        RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller);
         if (mHasWorkProfile) {
             mViewPager = findViewById(R.id.widgets_view_pager);
             // Temporarily disable swipe gesture until widgets list horizontal scrollviews per
@@ -108,10 +116,12 @@
             mViewPager.initParentViews(this);
             mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
             mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.PRIMARY);
+            mTabsView = findViewById(R.id.tabs);
             findViewById(R.id.tab_personal)
                     .setOnClickListener((View view) -> mViewPager.snapToPage(0));
             findViewById(R.id.tab_work)
                     .setOnClickListener((View view) -> mViewPager.snapToPage(1));
+            fastScroller.setIsRecyclerViewFirstChildInParent(false);
             springLayout.addSpringView(R.id.primary_widgets_list_view);
             springLayout.addSpringView(R.id.work_widgets_list_view);
         } else {
@@ -119,12 +129,36 @@
             springLayout.addSpringView(R.id.primary_widgets_list_view);
         }
 
+        layoutInflater.inflate(R.layout.widgets_full_sheet_search_and_recommendations, springLayout,
+                true);
+        springLayout.addSpringView(R.id.search_and_recommendations_container);
+
+        mSearchAndRecommendationViewHolder = new SearchAndRecommendationViewHolder(
+                findViewById(R.id.search_and_recommendations_container));
+        mSearchAndRecommendationsScrollController = new SearchAndRecommendationsScrollController(
+                mHasWorkProfile,
+                mSearchAndRecommendationViewHolder,
+                findViewById(R.id.primary_widgets_list_view),
+                mHasWorkProfile ? findViewById(R.id.work_widgets_list_view) : null,
+                mTabsView,
+                mViewPager);
+        fastScroller.setOnFastScrollChangeListener(mSearchAndRecommendationsScrollController);
+
         onWidgetsBound();
     }
 
     @Override
     public void onActivePageChanged(int currentActivePage) {
         mAdapters.get(currentActivePage).mWidgetsRecyclerView.bindFastScrollbar();
+
+        reset();
+    }
+
+    private void reset() {
+        mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.scrollToTop();
+        if (mHasWorkProfile) {
+            mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop();
+        }
     }
 
     @VisibleForTesting
@@ -220,6 +254,12 @@
                 contentLeft + contentWidth, height);
 
         setTranslationShift(mTranslationShift);
+
+        if (mInitialTabsHeight == 0 && mTabsView != null) {
+            mInitialTabsHeight = mTabsView.getMeasuredHeight();
+        }
+
+        mSearchAndRecommendationsScrollController.updateMarginAndPadding();
     }
 
     @Override
@@ -325,6 +365,14 @@
         AccessibilityManagerCompat.sendStateEventToTest(getContext(), NORMAL_STATE_ORDINAL);
     }
 
+    @Override
+    public int getHeaderViewHeight() {
+        // No need to check work profile here because mInitialTabHeight is always 0 if there is no
+        // work profile.
+        return mInitialTabsHeight
+                + mSearchAndRecommendationViewHolder.mContainer.getMeasuredHeight();
+    }
+
     /** A holder class for holding adapters & their corresponding recycler view. */
     private final class AdapterHolder {
         static final int PRIMARY = 0;
@@ -354,9 +402,24 @@
         void setup(WidgetsRecyclerView recyclerView) {
             mWidgetsRecyclerView = recyclerView;
             mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
+            mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
             mWidgetsRecyclerView.setEdgeEffectFactory(
                     ((TopRoundedCornerView) mContent).createEdgeEffectFactory());
             mWidgetsListAdapter.setApplyBitmapDeferred(false, mWidgetsRecyclerView);
         }
     }
+
+    final class SearchAndRecommendationViewHolder {
+        final View mContainer;
+        final View mCollapseHandle;
+        final EditText mSearchBar;
+        final TextView mHeaderTitle;
+
+        SearchAndRecommendationViewHolder(View searchAndRecommendationContainer) {
+            mContainer = searchAndRecommendationContainer;
+            mCollapseHandle = mContainer.findViewById(R.id.collapse_handle);
+            mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
+            mHeaderTitle = mContainer.findViewById(R.id.title);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 52e9496..d65a809 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -40,6 +40,7 @@
 
     private final Point mFastScrollerOffset = new Point();
     private boolean mTouchDownOnScroller;
+    private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
 
     public WidgetsRecyclerView(Context context) {
         this(context, null);
@@ -135,8 +136,8 @@
     @Override
     protected int getAvailableScrollHeight() {
         View child = getChildAt(0);
-        return child.getMeasuredHeight() * mAdapter.getItemCount() - getScrollbarTrackHeight()
-                - mScrollbarTop;
+        return child.getMeasuredHeight() * mAdapter.getItemCount() + getScrollBarTop()
+                + getPaddingBottom() - mScrollbar.getHeight();
     }
 
     private boolean isModelNotReady() {
@@ -145,7 +146,9 @@
 
     @Override
     public int getScrollBarTop() {
-        return mScrollbarTop;
+        return mHeaderViewDimensionsProvider == null
+                ? mScrollbarTop
+                : mHeaderViewDimensionsProvider.getHeaderViewHeight() + mScrollbarTop;
     }
 
     @Override
@@ -171,4 +174,21 @@
     @Override
     public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
     }
+
+    public void setHeaderViewDimensionsProvider(
+            HeaderViewDimensionsProvider headerViewDimensionsProvider) {
+        mHeaderViewDimensionsProvider = headerViewDimensionsProvider;
+    }
+
+    /**
+     * Provides dimensions of the header view that is shown at the top of a
+     * {@link WidgetsRecyclerView}.
+     */
+    public interface HeaderViewDimensionsProvider {
+        /**
+         * Returns the height, in pixels, of the header view that is shown at the top of a
+         * {@link WidgetsRecyclerView}.
+         */
+        int getHeaderViewHeight();
+    }
 }