Merge "Suppress home gesture when bubbles are expanded" into tm-qpr-dev
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index a24a588..7d7054f 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -57,6 +57,7 @@
             android:enabled="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
+                <action android:name="android.intent.action.SHOW_WORK_APPS" />
                 <category android:name="android.intent.category.HOME" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.MONKEY"/>
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index db19c45..269b3c2 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -54,6 +54,7 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.Executors;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.TouchInteractionService.TISBinder;
@@ -136,6 +137,10 @@
         startBackgroundAnimation();
     }
 
+    private void runOnUiHelperThread(Runnable runnable) {
+        Executors.UI_HELPER_EXECUTOR.execute(runnable);
+    }
+
     private void startBackgroundAnimation() {
         if (Utilities.ATLEAST_S && mVibrator != null && mVibrator.areAllPrimitivesSupported(
                 VibrationEffect.Composition.PRIMITIVE_THUD)) {
@@ -144,22 +149,22 @@
                         new Animator.AnimatorListener() {
                             @Override
                             public void onAnimationStart(Animator animation) {
-                                mVibrator.vibrate(getVibrationEffect());
+                                runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect()));
                             }
 
                             @Override
                             public void onAnimationRepeat(Animator animation) {
-                                mVibrator.vibrate(getVibrationEffect());
+                                runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect()));
                             }
 
                             @Override
                             public void onAnimationEnd(Animator animation) {
-                                mVibrator.cancel();
+                                runOnUiHelperThread(mVibrator::cancel);
                             }
 
                             @Override
                             public void onAnimationCancel(Animator animation) {
-                                mVibrator.cancel();
+                                runOnUiHelperThread(mVibrator::cancel);
                             }
                         };
             }
diff --git a/res/values-sw600dp-land/dimens.xml b/res/values-sw600dp-land/dimens.xml
index dce09e3..63970cd 100644
--- a/res/values-sw600dp-land/dimens.xml
+++ b/res/values-sw600dp-land/dimens.xml
@@ -16,6 +16,9 @@
   -->
 
 <resources>
+<!-- PagedView  -->
+    <dimen name="min_page_snap_velocity">3600dp</dimen>
+
 <!-- Hotseat -->
     <dimen name="spring_loaded_hotseat_top_margin">44dp</dimen>
 
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
new file mode 100644
index 0000000..072b92d
--- /dev/null
+++ b/res/values-sw600dp/config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+
+    <!-- The duration of the PagedView page snap animation -->
+    <integer name="config_pageSnapAnimationDuration">550</integer>
+
+</resources>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index eb347f2..d69e777 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -15,6 +15,9 @@
 -->
 
 <resources>
+<!-- PagedView-->
+    <dimen name="min_page_snap_velocity">3000dp</dimen>
+
 <!-- DragController -->
     <dimen name="drag_flingToDeleteMinVelocity">-1000dp</dimen>
 
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
index 439ea93..235631d 100644
--- a/res/values-sw720dp-land/dimens.xml
+++ b/res/values-sw720dp-land/dimens.xml
@@ -15,6 +15,9 @@
 -->
 
 <resources>
+<!-- PagedView  -->
+    <dimen name="min_page_snap_velocity">5300dp</dimen>
+
 <!-- Dynamic grid -->
     <dimen name="dynamic_grid_edge_margin">21.93dp</dimen>
     <dimen name="cell_layout_padding">29.33dp</dimen>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index fad8c95..7b2ed8b 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -15,6 +15,9 @@
 -->
 
 <resources>
+<!-- PagedView  -->
+    <dimen name="min_page_snap_velocity">3400dp</dimen>
+
 <!-- AllApps -->
     <dimen name="all_apps_bottom_sheet_horizontal_padding">28dp</dimen>
 
@@ -28,6 +31,7 @@
     <dimen name="drop_target_button_drawable_horizontal_padding">24dp</dimen>
     <dimen name="drop_target_button_drawable_vertical_padding">20dp</dimen>
     <dimen name="drop_target_button_gap">32dp</dimen>
+    <dimen name="drop_target_button_workspace_edge_gap">32dp</dimen>
     <dimen name="drop_target_top_margin">110dp</dimen>
     <dimen name="drop_target_bottom_margin">48dp</dimen>
 
diff --git a/res/values/config.xml b/res/values/config.xml
index 5ecd929..9aa1f03 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -1,3 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
 <resources>
     <!-- Miscellaneous -->
     <bool name="config_largeHeap">false</bool>
@@ -25,6 +40,9 @@
     <!-- The duration of the animation from search hint to text entry -->
     <integer name="config_searchHintAnimationDuration">50</integer>
 
+    <!-- The duration of the PagedView page snap animation -->
+    <integer name="config_pageSnapAnimationDuration">750</integer>
+
     <!-- View tag key used to store SpringAnimation data. -->
     <item type="id" name="spring_animation_tag" />
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 098c694..dd99a7a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -24,6 +24,8 @@
     <dimen name="dynamic_grid_icon_drawable_padding">7dp</dimen>
     <!-- Minimum space between workspace and hotseat in spring loaded mode -->
     <dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen>
+    <!-- Minimum amount of next page visible in spring loaded mode -->
+    <dimen name="dynamic_grid_spring_loaded_min_next_space_visible">24dp</dimen>
 
     <dimen name="dynamic_grid_cell_border_spacing">16dp</dimen>
     <dimen name="cell_layout_padding">10.77dp</dimen>
@@ -58,13 +60,9 @@
 <!-- Drop target bar -->
     <dimen name="dynamic_grid_drop_target_size">56dp</dimen>
     <dimen name="drop_target_vertical_gap">20dp</dimen>
-    <dimen name="drop_target_top_margin">36dp</dimen>
+    <dimen name="drop_target_top_margin">32dp</dimen>
     <dimen name="drop_target_bottom_margin">16dp</dimen>
 
