Fix scrolling bugs in full widgets sheet

Bugs:
1. Layout margins were not taken into account
2. Fast scroller offset has a different scale to the recycler view
3. Update padding & margin in onMeasure rather than in onLayout

TODO: fast scroller measurement doesn't take into account of
      expanded item. It naively use the same height for all items
      from the recycler view adapter.

Test: Open full widgets sheet for both work profile and non-work
      profile setup. Scrollbar initial position is correct. Scroll
      up and down to confirm views vertical translation is correct.

Video: https://drive.google.com/file/d/1kp3iSm23RVk_otBNeYalEPPkWjjWMOY0/view?usp=sharing

Bug: 181629430
Change-Id: I398a94510751782e78aa8f426f37b03ecca8ec99
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
index a5ed20a..95fa05f 100644
--- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
@@ -16,10 +16,10 @@
 package com.android.launcher3.widget.picker;
 
 import android.view.View;
+import android.view.ViewGroup.MarginLayoutParams;
 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;
@@ -33,20 +33,21 @@
         RecyclerViewFastScroller.OnFastScrollChangeListener {
     private final boolean mHasWorkProfile;
     private final SearchAndRecommendationViewHolder mViewHolder;
-    private final RecyclerView mPrimaryRecyclerView;
+    private final WidgetsRecyclerView mPrimaryRecyclerView;
 
     // The following are only non null if mHasWorkProfile is true.
-    @Nullable private final RecyclerView mWorkRecyclerView;
+    @Nullable private final WidgetsRecyclerView mWorkRecyclerView;
     @Nullable private final View mPrimaryWorkTabsView;
     @Nullable private final PersonalWorkPagedView mPrimaryWorkViewPager;
 
+    private WidgetsRecyclerView mCurrentRecyclerView;
     private int mMaxCollapsibleHeight = 0;
 
     SearchAndRecommendationsScrollController(
             boolean hasWorkProfile,
             SearchAndRecommendationViewHolder viewHolder,
-            RecyclerView primaryRecyclerView,
-            @Nullable RecyclerView workRecyclerView,
+            WidgetsRecyclerView primaryRecyclerView,
+            @Nullable WidgetsRecyclerView workRecyclerView,
             @Nullable View personalWorkTabsView,
             @Nullable PersonalWorkPagedView primaryWorkViewPager) {
         mHasWorkProfile = hasWorkProfile;
@@ -55,6 +56,12 @@
         mWorkRecyclerView = workRecyclerView;
         mPrimaryWorkTabsView = personalWorkTabsView;
         mPrimaryWorkViewPager = primaryWorkViewPager;
+        mCurrentRecyclerView = mPrimaryRecyclerView;
+    }
+
+    /** Sets the current active {@link WidgetsRecyclerView}. */
+    public void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView) {
+        mCurrentRecyclerView = currentRecyclerView;
     }
 
     /**
@@ -64,10 +71,10 @@
         // 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();
+                + measureHeightWithVerticalMargins(mViewHolder.mCollapseHandle)
+                + measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle);
 
-        int topContainerHeight = mViewHolder.mContainer.getMeasuredHeight();
+        int topContainerHeight = measureHeightWithVerticalMargins(mViewHolder.mContainer);
         if (mHasWorkProfile) {
             // In a work profile setup, the full widget sheet contains the following views:
             //           -------               -|
@@ -114,8 +121,8 @@
             //
             // 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 tabsViewActualHeight = measureHeightWithVerticalMargins(mPrimaryWorkTabsView)
+                    - mPrimaryWorkTabsView.getPaddingTop();
             int topOffsetAfterAllViewsCollapsed =
                     topContainerHeight + tabsViewActualHeight - mMaxCollapsibleHeight;
 
@@ -149,9 +156,12 @@
      * views (e.g. recycler views, tabs) upon scrolling.
      */
     @Override
-    public void onThumbOffsetYChanged(int y) {
+    public void onThumbOffsetYChanged(int unused) {
+        // Always use the recycler view offset because fast scroller offset has a different scale.
+        int recyclerViewYOffset = mCurrentRecyclerView.getCurrentScrollY();
+        if (recyclerViewYOffset < 0) return;
         if (mMaxCollapsibleHeight > 0) {
-            int yDisplacement = Math.max(-y, -mMaxCollapsibleHeight);
+            int yDisplacement = Math.max(-recyclerViewYOffset, -mMaxCollapsibleHeight);
             mViewHolder.mHeaderTitle.setTranslationY(yDisplacement);
             mViewHolder.mSearchBar.setTranslationY(yDisplacement);
             if (mHasWorkProfile) {
@@ -159,4 +169,20 @@
             }
         }
     }
+
+    /** Resets any previous view translation. */
+    public void reset() {
+        mViewHolder.mHeaderTitle.setTranslationY(0);
+        mViewHolder.mSearchBar.setTranslationY(0);
+        if (mHasWorkProfile) {
+            mPrimaryWorkTabsView.setTranslationY(0);
+        }
+    }
+
+    /** private the height, in pixel, + the vertical margins of a given view. */
+    private static int measureHeightWithVerticalMargins(View view) {
+        MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
+        return view.getMeasuredHeight() + marginLayoutParams.bottomMargin
+                + marginLayoutParams.topMargin;
+    }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 91b79f9..52a2fc5 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -149,7 +149,10 @@
 
     @Override
     public void onActivePageChanged(int currentActivePage) {
-        mAdapters.get(currentActivePage).mWidgetsRecyclerView.bindFastScrollbar();
+        WidgetsRecyclerView currentRecyclerView =
+                mAdapters.get(currentActivePage).mWidgetsRecyclerView;
+        currentRecyclerView.bindFastScrollbar();
+        mSearchAndRecommendationsScrollController.setCurrentRecyclerView(currentRecyclerView);
 
         reset();
     }
@@ -159,6 +162,7 @@
         if (mHasWorkProfile) {
             mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop();
         }
+        mSearchAndRecommendationsScrollController.reset();
     }
 
     @VisibleForTesting
@@ -241,6 +245,12 @@
             mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
                     maxSpansPerRow);
         }
+
+        if (mInitialTabsHeight == 0 && mTabsView != null) {
+            mInitialTabsHeight = measureHeightWithVerticalMargins(mTabsView);
+        }
+
+        mSearchAndRecommendationsScrollController.updateMarginAndPadding();
     }
 
     @Override
@@ -255,12 +265,6 @@
                 contentLeft + contentWidth, height);
 
         setTranslationShift(mTranslationShift);
-
-        if (mInitialTabsHeight == 0 && mTabsView != null) {
-            mInitialTabsHeight = mTabsView.getMeasuredHeight();
-        }
-
-        mSearchAndRecommendationsScrollController.updateMarginAndPadding();
     }
 
     @Override
@@ -371,7 +375,14 @@
         // No need to check work profile here because mInitialTabHeight is always 0 if there is no
         // work profile.
         return mInitialTabsHeight
-                + mSearchAndRecommendationViewHolder.mContainer.getMeasuredHeight();
+                + measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mContainer);
+    }
+
+    /** private the height, in pixel, + the vertical margins of a given view. */
+    private static int measureHeightWithVerticalMargins(View view) {
+        MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
+        return view.getMeasuredHeight() + marginLayoutParams.bottomMargin
+                + marginLayoutParams.topMargin;
     }
 
     /** A holder class for holding adapters & their corresponding recycler view. */