-    <!-- Button drop target bar -->
-    <dimen name="button_drop_target_min_text_size">10sp</dimen>
-    <dimen name="button_drop_target_resize_text_increment">1sp</dimen>
-
 <!-- App Widget resize frame -->
     <dimen name="widget_handle_margin">13dp</dimen>
     <dimen name="resize_frame_background_padding">24dp</dimen>
@@ -99,6 +97,13 @@
     <dimen name="fastscroll_width">58dp</dimen>
     <dimen name="fastscroll_end_margin">-26dp</dimen>
 
+    <!-- PagedView -->
+    <dimen name="fling_threshold_velocity">500dp</dimen>
+    <dimen name="easy_fling_threshold_velocity">400dp</dimen>
+    <dimen name="min_fling_velocity">250dp</dimen>
+    <!-- The minimum velocity of a page snap after a fling gesture -->
+    <dimen name="min_page_snap_velocity">1500dp</dimen>
+
     <!-- All Apps -->
     <dimen name="all_apps_starting_vertical_translate">300dp</dimen>
     <dimen name="all_apps_search_bar_field_height">48dp</dimen>
@@ -229,7 +234,9 @@
     <dimen name="drop_target_button_drawable_padding">8dp</dimen>
     <dimen name="drop_target_button_drawable_horizontal_padding">16dp</dimen>
     <dimen name="drop_target_button_drawable_vertical_padding">8dp</dimen>
-    <dimen name="drop_target_button_gap">22dp</dimen>
+    <dimen name="drop_target_button_gap">28dp</dimen>
+    <dimen name="drop_target_button_workspace_edge_gap">0dp</dimen>
+    <dimen name="drop_target_button_screen_edge_gap">28dp</dimen>
 
     <!-- the distance an icon must be dragged before button drop targets accept it -->
     <dimen name="drag_distanceThreshold">30dp</dimen>
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 0b07c95..8da4f05 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -24,6 +24,7 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.text.InputType;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
@@ -49,6 +50,8 @@
     private static final int[] sTempCords = new int[2];
     private static final int DRAG_VIEW_DROP_DURATION = 285;
     private static final float DRAG_VIEW_HOVER_OVER_OPACITY = 0.65f;
+    private static final int MAX_LINES_TEXT_MULTI_LINE = 2;
+    private static final int MAX_LINES_TEXT_SINGLE_LINE = 1;
 
     public static final int TOOLTIP_DEFAULT = 0;
     public static final int TOOLTIP_LEFT = 1;
@@ -72,6 +75,8 @@
     protected CharSequence mText;
     protected Drawable mDrawable;
     private boolean mTextVisible = true;
+    private boolean mIconVisible = true;
+    private boolean mTextMultiLine = true;
 
     private PopupWindow mToolTip;
     private int mToolTipLocation;
@@ -109,8 +114,7 @@
         // drawableLeft and drawableStart.
         mDrawable = getContext().getDrawable(resId).mutate();
         mDrawable.setTintList(getTextColors());
-        centerIcon();
-        setCompoundDrawablesRelative(mDrawable, null, null, null);
+        updateIconVisibility();
     }
 
     public void setDropTargetBar(DropTargetBar dropTargetBar) {
@@ -306,13 +310,49 @@
         if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) {
             mTextVisible = isVisible;
             setText(newText);
-            centerIcon();
-            setCompoundDrawablesRelative(mDrawable, null, null, null);
-            int drawablePadding = mTextVisible ? mDrawablePadding : 0;
-            setCompoundDrawablePadding(drawablePadding);
+            updateIconVisibility();
         }
     }
 
+    /**
+     * Display button text over multiple lines when isMultiLine is true, single line otherwise.
+     */
+    public void setTextMultiLine(boolean isMultiLine) {
+        if (mTextMultiLine != isMultiLine) {
+            mTextMultiLine = isMultiLine;
+            setSingleLine(!isMultiLine);
+            setMaxLines(isMultiLine ? MAX_LINES_TEXT_MULTI_LINE : MAX_LINES_TEXT_SINGLE_LINE);
+            int inputType = InputType.TYPE_CLASS_TEXT;
+            if (isMultiLine) {
+                inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
+
+            }
+            setInputType(inputType);
+        }
+    }
+
+    protected boolean isTextMultiLine() {
+        return mTextMultiLine;
+    }
+
+    /**
+     * Sets the button icon visible when isVisible is true, hides it otherwise.
+     */
+    public void setIconVisible(boolean isVisible) {
+        if (mIconVisible != isVisible) {
+            mIconVisible = isVisible;
+            updateIconVisibility();
+        }
+    }
+
+    private void updateIconVisibility() {
+        if (mIconVisible) {
+            centerIcon();
+        }
+        setCompoundDrawablesRelative(mIconVisible ? mDrawable : null, null, null, null);
+        setCompoundDrawablePadding(mIconVisible && mTextVisible ? mDrawablePadding : 0);
+    }
+
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
@@ -324,40 +364,6 @@
         hideTooltip();
     }
 
-
-    /**
-     * Reduce the size of the text until it fits or reaches a minimum.
-     *
-     * The minimum size is defined by {@code R.dimen.button_drop_target_min_text_size} and
-     * it diminishes by intervals defined by
-     * {@code R.dimen.button_drop_target_resize_text_increment}
-     * This functionality is very similar to the option
-     * {@link TextView#setAutoSizeTextTypeWithDefaults(int)} but can't be used in this view because
-     * the layout width is {@code WRAP_CONTENT}.
-     *
-     * @param availableWidth Available width in the button to fit the text, used in
-     *        {@code ButtonDropTarget#isTextTruncated(int)}
-     * @return The biggest text size in SP that makes the text fit or if the text can't fit returns
-     *         the min available value
-     */
-    public float resizeTextToFit(int availableWidth) {
-        float minSize = Utilities.pxToSp(getResources()
-                .getDimensionPixelSize(R.dimen.button_drop_target_min_text_size));
-        float step = Utilities.pxToSp(getResources()
-                .getDimensionPixelSize(R.dimen.button_drop_target_resize_text_increment));
-        float textSize = Utilities.pxToSp(getTextSize());
-
-        while (textSize > minSize) {
-            if (isTextTruncated(availableWidth)) {
-                textSize -= step;
-                setTextSize(textSize);
-            } else {
-                return textSize;
-            }
-        }
-        return minSize;
-    }
-
     public boolean isTextTruncated(int availableWidth) {
         availableWidth -= (getPaddingLeft() + getPaddingRight() + mDrawable.getIntrinsicWidth()
                 + getCompoundDrawablePadding());
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index be180a6..f028d3c 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -108,6 +108,7 @@
     public float workspaceSpringLoadShrunkTop;
     public float workspaceSpringLoadShrunkBottom;
     public final int workspaceSpringLoadedBottomSpace;
+    public final int workspaceSpringLoadedMinNextPageVisiblePx;
 
     private final int extraSpace;
     public int workspaceTopPadding;
@@ -214,6 +215,8 @@
     public int dropTargetHorizontalPaddingPx;
     public int dropTargetVerticalPaddingPx;
     public int dropTargetGapPx;
+    public int dropTargetButtonWorkspaceEdgeGapPx;
+    public int dropTargetButtonScreenEdgeGapPx;
 
     // Insets
     private final Rect mInsets = new Rect();
@@ -343,9 +346,15 @@
         dropTargetVerticalPaddingPx = res.getDimensionPixelSize(
                 R.dimen.drop_target_button_drawable_vertical_padding);
         dropTargetGapPx = res.getDimensionPixelSize(R.dimen.drop_target_button_gap);
+        dropTargetButtonWorkspaceEdgeGapPx = res.getDimensionPixelSize(
+                R.dimen.drop_target_button_workspace_edge_gap);
+        dropTargetButtonScreenEdgeGapPx = res.getDimensionPixelSize(
+                R.dimen.drop_target_button_screen_edge_gap);
 
         workspaceSpringLoadedBottomSpace =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
+        workspaceSpringLoadedMinNextPageVisiblePx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_spring_loaded_min_next_space_visible);
 
         workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
@@ -501,7 +510,7 @@
      */
     private int calculateQsbWidth() {
         if (isQsbInline) {
-            int columns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
+            int columns = getPanelCount() * inv.numColumns;
             return getIconToIconWidthForColumns(columns)
                     - iconSizePx * numShownHotseatIcons
                     - hotseatBorderSpace * numShownHotseatIcons;
@@ -955,13 +964,6 @@
     }
 
     /**
-     * Gets the minimum visible amount of the next workspace page when in the spring-loaded state.
-     */
-    private float getWorkspaceSpringLoadedMinimumNextPageVisible() {
-        return getCellSize().x / 2f;
-    }
-
-    /**
      * Gets the scale of the workspace for the spring-loaded edit state.
      */
     public float getWorkspaceSpringLoadScale() {
@@ -972,8 +974,7 @@
         // Reduce scale if next pages would not be visible after scaling the workspace
         int workspaceWidth = availableWidthPx;
         float scaledWorkspaceWidth = workspaceWidth * scale;
-        float maxAvailableWidth =
-                workspaceWidth - (2 * getWorkspaceSpringLoadedMinimumNextPageVisible());
+        float maxAvailableWidth = workspaceWidth - (2 * workspaceSpringLoadedMinNextPageVisiblePx);
         if (scaledWorkspaceWidth > maxAvailableWidth) {
             scale *= maxAvailableWidth / scaledWorkspaceWidth;
         }
@@ -1412,11 +1413,19 @@
         writer.println(prefix + pxToDpStr("dropTargetBarSizePx", dropTargetBarSizePx));
         writer.println(
                 prefix + pxToDpStr("dropTargetBarBottomMarginPx", dropTargetBarBottomMarginPx));
+        writer.println(prefix + pxToDpStr("dropTargetButtonWorkspaceEdgeGapPx",
+                dropTargetButtonWorkspaceEdgeGapPx));
+        writer.println(prefix + pxToDpStr("dropTargetButtonScreenEdgeGapPx",
+                dropTargetButtonScreenEdgeGapPx));
 
         writer.println(
                 prefix + pxToDpStr("workspaceSpringLoadShrunkTop", workspaceSpringLoadShrunkTop));
         writer.println(prefix + pxToDpStr("workspaceSpringLoadShrunkBottom",
                 workspaceSpringLoadShrunkBottom));
+        writer.println(prefix + pxToDpStr("workspaceSpringLoadedBottomSpace",
+                workspaceSpringLoadedBottomSpace));
+        writer.println(prefix + pxToDpStr("workspaceSpringLoadedMinNextPageVisiblePx",
+                workspaceSpringLoadedMinNextPageVisiblePx));
         writer.println(
                 prefix + pxToDpStr("getWorkspaceSpringLoadScale()", getWorkspaceSpringLoadScale()));
     }
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 2e3f26c..6a8ba1b 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -39,8 +39,6 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.testing.TestProtocol;
 
-import java.util.Arrays;
-
 /*
  * The top bar containing various drop targets: Delete/App Info/Uninstall.
  */
@@ -53,6 +51,8 @@
     private final Runnable mFadeAnimationEndRunnable =
             () -> updateVisibility(DropTargetBar.this);
 
+    private final Launcher mLauncher;
+
     @ViewDebug.ExportedProperty(category = "launcher")
     protected boolean mDeferOnDragEnd;
 
@@ -60,16 +60,19 @@
     protected boolean mVisible = false;
 
     private ButtonDropTarget[] mDropTargets;
+    private ButtonDropTarget[] mTempTargets;
     private ViewPropertyAnimator mCurrentAnimation;
 
     private boolean mIsVertical = true;
 
     public DropTargetBar(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mLauncher = Launcher.getLauncher(context);
     }
 
     public DropTargetBar(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        mLauncher = Launcher.getLauncher(context);
     }
 
     @Override
@@ -80,12 +83,13 @@
             mDropTargets[i] = (ButtonDropTarget) getChildAt(i);
             mDropTargets[i].setDropTargetBar(this);
         }
+        mTempTargets = new ButtonDropTarget[getChildCount()];
     }
 
     @Override
     public void setInsets(Rect insets) {
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
-        DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         mIsVertical = grid.isVerticalBarLayout();
 
         lp.leftMargin = insets.left;
@@ -116,10 +120,15 @@
         lp.height = grid.dropTargetBarSizePx;
         lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
 
+        DeviceProfile dp = mLauncher.getDeviceProfile();
+        int horizontalPadding = dp.dropTargetHorizontalPaddingPx;
+        int verticalPadding = dp.dropTargetVerticalPaddingPx;
         setLayoutParams(lp);
         for (ButtonDropTarget button : mDropTargets) {
             button.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.dropTargetTextSizePx);
             button.setToolTipLocation(tooltipLocation);
+            button.setPadding(horizontalPadding, verticalPadding, horizontalPadding,
+                    verticalPadding);
         }
     }
 
@@ -135,36 +144,83 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int width = MeasureSpec.getSize(widthMeasureSpec);
         int height = MeasureSpec.getSize(heightMeasureSpec);
+        int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
 
-        int visibleCount = getVisibleButtonsCount();
-        if (visibleCount > 0) {
-            int availableWidth = width / visibleCount;
-            boolean textVisible = true;
-            boolean textResized = false;
-            float textSize = mDropTargets[0].getTextSize();
-            for (ButtonDropTarget button : mDropTargets) {
-                if (button.getVisibility() == GONE) {
-                    continue;
-                }
-                if (button.isTextTruncated(availableWidth)) {
-                    textSize = Math.min(textSize, button.resizeTextToFit(availableWidth));
-                    textResized = true;
-                }
-                textVisible = textVisible && !button.isTextTruncated(availableWidth);
-            }
+        int visibleCount = getVisibleButtons(mTempTargets);
+        if (visibleCount == 1) {
+            int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST);
 
-            if (textResized) {
-                for (ButtonDropTarget button : mDropTargets) {
-                    button.setTextSize(textSize);
-                }
-            }
+            ButtonDropTarget firstButton = mTempTargets[0];
+            firstButton.setTextVisible(true);
+            firstButton.setIconVisible(true);
+            firstButton.measure(widthSpec, heightSpec);
+        } else if (visibleCount == 2) {
+            DeviceProfile dp = mLauncher.getDeviceProfile();
+            int verticalPadding = dp.dropTargetVerticalPaddingPx;
+            int horizontalPadding = dp.dropTargetHorizontalPaddingPx;
 
-            int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
-            int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
-            for (ButtonDropTarget button : mDropTargets) {
-                if (button.getVisibility() != GONE) {
-                    button.setTextVisible(textVisible);
-                    button.measure(widthSpec, heightSpec);
+            ButtonDropTarget firstButton = mTempTargets[0];
+            firstButton.setTextVisible(true);
+            firstButton.setIconVisible(true);
+
+            ButtonDropTarget secondButton = mTempTargets[1];
+            secondButton.setTextVisible(true);
+            secondButton.setIconVisible(true);
+            secondButton.setTextMultiLine(false);
+            // Reset second button padding in case it was previously changed to multi-line text.
+            secondButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding,
+                    verticalPadding);
+
+            if (dp.isTwoPanels) {
+                // Both buttons for two panel fit to the width of one Cell Layout (less
+                // half of the center gap between the buttons).
+                float scale = dp.getWorkspaceSpringLoadScale();
+                int scaledPanelWidth = (int) (dp.getCellLayoutWidth() * scale);
+                int halfButtonGap = dp.dropTargetGapPx / 2;
+                scaledPanelWidth -= halfButtonGap / 2;
+
+                int widthSpec = MeasureSpec.makeMeasureSpec(scaledPanelWidth, MeasureSpec.AT_MOST);
+                firstButton.measure(widthSpec, heightSpec);
+                secondButton.measure(widthSpec, heightSpec);
+            } else {
+                int availableWidth;
+                int buttonGap = dp.dropTargetGapPx;
+                if (mIsVertical) {
+                    // Both buttons plus the button gap do not display past the edge of the
+                    // scaled workspace, less a pre-defined gap from the edge of the workspace.
+                    float scale = dp.getWorkspaceSpringLoadScale();
+                    int panelWidth = (int) (dp.getCellLayoutWidth() * scale);
+                    availableWidth = Math.min(
+                            panelWidth - (2 * dp.dropTargetButtonWorkspaceEdgeGapPx), width);
+                } else {
+                    // Both buttons plus the button gap display up to a pre-defined margin of
+                    // the unscaled workspace edge.
+                    availableWidth = Math.min(
+                            dp.availableWidthPx - (2 * dp.dropTargetButtonScreenEdgeGapPx),
+                            width);
+                }
+                int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth - buttonGap,
+                        MeasureSpec.AT_MOST);
+
+                // First button's width is at most the drop target bar's total width less the button
+                // gap.
+                firstButton.measure(widthSpec, heightSpec);
+
+                int usedWidth = firstButton.getMeasuredWidth() + buttonGap;
+                int remainingWidth = availableWidth - usedWidth;
+                widthSpec = MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST);
+                secondButton.measure(widthSpec, heightSpec);
+
+                // Remove both icons and put the second button's text on two lines if text is
+                // truncated on phones. We assume first button's text is never truncated, so it
+                // remains single-line.
+                if (secondButton.isTextTruncated(remainingWidth) && !mIsVertical) {
+                    firstButton.setIconVisible(false);
+                    secondButton.setIconVisible(false);
+                    secondButton.setTextMultiLine(true);
+                    secondButton.setPadding(secondButton.getPaddingLeft(),
+                            secondButton.getPaddingTop() / 2, secondButton.getPaddingRight(),
+                            secondButton.getPaddingBottom() / 2);
                 }
             }
         }
@@ -173,98 +229,79 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        int visibleCount = getVisibleButtonsCount();
+        int visibleCount = getVisibleButtons(mTempTargets);
         if (visibleCount == 0) {
             return;
         }
 
-        Launcher launcher = Launcher.getLauncher(getContext());
-        Workspace<?> workspace = launcher.getWorkspace();
-        DeviceProfile dp = launcher.getDeviceProfile();
-        int buttonHorizontalPadding = dp.dropTargetHorizontalPaddingPx;
-        int buttonVerticalPadding = dp.dropTargetVerticalPaddingPx;
+        DeviceProfile dp = mLauncher.getDeviceProfile();
         int barCenter = (right - left) / 2;
-
-        ButtonDropTarget[] visibleButtons = Arrays.stream(mDropTargets)
-                .filter(b -> b.getVisibility() != GONE)
-                .toArray(ButtonDropTarget[]::new);
-        Arrays.stream(visibleButtons).forEach(
-                b -> b.setPadding(buttonHorizontalPadding, buttonVerticalPadding,
-                        buttonHorizontalPadding, buttonVerticalPadding));
+        if (mIsVertical) {
+            // Center vertical bar over scaled workspace, accounting for hotseat offset.
+            float scale = dp.getWorkspaceSpringLoadScale();
+            Workspace<?> ws = mLauncher.getWorkspace();
+            int workspaceCenter = (ws.getLeft() + ws.getRight()) / 2;
+            int cellLayoutCenter = ((dp.getInsets().left + dp.workspacePadding.left) + (dp.widthPx
+                    - dp.getInsets().right - dp.workspacePadding.right)) / 2;
+            int cellLayoutCenterOffset = (int) ((cellLayoutCenter - workspaceCenter) * scale);
+            barCenter = workspaceCenter + cellLayoutCenterOffset;
+        }
 
         if (visibleCount == 1) {
-            ButtonDropTarget button = visibleButtons[0];
+            ButtonDropTarget button = mTempTargets[0];
             button.layout(barCenter - (button.getMeasuredWidth() / 2), 0,
                     barCenter + (button.getMeasuredWidth() / 2), button.getMeasuredHeight());
         } else if (visibleCount == 2) {
             int buttonGap = dp.dropTargetGapPx;
 
             if (dp.isTwoPanels) {
-                ButtonDropTarget leftButton = visibleButtons[0];
+                ButtonDropTarget leftButton = mTempTargets[0];
                 leftButton.layout(barCenter - leftButton.getMeasuredWidth() - (buttonGap / 2), 0,
                         barCenter - (buttonGap / 2), leftButton.getMeasuredHeight());
 
-                ButtonDropTarget rightButton = visibleButtons[1];
+                ButtonDropTarget rightButton = mTempTargets[1];
                 rightButton.layout(barCenter + (buttonGap / 2), 0,
-                        barCenter + rightButton.getMeasuredWidth() + (buttonGap / 2),
+                        barCenter + (buttonGap / 2) + rightButton.getMeasuredWidth(),
                         rightButton.getMeasuredHeight());
-            } else if (dp.isTablet) {
-                int numberOfMargins = visibleCount - 1;
-                int buttonWidths = Arrays.stream(mDropTargets)
-                        .filter(b -> b.getVisibility() != GONE)
-                        .mapToInt(ButtonDropTarget::getMeasuredWidth)
-                        .sum();
-                int totalWidth = buttonWidths + (numberOfMargins * buttonGap);
-                int buttonsStartMargin = barCenter - (totalWidth / 2);
-
-                int start = buttonsStartMargin;
-                for (ButtonDropTarget button : visibleButtons) {
-                    int margin = (start != buttonsStartMargin) ? buttonGap : 0;
-                    button.layout(start + margin, 0, start + margin + button.getMeasuredWidth(),
-                            button.getMeasuredHeight());
-                    start += button.getMeasuredWidth() + margin;
-                }
-            } else if (mIsVertical) {
-                // Center buttons over workspace, not screen.
-                int verticalCenter = (workspace.getRight() - workspace.getLeft()) / 2;
-                ButtonDropTarget leftButton = visibleButtons[0];
-                leftButton.layout(verticalCenter - leftButton.getMeasuredWidth() - (buttonGap / 2),
-                        0, verticalCenter - (buttonGap / 2), leftButton.getMeasuredHeight());
-
-                ButtonDropTarget rightButton = visibleButtons[1];
-                rightButton.layout(verticalCenter + (buttonGap / 2), 0,
-                        verticalCenter + rightButton.getMeasuredWidth() + (buttonGap / 2),
-                        rightButton.getMeasuredHeight());
-            } else if (dp.isPhone) {
-                // Buttons aligned to outer edges of scaled workspace.
-                float scale = dp.getWorkspaceSpringLoadScale();
-
-                int workspaceWidth = (int) (launcher.getWorkspace().getNormalChildWidth() * scale);
-                int start = barCenter - (workspaceWidth / 2);
-                int end = barCenter + (workspaceWidth / 2);
-
-                ButtonDropTarget leftButton = visibleButtons[0];
-                ButtonDropTarget rightButton = visibleButtons[1];
-
-                // If the text within the buttons is too long, the buttons can overlap
-                int overlap = start + leftButton.getMeasuredWidth() + rightButton.getMeasuredWidth()
-                        - end;
-                if (overlap > 0) {
-                    end += overlap;
+            } else {
+                int start;
+                int end;
+                if (mIsVertical) {
+                    // Scaled CellLayout width is assumed to not exceed the bounds of left/right.
+                    float scale = dp.getWorkspaceSpringLoadScale();
+                    int panelWidth = (int) (dp.getCellLayoutWidth() * scale);
+                    start = barCenter - (panelWidth / 2) + dp.dropTargetButtonWorkspaceEdgeGapPx;
+                    end = barCenter + (panelWidth / 2) - dp.dropTargetButtonWorkspaceEdgeGapPx;
+                } else {
+                    start = Math.max(dp.dropTargetButtonScreenEdgeGapPx, left);
+                    end = Math.min(dp.availableWidthPx - dp.dropTargetButtonScreenEdgeGapPx, right);
                 }
 
-                leftButton.layout(start, 0, start + leftButton.getMeasuredWidth(),
+                ButtonDropTarget leftButton = mTempTargets[0];
+                ButtonDropTarget rightButton = mTempTargets[1];
+
+                int leftButtonWidth = leftButton.getMeasuredWidth();
+                int rightButtonWidth = rightButton.getMeasuredWidth();
+                int buttonPlusGapWidth = leftButtonWidth + buttonGap + rightButtonWidth;
+
+                int extraSpace = end - start - buttonPlusGapWidth;
+                start = (start - left) + (extraSpace / 2);
+
+                leftButton.layout(start, 0, start + leftButtonWidth,
                         leftButton.getMeasuredHeight());
-                rightButton.layout(end - rightButton.getMeasuredWidth(), 0, end,
+
+                int rightButtonStart = start + leftButtonWidth + buttonGap;
+                rightButton.layout(rightButtonStart, 0, rightButtonStart + rightButtonWidth,
                         rightButton.getMeasuredHeight());
             }
         }
     }
 
-    private int getVisibleButtonsCount() {
+    private int getVisibleButtons(ButtonDropTarget[] outVisibleButtons) {
         int visibleCount = 0;
-        for (ButtonDropTarget buttons : mDropTargets) {
-            if (buttons.getVisibility() != GONE) {
+        for (ButtonDropTarget button : mDropTargets) {
+            if (button.getVisibility() != GONE) {
+                outVisibleButtons[visibleCount] = button;
                 visibleCount++;
             }
         }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 95a8a2a..4d33eae 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -28,6 +28,8 @@
 import android.animation.LayoutTransition;
 import android.annotation.SuppressLint;
 import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Rect;
@@ -78,27 +80,19 @@
     public static final int INVALID_PAGE = -1;
     protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE;
 
-    public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
-
     private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
     // The page is moved more than halfway, automatically move to the next page on touch up.
     private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f;
 
     private static final float MAX_SCROLL_PROGRESS = 1.0f;
 
-    // The following constants need to be scaled based on density. The scaled versions will be
-    // assigned to the corresponding member variables below.
-    private static final int FLING_THRESHOLD_VELOCITY = 500;
-    private static final int EASY_FLING_THRESHOLD_VELOCITY = 400;
-    private static final int MIN_SNAP_VELOCITY = 1500;
-    private static final int MIN_FLING_VELOCITY = 250;
-
     private boolean mFreeScroll = false;
 
-    protected final int mFlingThresholdVelocity;
-    protected final int mEasyFlingThresholdVelocity;
-    protected final int mMinFlingVelocity;
-    protected final int mMinSnapVelocity;
+    private int mFlingThresholdVelocity;
+    private int mEasyFlingThresholdVelocity;
+    private int mMinFlingVelocity;
+    private int mMinSnapVelocity;
+    private int mPageSnapAnimationDuration;
 
     protected boolean mFirstLayout = true;
 
@@ -192,11 +186,7 @@
         mPageSlop = configuration.getScaledPagingTouchSlop();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
 
-        float density = getResources().getDisplayMetrics().density;
-        mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * density);
-        mEasyFlingThresholdVelocity = (int) (EASY_FLING_THRESHOLD_VELOCITY * density);
-        mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density);
-        mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
+        updateVelocityValues();
 
         initEdgeEffect();
         setDefaultFocusHighlightEnabled(false);
@@ -628,6 +618,22 @@
                 - mInsets.left - mInsets.right;
     }
 
+    private void updateVelocityValues() {
+        Resources res = getResources();
+        mFlingThresholdVelocity = res.getDimensionPixelSize(R.dimen.fling_threshold_velocity);
+        mEasyFlingThresholdVelocity =
+                res.getDimensionPixelSize(R.dimen.easy_fling_threshold_velocity);
+        mMinFlingVelocity = res.getDimensionPixelSize(R.dimen.min_fling_velocity);
+        mMinSnapVelocity = res.getDimensionPixelSize(R.dimen.min_page_snap_velocity);
+        mPageSnapAnimationDuration = res.getInteger(R.integer.config_pageSnapAnimationDuration);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateVelocityValues();
+    }
+
     @Override
     public void requestLayout() {
         mIsLayoutValid = false;
@@ -1616,7 +1622,7 @@
     }
 
     protected void snapToDestination() {
-        snapToPage(getDestinationPage(), PAGE_SNAP_ANIMATION_DURATION);
+        snapToPage(getDestinationPage(), mPageSnapAnimationDuration);
     }
 
     // We want the duration of the page snap animation to be influenced by the distance that
@@ -1640,7 +1646,7 @@
         if (Math.abs(velocity) < mMinFlingVelocity) {
             // If the velocity is low enough, then treat this more as an automatic page advance
             // as opposed to an apparent physical response to flinging
-            return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+            return snapToPage(whichPage, mPageSnapAnimationDuration);
         }
 
         // Here we compute a "distance" that will be used in the computation of the overall
@@ -1663,11 +1669,11 @@
     }
 
     public boolean snapToPage(int whichPage) {
-        return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+        return snapToPage(whichPage, mPageSnapAnimationDuration);
     }
 
     public boolean snapToPageImmediately(int whichPage) {
-        return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true);
+        return snapToPage(whichPage, mPageSnapAnimationDuration, true);
     }
 
     public boolean snapToPage(int whichPage, int duration) {
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 95c67dd..7c1e152 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -388,34 +388,34 @@
         mInsets.set(insets);
         DeviceProfile grid = mActivityContext.getDeviceProfile();
 
-        for (int i = 0; i < mAH.size(); i++) {
-            mAH.get(i).mPadding.bottom = insets.bottom;
-            mAH.get(i).mPadding.left = mAH.get(i).mPadding.right = grid.allAppsLeftRightPadding;
-            mAH.get(i).applyPadding();
-        }
+        applyAdapterPaddings(grid);
 
         MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
-        int leftRightMargin = grid.allAppsLeftRightMargin;
-        mlp.leftMargin = insets.left + leftRightMargin;
-        mlp.rightMargin = insets.right + leftRightMargin;
+        mlp.leftMargin = insets.left;
+        mlp.rightMargin = insets.right;
         setLayoutParams(mlp);
 
         if (grid.isVerticalBarLayout()) {
             setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
         } else {
-            setPadding(0, grid.allAppsTopPadding, 0, 0);
+            setPadding(grid.allAppsLeftRightMargin, grid.allAppsTopPadding,
+                    grid.allAppsLeftRightMargin, 0);
         }
 
         InsettableFrameLayout.dispatchInsets(this, insets);
     }
 
+    /**
+     * Returns a padding in case a scrim is shown on the bottom of the view and a padding is needed.
+     */
+    protected int getNavBarScrimHeight(WindowInsets insets) {
+        return 0;
+    }
+
     @Override
     public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
-        if (Utilities.ATLEAST_Q) {
-            mNavBarScrimHeight = insets.getTappableElementInsets().bottom;
-        } else {
-            mNavBarScrimHeight = insets.getStableInsetBottom();
-        }
+        mNavBarScrimHeight = getNavBarScrimHeight(insets);
+        applyAdapterPaddings(mActivityContext.getDeviceProfile());
         return super.dispatchApplyWindowInsets(insets);
     }
 
@@ -483,6 +483,15 @@
         mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
     }
 
+    private void applyAdapterPaddings(DeviceProfile grid) {
+        int bottomPadding = Math.max(mInsets.bottom, mNavBarScrimHeight);
+        for (int i = 0; i < mAH.size(); i++) {
+            mAH.get(i).mPadding.bottom = bottomPadding;
+            mAH.get(i).mPadding.left = mAH.get(i).mPadding.right = grid.allAppsLeftRightPadding;
+            mAH.get(i).applyPadding();
+        }
+    }
+
     private void setDeviceManagementResources() {
         if (mActivityContext.getStringCache() != null) {
             Button personalTab = findViewById(R.id.tab_personal);
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index 8601819..20f5e74 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -18,9 +18,11 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
+import android.view.WindowInsets;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.Utilities;
 
 /**
  * AllAppsContainerView with launcher specific callbacks
@@ -58,4 +60,13 @@
         }
         return super.onTouchEvent(ev);
     }
+
+    @Override
+    protected int getNavBarScrimHeight(WindowInsets insets) {
+        if (Utilities.ATLEAST_Q) {
+            return insets.getTappableElementInsets().bottom;
+        } else {
+            return insets.getStableInsetBottom();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 512fa1c..e68ebdb 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -72,7 +72,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.OnAlarmListener;
-import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Utilities;
@@ -145,7 +144,7 @@
      * Time for which the scroll hint is shown before automatically changing page.
      */
     public static final int SCROLL_HINT_DURATION = 500;
-    public static final int RESCROLL_DELAY = PagedView.PAGE_SNAP_ANIMATION_DURATION + 150;
+    private static final int RESCROLL_EXTRA_DELAY = 150;
 
     public static final int SCROLL_NONE = -1;
     public static final int SCROLL_LEFT = 0;
@@ -1523,7 +1522,9 @@
 
             // Pause drag event until the scrolling is finished
             mScrollPauseAlarm.setOnAlarmListener(new OnScrollFinishedListener(mDragObject));
-            mScrollPauseAlarm.setAlarm(RESCROLL_DELAY);
+            int rescrollDelay = getResources().getInteger(
+                    R.integer.config_pageSnapAnimationDuration) + RESCROLL_EXTRA_DELAY;
+            mScrollPauseAlarm.setAlarm(rescrollDelay);
         }
     }
 
diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
index d2d569f..9442734 100644
--- a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
@@ -18,7 +18,6 @@
 
 import static com.android.launcher3.Utilities.ATLEAST_R;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.widget.BaseWidgetSheet.MAX_WIDTH_SCALE_FOR_LARGER_SCREEN;
 
 import android.animation.PropertyValuesHolder;
 import android.annotation.SuppressLint;
@@ -106,7 +105,10 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
         int widthUsed;
-        if (mInsets.bottom > 0) {
+        if (deviceProfile.isTablet) {
+            int margin = deviceProfile.allAppsLeftRightMargin;
+            widthUsed = Math.max(2 * margin, 2 * (mInsets.left + mInsets.right));
+        } else if (mInsets.bottom > 0) {
             widthUsed = mInsets.left + mInsets.right;
         } else {
             Rect padding = deviceProfile.workspacePadding;
@@ -114,18 +116,8 @@
                     2 * (mInsets.left + mInsets.right));
         }
 
-        if (deviceProfile.isTablet || deviceProfile.isTwoPanels) {
-            // In large screen devices, we restrict the width of the widgets picker to show part of
-            // the home screen. Let's ensure the minimum width used is at least the minimum width
-            // that isn't taken by the widgets picker.
-            int minUsedWidth = (int) (deviceProfile.availableWidthPx
-                    * (1 - MAX_WIDTH_SCALE_FOR_LARGER_SCREEN));
-            widthUsed = Math.max(widthUsed, minUsedWidth);
-        }
-
-        int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
         measureChildWithMargins(mContent, widthMeasureSpec,
-                widthUsed, heightMeasureSpec, heightUsed);
+                widthUsed, heightMeasureSpec, deviceProfile.bottomSheetTopPadding);
         setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
                 MeasureSpec.getSize(heightMeasureSpec));
     }
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index c7bb612..1bcba14 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -16,6 +16,8 @@
 package com.android.launcher3.widget;
 
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -23,6 +25,7 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
+import android.view.WindowInsets;
 import android.widget.Toast;
 
 import androidx.annotation.GuardedBy;
@@ -43,6 +46,7 @@
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.window.WindowManagerProxy;
 import com.android.launcher3.views.AbstractSlideInView;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.ArrowTipView;
@@ -55,11 +59,6 @@
         PopupDataProvider.PopupDataChangeListener, Insettable {
     /** The default number of cells that can fit horizontally in a widget sheet. */
     protected static final int DEFAULT_MAX_HORIZONTAL_SPANS = 4;
-    /**
-     * The maximum scale, [0, 1], of the device screen width that the widgets picker can consume
-     * on large screen devices.
-     */
-    protected static final float MAX_WIDTH_SCALE_FOR_LARGER_SCREEN = 0.89f;
 
     protected static final String KEY_WIDGETS_EDUCATION_TIP_SEEN =
             "launcher.widgets_education_tip_seen";
@@ -70,10 +69,15 @@
 
     private int mContentHorizontalMarginInPx;
 
+    protected int mNavBarScrimHeight;
+    private final Paint mNavBarScrimPaint;
+
     public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mContentHorizontalMarginInPx = getResources().getDimensionPixelSize(
                 R.dimen.widget_list_horizontal_margin);
+        mNavBarScrimPaint = new Paint();
+        mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
     }
 
     protected int getScrimColor(Context context) {
@@ -83,6 +87,9 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
+        WindowInsets windowInsets = WindowManagerProxy.INSTANCE.get(getContext())
+                .normalizeWindowInsets(getContext(), getRootWindowInsets(), new Rect());
+        mNavBarScrimHeight = getNavBarScrimHeight(windowInsets);
         mActivityContext.getPopupDataProvider().setChangeListener(this);
     }
 
@@ -136,6 +143,30 @@
         }
     }
 
+    private int getNavBarScrimHeight(WindowInsets insets) {
+        if (Utilities.ATLEAST_Q) {
+            return insets.getTappableElementInsets().bottom;
+        } else {
+            return insets.getStableInsetBottom();
+        }
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        mNavBarScrimHeight = getNavBarScrimHeight(insets);
+        return super.onApplyWindowInsets(insets);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+
+        if (mNavBarScrimHeight > 0) {
+            canvas.drawRect(0, getHeight() - mNavBarScrimHeight, getWidth(), getHeight(),
+                    mNavBarScrimPaint);
+        }
+    }
+
     /** Called when the horizontal margin of the content view has changed. */
     protected abstract void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx);
 
@@ -147,7 +178,10 @@
     protected void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
         int widthUsed;
-        if (mInsets.bottom > 0) {
+        if (deviceProfile.isTablet) {
+            int margin = deviceProfile.allAppsLeftRightMargin;
+            widthUsed = Math.max(2 * margin, 2 * (mInsets.left + mInsets.right));
+        } else if (mInsets.bottom > 0) {
             widthUsed = mInsets.left + mInsets.right;
         } else {
             Rect padding = deviceProfile.workspacePadding;
@@ -155,15 +189,6 @@
                     2 * (mInsets.left + mInsets.right));
         }
 
-        if (deviceProfile.isTablet || deviceProfile.isTwoPanels) {
-            // In large screen devices, we restrict the width of the widgets picker to show part of
-            // the home screen. Let's ensure the minimum width used is at least the minimum width
-            // that isn't taken by the widgets picker.
-            int minUsedWidth = (int) (deviceProfile.availableWidthPx
-                    * (1 - MAX_WIDTH_SCALE_FOR_LARGER_SCREEN));
-            widthUsed = Math.max(widthUsed, minUsedWidth);
-        }
-
         measureChildWithMargins(mContent, widthMeasureSpec,
                 widthUsed, heightMeasureSpec, deviceProfile.bottomSheetTopPadding);
         setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index b152ddc..bf521cc 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -247,10 +247,12 @@
     @Override
     public void setInsets(Rect insets) {
         super.setInsets(insets);
+        int bottomPadding = Math.max(insets.bottom, mNavBarScrimHeight);
 
         mContent.setPadding(mContent.getPaddingStart(),
-                mContent.getPaddingTop(), mContent.getPaddingEnd(), insets.bottom);
-        if (insets.bottom > 0) {
+                mContent.getPaddingTop(), mContent.getPaddingEnd(),
+                bottomPadding);
+        if (bottomPadding > 0) {
             setupNavBarColor();
         } else {
             clearNavBarColor();
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 341cb5c..a49cdc0 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -328,15 +328,15 @@
     @Override
     public void setInsets(Rect insets) {
         super.setInsets(insets);
-
-        setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, insets.bottom);
-        setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, insets.bottom);
+        int bottomPadding = Math.max(insets.bottom, mNavBarScrimHeight);
+        setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, bottomPadding);
+        setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, bottomPadding);
         if (mHasWorkProfile) {
-            setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, insets.bottom);
+            setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, bottomPadding);
         }
-        ((MarginLayoutParams) mNoWidgetsView.getLayoutParams()).bottomMargin = insets.bottom;
+        ((MarginLayoutParams) mNoWidgetsView.getLayoutParams()).bottomMargin = bottomPadding;
 
-        if (insets.bottom > 0) {
+        if (bottomPadding > 0) {
             setupNavBarColor();
         } else {
             clearNavBarColor();