Merge "Animate live tile translation and update live tile page scroll on dismissal of non-live tile tasks." into sc-v2-dev
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index d32c115..d61a895 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -13,12 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <com.android.launcher3.taskbar.TaskbarDragLayer
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/taskbar_container"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:clipChildren="false">
 
     <com.android.launcher3.taskbar.TaskbarView
         android:id="@+id/taskbar_view"
@@ -26,39 +26,32 @@
         android:layout_height="wrap_content"
         android:gravity="center"
         android:forceHasOverlappingRendering="false"
+        android:layout_gravity="bottom"
+        android:clipChildren="false" />
+
+    <FrameLayout
+        android:id="@+id/navbuttons_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
         android:layout_gravity="bottom" >
 
         <LinearLayout
-            android:id="@+id/nav_button_layout"
+            android:id="@+id/start_nav_buttons"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
             android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
-            android:forceHasOverlappingRendering="false"
-            android:gravity="center" />
+            android:gravity="center_vertical"
+            android:layout_gravity="start"/>
 
-        <LinearLayout
-            android:id="@+id/hotseat_icons_layout"
+        <FrameLayout
+            android:id="@+id/end_nav_buttons"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:forceHasOverlappingRendering="false"
-            android:gravity="center" />
-
-        <LinearLayout
-            android:id="@+id/contextual_button_layout"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
             android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
-            android:forceHasOverlappingRendering="false"
-            android:gravity="center" />
-
-    </com.android.launcher3.taskbar.TaskbarView>
-
-    <com.android.launcher3.taskbar.ImeBarView
-        android:id="@+id/ime_bar_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:visibility="gone"/>
+            android:gravity="center_vertical"
+            android:layout_gravity="end"/>
+    </FrameLayout>
 
 </com.android.launcher3.taskbar.TaskbarDragLayer>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_nav_button.xml b/quickstep/res/layout/taskbar_nav_button.xml
new file mode 100644
index 0000000..985f928
--- /dev/null
+++ b/quickstep/res/layout/taskbar_nav_button.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/taskbar_nav_buttons_size"
+    android:layout_height="@dimen/taskbar_nav_buttons_size"
+    android:background="@drawable/taskbar_icon_click_feedback_roundrect"
+    android:scaleType="center"/>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index d9c33ae..d8899a6 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -148,11 +148,9 @@
 
     <!-- Taskbar -->
     <dimen name="taskbar_size">60dp</dimen>
-    <dimen name="taskbar_icon_size">44dp</dimen>
     <dimen name="taskbar_icon_touch_size">48dp</dimen>
     <dimen name="taskbar_icon_drag_icon_size">54dp</dimen>
-    <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
-    <dimen name="taskbar_icon_spacing">8dp</dimen>
     <dimen name="taskbar_folder_margin">16dp</dimen>
     <dimen name="taskbar_nav_buttons_spacing">16dp</dimen>
+    <dimen name="taskbar_nav_buttons_size">48dp</dimen>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 2a86e81..8a7d01b 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -351,14 +351,6 @@
     }
 
     @Override
-    public float getNormalTaskbarScale() {
-        if (mTaskbarUIController != null) {
-            return mTaskbarUIController.getTaskbarScaleOnHome();
-        }
-        return super.getNormalTaskbarScale();
-    }
-
-    @Override
     public void onDragLayerHierarchyChanged() {
         onLauncherStateOrFocusChanged();
     }
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 14b0c5d..c7c2567 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -85,7 +85,7 @@
         mSampleHotseat = findViewById(R.id.sample_prediction);
 
         DeviceProfile grid = mActivityContext.getDeviceProfile();
-        Rect padding = grid.getHotseatLayoutPadding();
+        Rect padding = grid.getHotseatLayoutPadding(getContext());
 
         mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
         mSampleHotseat.setGridSize(grid.numShownHotseatIcons, 1);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 46ef698..5b4e5f2 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -161,7 +161,7 @@
         if (mSurface != surface) {
             mSurface = surface;
             if (surface != null) {
-                setDepth(mDepth);
+                dispatchTransactionSurface(mDepth);
             }
         }
     }
@@ -175,6 +175,8 @@
         float toDepth = toState.getDepth(mLauncher);
         if (Float.compare(mDepth, toDepth) != 0) {
             setDepth(toDepth);
+        } else if (toState == LauncherState.OVERVIEW) {
+            dispatchTransactionSurface(mDepth);
         }
     }
 
@@ -200,26 +202,35 @@
         if (Float.compare(mDepth, depthF) == 0) {
             return;
         }
+        if (dispatchTransactionSurface(depthF)) {
+            mDepth = depthF;
+        }
+    }
 
+    private boolean dispatchTransactionSurface(float depth) {
         boolean supportsBlur = BlurUtils.supportsBlursOnWindows();
         if (supportsBlur && (mSurface == null || !mSurface.isValid())) {
-            return;
+            return false;
         }
-        mDepth = depthF;
         ensureDependencies();
         IBinder windowToken = mLauncher.getRootView().getWindowToken();
         if (windowToken != null) {
-            mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
+            mWallpaperManager.setWallpaperZoomOut(windowToken, depth);
         }
 
         if (supportsBlur) {
-            boolean isOpaque = mLauncher.getScrimView().isFullyOpaque();
-            int blur = isOpaque ? 0 : (int) (mDepth * mMaxBlurRadius);
+            // We cannot mark the window as opaque in overview because there will be an app window
+            // below the launcher layer, and we need to draw it -- without blurs.
+            boolean isOverview = mLauncher.isInState(LauncherState.OVERVIEW);
+            boolean opaque = mLauncher.getScrimView().isFullyOpaque() && !isOverview;
+
+            int blur = opaque || isOverview ? 0 : (int) (depth * mMaxBlurRadius);
             new SurfaceControl.Transaction()
                     .setBackgroundBlurRadius(mSurface, blur)
-                    .setOpaque(mSurface, isOpaque)
+                    .setOpaque(mSurface, opaque)
                     .apply();
         }
+        return true;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
deleted file mode 100644
index 86ac39f..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 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.taskbar;
-
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
-
-import android.annotation.DrawableRes;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.android.launcher3.R;
-import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
-import com.android.launcher3.taskbar.contextual.RotationContextButton;
-
-/**
- * Creates Buttons for Taskbar for 3 button nav.
- * Can add animations and state management for buttons in this class as things progress.
- */
-public class ButtonProvider {
-
-    private final int mMarginLeftRight;
-    private final TaskbarActivityContext mContext;
-
-    public ButtonProvider(TaskbarActivityContext context) {
-        mContext = context;
-        mMarginLeftRight = context.getResources()
-                .getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
-    }
-
-    public View getBack() {
-        // Back button
-        return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK);
-    }
-
-    public View getDown() {
-        // Ime down button
-        return getButtonForDrawable(R.drawable.ic_sysbar_back, BUTTON_BACK);
-    }
-
-    public View getHome() {
-        // Home button
-        return getButtonForDrawable(R.drawable.ic_sysbar_home, BUTTON_HOME);
-    }
-
-    public View getRecents() {
-        // Recents button
-        return getButtonForDrawable(R.drawable.ic_sysbar_recent, BUTTON_RECENTS);
-    }
-
-    public View getImeSwitcher() {
-        // IME Switcher Button
-        return getButtonForDrawable(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH);
-    }
-
-    public RotationContextButton getContextualRotation() {
-        // Rotation suggestion button
-        return new RotationContextButton(mContext);
-    }
-
-    private View getButtonForDrawable(@DrawableRes int drawableId, @TaskbarButton int buttonType) {
-        ImageView buttonView = new ImageView(mContext);
-        buttonView.setImageResource(drawableId);
-        buttonView.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
-        buttonView.setPadding(mMarginLeftRight, 0, mMarginLeftRight, 0);
-        buttonView.setOnClickListener(view -> mContext.onNavigationButtonClick(buttonType));
-        return buttonView;
-    }
-
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
deleted file mode 100644
index d581302..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 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.taskbar;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.RelativeLayout;
-
-import com.android.launcher3.views.ActivityContext;
-
-public class ImeBarView extends RelativeLayout {
-
-    private View mImeView;
-
-    public ImeBarView(Context context) {
-        this(context, null);
-    }
-
-    public ImeBarView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public ImeBarView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public void init(ButtonProvider buttonProvider) {
-        // TODO (b/187966005), maybe need to replace ime switcher button with
-        //  RotationContextButton when device rotates
-        ActivityContext context = getActivityContext();
-        RelativeLayout.LayoutParams imeParams = new RelativeLayout.LayoutParams(
-                context.getDeviceProfile().iconSizePx,
-                context.getDeviceProfile().iconSizePx
-        );
-        RelativeLayout.LayoutParams downParams = new RelativeLayout.LayoutParams(imeParams);
-
-        imeParams.addRule(ALIGN_PARENT_END);
-        imeParams.setMarginEnd(context.getDeviceProfile().iconSizePx);
-        downParams.setMarginStart(context.getDeviceProfile().iconSizePx);
-        downParams.addRule(ALIGN_PARENT_START);
-
-        // Down Arrow
-        View downView = buttonProvider.getDown();
-        downView.setLayoutParams(downParams);
-        downView.setRotation(-90);
-        addView(downView);
-
-        // IME switcher button
-        mImeView = buttonProvider.getImeSwitcher();
-        mImeView.setLayoutParams(imeParams);
-        addView(mImeView);
-    }
-
-    public void setImeSwitcherVisibility(boolean show) {
-        mImeView.setVisibility(show ? VISIBLE : GONE);
-    }
-
-    private <T extends Context & ActivityContext> T getActivityContext() {
-        return ActivityContext.lookupContext(getContext());
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index f124de7..79af7cc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -15,42 +15,42 @@
  */
 package com.android.launcher3.taskbar;
 
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.graphics.Rect;
-import android.graphics.RectF;
 import android.view.MotionEvent;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.QuickstepTransitionManager;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.quickstep.AnimatedFloat;
+
 
 /**
  * A data source which integrates with a Launcher instance
- * TODO: Rename to have Launcher prefix
  */
-
 public class LauncherTaskbarUIController extends TaskbarUIController {
 
     private final BaseQuickstepLauncher mLauncher;
     private final TaskbarStateHandler mTaskbarStateHandler;
-    private final TaskbarAnimationController mTaskbarAnimationController;
     private final TaskbarHotseatController mHotseatController;
 
     private final TaskbarActivityContext mContext;
     final TaskbarDragLayer mTaskbarDragLayer;
     final TaskbarView mTaskbarView;
 
+    private AnimatedFloat mTaskBarAlpha;
+    private AlphaProperty mIconAlphaForHome;
     private @Nullable Animator mAnimator;
     private boolean mIsAnimatingToLauncher;
-    private ContextualRotationNotifier mContextualRotationNotifier;
 
     public LauncherTaskbarUIController(
             BaseQuickstepLauncher launcher, TaskbarActivityContext context) {
@@ -60,20 +60,19 @@
 
         mLauncher = launcher;
         mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
-        mTaskbarAnimationController = new TaskbarAnimationController(mLauncher,
-                createTaskbarAnimationControllerCallbacks());
         mHotseatController = new TaskbarHotseatController(
                 mLauncher, mTaskbarView::updateHotseatItems);
+
     }
 
     @Override
-    protected void onCreate(ContextualRotationNotifier notifier) {
-        mContextualRotationNotifier = notifier;
-        mTaskbarStateHandler.setAnimationController(mTaskbarAnimationController);
-        mTaskbarAnimationController.init();
+    protected void init(AnimatedFloat taskBarAlpha, AlphaProperty iconAlphaForLauncherState,
+            AlphaProperty iconAlphaForHome) {
+        mTaskBarAlpha = taskBarAlpha;
+        mIconAlphaForHome = iconAlphaForHome;
+        mTaskbarStateHandler.setAnimationController(iconAlphaForLauncherState);
         mHotseatController.init();
         setTaskbarViewVisible(!mLauncher.hasBeenResumed());
-        alignRealHotseatWithTaskbar();
         mLauncher.setTaskbarUIController(this);
     }
 
@@ -83,9 +82,7 @@
             // End this first, in case it relies on properties that are about to be cleaned up.
             mAnimator.end();
         }
-        mContextualRotationNotifier = null;
         mTaskbarStateHandler.setAnimationController(null);
-        mTaskbarAnimationController.cleanup();
         mHotseatController.cleanup();
         setTaskbarViewVisible(true);
         mLauncher.getHotseat().setIconsAlpha(1f);
@@ -97,46 +94,6 @@
         return !mIsAnimatingToLauncher;
     }
 
-    private TaskbarAnimationControllerCallbacks createTaskbarAnimationControllerCallbacks() {
-        return new TaskbarAnimationControllerCallbacks() {
-            @Override
-            public void updateTaskbarBackgroundAlpha(float alpha) {
-                mTaskbarDragLayer.setTaskbarBackgroundAlpha(alpha);
-            }
-
-            @Override
-            public void updateTaskbarVisibilityAlpha(float alpha) {
-                mTaskbarView.setAlpha(alpha);
-                if (mContextualRotationNotifier != null) {
-                    mContextualRotationNotifier.onTaskbarVisibilityChanged(alpha == 1);
-                }
-            }
-
-            @Override
-            public void updateImeBarVisibilityAlpha(float alpha) {
-                mTaskbarDragLayer.updateImeBarVisibilityAlpha(alpha);
-            }
-
-            @Override
-            public void updateTaskbarScale(float scale) {
-                mTaskbarView.setScaleX(scale);
-                mTaskbarView.setScaleY(scale);
-            }
-
-            @Override
-            public void updateTaskbarTranslationY(float translationY) {
-                if (translationY < 0) {
-                    // Resize to accommodate the max translation we'll reach.
-                    mContext.setTaskbarWindowHeight(mContext.getDeviceProfile().taskbarSize
-                            + mLauncher.getHotseat().getTaskbarOffsetY());
-                } else {
-                    mContext.setTaskbarWindowHeight(mContext.getDeviceProfile().taskbarSize);
-                }
-                mTaskbarView.setTranslationY(translationY);
-            }
-        };
-    }
-
     /**
      * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
      */
@@ -146,7 +103,7 @@
             mAnimator.cancel();
         }
         if (isResumed) {
-            mAnimator = createAnimToLauncher(null, duration);
+            mAnimator = createAnimToLauncher(mLauncher.getStateManager().getState(), duration);
         } else {
             mAnimator = createAnimToApp(duration);
         }
@@ -162,20 +119,19 @@
     /**
      * Create Taskbar animation when going from an app to Launcher.
      * @param toState If known, the state we will end up in when reaching Launcher.
+     * TODO: Move this and createAnimToApp to TaskbarStateHandler using the BACKGROUND state
      */
-    public Animator createAnimToLauncher(@Nullable LauncherState toState, long duration) {
+    public Animator createAnimToLauncher(@NonNull LauncherState toState, long duration) {
         PendingAnimation anim = new PendingAnimation(duration);
-        anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(0, duration));
-        if (toState != null) {
-            mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim);
-        }
+        mTaskbarStateHandler.setState(toState, anim);
+
+        anim.setFloat(mTaskBarAlpha, AnimatedFloat.VALUE, 0, LINEAR);
+        mTaskbarView.alignIconsWithLauncher(mLauncher.getDeviceProfile(), anim);
 
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
                 mIsAnimatingToLauncher = true;
-                mTaskbarView.setHolesAllowedInLayout(true);
-                mTaskbarView.updateHotseatItemsVisibility();
             }
 
             @Override
@@ -190,27 +146,17 @@
 
     private Animator createAnimToApp(long duration) {
         PendingAnimation anim = new PendingAnimation(duration);
-        anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(1, duration));
+        anim.setFloat(mTaskBarAlpha, AnimatedFloat.VALUE, 1, LINEAR);
+        anim.addListener(AnimatorListeners.forEndCallback(mTaskbarView.resetIconPosition(anim)));
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                mTaskbarView.updateHotseatItemsVisibility();
                 setTaskbarViewVisible(true);
             }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mTaskbarView.setHolesAllowedInLayout(false);
-            }
         });
         return anim.buildAnim();
     }
 
-    @Override
-    protected void onImeVisible(TaskbarDragLayer containerView, boolean isVisible) {
-        mTaskbarAnimationController.animateToVisibilityForIme(isVisible ? 0 : 1);
-    }
-
     /**
      * Should be called when one or more items in the Hotseat have changed.
      */
@@ -230,55 +176,8 @@
         return mContext.getDragController().isDragging();
     }
 
-    /**
-     * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
-     */
-    @Override
-    public void alignRealHotseatWithTaskbar() {
-        Rect hotseatBounds = new Rect();
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        int hotseatHeight = grid.workspacePadding.bottom + grid.taskbarSize;
-        int taskbarOffset = mLauncher.getHotseat().getTaskbarOffsetY();
-        int hotseatTopDiff = hotseatHeight - grid.taskbarSize - taskbarOffset;
-        int hotseatBottomDiff = taskbarOffset;
-
-        RectF hotseatBoundsF = mTaskbarView.getHotseatBounds();
-        Utilities.scaleRectFAboutPivot(hotseatBoundsF, getTaskbarScaleOnHome(),
-                mTaskbarView.getPivotX(), mTaskbarView.getPivotY());
-        hotseatBoundsF.round(hotseatBounds);
-        mLauncher.getHotseat().setPadding(hotseatBounds.left,
-                hotseatBounds.top + hotseatTopDiff,
-                mTaskbarView.getWidth() - hotseatBounds.right,
-                mTaskbarView.getHeight() - hotseatBounds.bottom + hotseatBottomDiff);
-    }
-
-    /**
-     * Returns the ratio of the taskbar icon size on home vs in an app.
-     */
-    public float getTaskbarScaleOnHome() {
-        DeviceProfile inAppDp = mContext.getDeviceProfile();
-        DeviceProfile onHomeDp = mLauncher.getDeviceProfile();
-        return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx;
-    }
-
     void setTaskbarViewVisible(boolean isVisible) {
-        mTaskbarView.setIconsVisibility(isVisible);
+        mIconAlphaForHome.setValue(isVisible ? 1 : 0);
         mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f);
     }
-
-    /**
-     * Contains methods that TaskbarAnimationController can call to interface with
-     * TaskbarController.
-     */
-    protected interface TaskbarAnimationControllerCallbacks {
-        void updateTaskbarBackgroundAlpha(float alpha);
-        void updateTaskbarVisibilityAlpha(float alpha);
-        void updateImeBarVisibilityAlpha(float alpha);
-        void updateTaskbarScale(float scale);
-        void updateTaskbarTranslationY(float translationY);
-    }
-
-    public interface ContextualRotationNotifier {
-        void onTaskbarVisibilityChanged(boolean showing);
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonUIController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonUIController.java
new file mode 100644
index 0000000..1281b2e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonUIController.java
@@ -0,0 +1,316 @@
+/*
+ * 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.taskbar;
+
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
+
+import android.animation.ObjectAnimator;
+import android.annotation.DrawableRes;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Region.Op;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.util.Property;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnHoverListener;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
+import com.android.launcher3.taskbar.contextual.RotationButton;
+import com.android.launcher3.taskbar.contextual.RotationButtonController;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.quickstep.AnimatedFloat;
+
+import java.util.ArrayList;
+import java.util.function.IntPredicate;
+
+/**
+ * Controller for managing nav bar buttons in taskbar
+ */
+public class NavbarButtonUIController {
+
+    private final Rect mTempRect = new Rect();
+
+    private static final int FLAG_SWITCHER_SUPPORTED = 1 << 0;
+    private static final int FLAG_IME_VISIBLE = 1 << 1;
+    private static final int FLAG_ROTATION_BUTTON_VISIBLE = 1 << 2;
+
+    private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
+
+    private final ArrayList<StatePropertyHolder> mPropertyHolders = new ArrayList<>();
+    private final ArrayList<View> mAllButtons = new ArrayList<>();
+    private int mState;
+
+    private final TaskbarActivityContext mContext;
+
+    public NavbarButtonUIController(TaskbarActivityContext context) {
+        mContext = context;
+    }
+
+    /**
+     * Initializes the controller
+     */
+    public void init(TaskbarDragLayer dragLayer,
+            TaskbarNavButtonController navButtonController,
+            RotationButtonController rotationButtonController,
+            AnimatedFloat taskbarBackgroundAlpha, AlphaProperty taskbarIconAlpha) {
+        FrameLayout buttonController = dragLayer.findViewById(R.id.navbuttons_view);
+        buttonController.getLayoutParams().height = mContext.getDeviceProfile().taskbarSize;
+
+        if (mContext.canShowNavButtons()) {
+            ViewGroup startContainer = buttonController.findViewById(R.id.start_nav_buttons);
+            ViewGroup endContainer = buttonController.findViewById(R.id.end_nav_buttons);
+
+            initButtons(startContainer, endContainer, navButtonController);
+
+            // Animate taskbar background when IME shows
+            mPropertyHolders.add(new StatePropertyHolder(taskbarBackgroundAlpha,
+                    flags -> (flags & FLAG_IME_VISIBLE) == 0,
+                    AnimatedFloat.VALUE, 0, 1));
+            mPropertyHolders.add(new StatePropertyHolder(
+                    taskbarIconAlpha, flags -> (flags & FLAG_IME_VISIBLE) == 0,
+                    MultiValueAlpha.VALUE, 1, 0));
+
+            // Rotation button
+            RotationButton rotationButton = new RotationButtonImpl(addButton(endContainer));
+            rotationButton.hide();
+            rotationButtonController.setRotationButton(rotationButton);
+        } else {
+            rotationButtonController.setRotationButton(new RotationButton() { });
+        }
+
+        applyState();
+        mPropertyHolders.forEach(StatePropertyHolder::endAnimation);
+    }
+
+    private void initButtons(ViewGroup startContainer, ViewGroup endContainer,
+            TaskbarNavButtonController navButtonController) {
+
+        View backButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
+                startContainer, navButtonController);
+        // Rotate when Ime visible
+        mPropertyHolders.add(new StatePropertyHolder(backButton,
+                flags -> (flags & FLAG_IME_VISIBLE) == 0, View.ROTATION, 0,
+                Utilities.isRtl(mContext.getResources()) ? 90 : -90));
+
+        // home and recents buttons
+        View homeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, startContainer,
+                navButtonController);
+        mPropertyHolders.add(new StatePropertyHolder(homeButton,
+                flags -> (flags & FLAG_IME_VISIBLE) == 0));
+        View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
+                startContainer, navButtonController);
+        mPropertyHolders.add(new StatePropertyHolder(recentsButton,
+                flags -> (flags & FLAG_IME_VISIBLE) == 0));
+
+        // IME switcher
+        View imeSwitcherButton = addButton(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH,
+                endContainer, navButtonController);
+        mPropertyHolders.add(new StatePropertyHolder(imeSwitcherButton,
+                flags -> ((flags & MASK_IME_SWITCHER_VISIBLE) == MASK_IME_SWITCHER_VISIBLE)
+                        && ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0)));
+    }
+
+    /**
+     * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
+     */
+    public void setImeIsVisible(boolean isImeVisible) {
+        if (isImeVisible) {
+            mState |= FLAG_IME_VISIBLE;
+        } else {
+            mState &= ~FLAG_IME_VISIBLE;
+        }
+        applyState();
+    }
+
+    /**
+     * Returns true if IME bar is visible
+     */
+    public boolean isImeVisible() {
+        return (mState & FLAG_IME_VISIBLE) != 0;
+    }
+
+    /**
+     * Adds the bounds corresponding to all visible buttons to provided region
+     */
+    public void addVisibleButtonsRegion(TaskbarDragLayer parent, Region outRegion) {
+        int count = mAllButtons.size();
+        for (int i = 0; i < count; i++) {
+            View button = mAllButtons.get(i);
+            if (button.getVisibility() == View.VISIBLE) {
+                parent.getDescendantRectRelativeToSelf(button, mTempRect);
+                outRegion.op(mTempRect, Op.UNION);
+            }
+        }
+    }
+
+    /**
+     * Sets if ime switcher is visible or not when ime is visible
+     */
+    public void setImeSwitcherVisible(boolean imeSwitcherVisible) {
+        if (imeSwitcherVisible) {
+            mState |= FLAG_SWITCHER_SUPPORTED;
+        } else {
+            mState &= ~FLAG_SWITCHER_SUPPORTED;
+        }
+        applyState();
+    }
+
+    private void applyState() {
+        int count = mPropertyHolders.size();
+        for (int i = 0; i < count; i++) {
+            mPropertyHolders.get(i).setState(mState);
+        }
+    }
+
+    private ImageView addButton(@DrawableRes int drawableId, @TaskbarButton int buttonType,
+            ViewGroup parent, TaskbarNavButtonController navButtonController) {
+        ImageView buttonView = addButton(parent);
+        buttonView.setImageResource(drawableId);
+        buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType));
+        return buttonView;
+    }
+
+    private ImageView addButton(ViewGroup parent) {
+        ImageView buttonView = (ImageView) mContext.getLayoutInflater()
+                .inflate(R.layout.taskbar_nav_button, parent, false);
+        parent.addView(buttonView);
+        mAllButtons.add(buttonView);
+        return buttonView;
+    }
+
+    private class RotationButtonImpl implements RotationButton {
+
+        private final ImageView mButton;
+        private AnimatedVectorDrawable mImageDrawable;
+
+        RotationButtonImpl(ImageView button) {
+            mButton = button;
+        }
+
+        @Override
+        public void setRotationButtonController(RotationButtonController rotationButtonController) {
+            // TODO(b/187754252) UI polish, different icons based on light/dark context, etc
+            mImageDrawable = (AnimatedVectorDrawable) mButton.getContext()
+                    .getDrawable(rotationButtonController.getIconResId());
+            mButton.setImageDrawable(mImageDrawable);
+            mImageDrawable.setCallback(mButton);
+        }
+
+        @Override
+        public View getCurrentView() {
+            return mButton;
+        }
+
+        @Override
+        public void show() {
+            mButton.setVisibility(View.VISIBLE);
+            mState |= FLAG_ROTATION_BUTTON_VISIBLE;
+            applyState();
+        }
+
+        @Override
+        public void hide() {
+            mButton.setVisibility(View.GONE);
+            mState &= ~FLAG_ROTATION_BUTTON_VISIBLE;
+            applyState();
+        }
+
+        @Override
+        public boolean isVisible() {
+            return mButton.getVisibility() == View.VISIBLE;
+        }
+
+        @Override
+        public void updateIcon(int lightIconColor, int darkIconColor) {
+            // TODO(b/187754252): UI Polish
+        }
+
+        @Override
+        public void setOnClickListener(OnClickListener onClickListener) {
+            mButton.setOnClickListener(onClickListener);
+        }
+
+        @Override
+        public void setOnHoverListener(OnHoverListener onHoverListener) {
+            mButton.setOnHoverListener(onHoverListener);
+        }
+
+        @Override
+        public AnimatedVectorDrawable getImageDrawable() {
+            return mImageDrawable;
+        }
+
+        @Override
+        public void setDarkIntensity(float darkIntensity) {
+            // TODO(b/187754252) UI polish
+        }
+
+        @Override
+        public boolean acceptRotationProposal() {
+            return mButton.isAttachedToWindow();
+        }
+    }
+
+    private static class StatePropertyHolder {
+
+        private final float mEnabledValue, mDisabledValue;
+        private final ObjectAnimator mAnimator;
+        private final IntPredicate mEnableCondition;
+
+        private boolean mIsEnabled = true;
+
+        StatePropertyHolder(View view, IntPredicate enableCondition) {
+            this(view, enableCondition, LauncherAnimUtils.VIEW_ALPHA, 1, 0);
+            mAnimator.addListener(new AlphaUpdateListener(view));
+        }
+
+        <T> StatePropertyHolder(T target, IntPredicate enabledCondition,
+                Property<T, Float> property, float enabledValue, float disabledValue) {
+            mEnableCondition = enabledCondition;
+            mEnabledValue = enabledValue;
+            mDisabledValue = disabledValue;
+            mAnimator = ObjectAnimator.ofFloat(target, property, enabledValue, disabledValue);
+        }
+
+        public void setState(int flags) {
+            boolean isEnabled = mEnableCondition.test(flags);
+            if (mIsEnabled != isEnabled) {
+                mIsEnabled = isEnabled;
+                mAnimator.cancel();
+                mAnimator.setFloatValues(mIsEnabled ? mEnabledValue : mDisabledValue);
+                mAnimator.start();
+            }
+        }
+
+        public void endAnimation() {
+            if (mAnimator.isRunning()) {
+                mAnimator.end();
+            }
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index d51506c..5f7dce5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -29,6 +29,7 @@
 import android.content.pm.LauncherApps;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.inputmethodservice.InputMethodService;
 import android.os.Process;
 import android.os.SystemProperties;
 import android.util.Log;
@@ -53,10 +54,12 @@
 import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
 import com.android.launcher3.taskbar.contextual.RotationButtonController;
 import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.views.ActivityContext;
+import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.systemui.shared.recents.model.Task;
@@ -76,11 +79,16 @@
 
     private static final String WINDOW_TITLE = "Taskbar";
 
+    private static final int ALPHA_INDEX_HOME = 0;
+    private static final int ALPHA_INDEX_LAUNCHER_STATE = 1;
+    private static final int ALPHA_INDEX_IME = 2;
+
     private final DeviceProfile mDeviceProfile;
     private final LayoutInflater mLayoutInflater;
     private final TaskbarDragLayer mDragLayer;
     private final TaskbarIconController mIconController;
     private final TaskbarDragController mDragController;
+    private final NavbarButtonUIController mNavbarButtonUIController;
 
     private final WindowManager mWindowManager;
     private WindowManager.LayoutParams mWindowLayoutParams;
@@ -89,7 +97,6 @@
     private int mLastRequestedNonFullscreenHeight;
 
     private final SysUINavigationMode.Mode mNavMode;
-    private final SystemTaskbarNotificationManager mSystemTaskbarNotificationManager;
     private final TaskbarNavButtonController mNavButtonController;
     private final RotationButtonController mRotationButtonController;
 
@@ -101,43 +108,18 @@
     private final View.OnClickListener mOnTaskbarIconClickListener;
     private final View.OnLongClickListener mOnTaskbarIconLongClickListener;
 
-    private final TaskbarManager.SystemTaskbarNotifier mSystemTaskbarNotifier =
-            new TaskbarManager.SystemTaskbarNotifier() {
-                @Override
-                public void updateImeStatus(int displayId, int vis, int backDisposition,
-                        boolean showImeSwitcher) {
-                    /*
-                     * When in 3 button nav, sysui flags don't get called since we prevent
-                     *  sysui nav bar from instantiating at all, which is what's responsible for
-                     * sending sysui state flags over.
-                     */
-                    mIconController.updateImeStatus(displayId, vis, showImeSwitcher);
-                }
+    // Alpha property for task bar
+    private final AnimatedFloat mBgTaskbar = new AnimatedFloat(this::updateBackgroundAlpha);
+    private final AnimatedFloat mBgNavbar = new AnimatedFloat(this::updateBackgroundAlpha);
 
-                @Override
-                public void onRotationProposal(int rotation, boolean isValid) {
-                    mRotationButtonController.onRotationProposal(rotation, isValid);
-                }
-
-                @Override
-                public void disable(int displayId, int state1, int state2, boolean animate) {
-                    mRotationButtonController.onDisable2FlagChanged(state2);
-                }
-
-                @Override
-                public void onSystemBarAttributesChanged(int displayId, int behavior) {
-                    mRotationButtonController.onBehaviorChanged(displayId, behavior);
-                }
-            };
+    private final MultiValueAlpha mTaskbarIconAlpha;
 
     public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
-            TaskbarNavButtonController buttonController,
-            SystemTaskbarNotificationManager systemTaskbarNotificationManager) {
+            TaskbarNavButtonController buttonController) {
         super(windowContext, Themes.getActivityThemeRes(windowContext));
         mDeviceProfile = dp;
         mNavButtonController = buttonController;
         mNavMode = SysUINavigationMode.getMode(windowContext);
-        mSystemTaskbarNotificationManager = systemTaskbarNotificationManager;
         mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
                 () -> getPackageManager().isSafeMode());
 
@@ -146,23 +128,26 @@
         mOnTaskbarIconClickListener = this::onTaskbarIconClicked;
 
         float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size);
+        mDeviceProfile.updateIconSize(1, getResources());
         float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
         mDeviceProfile.updateIconSize(iconScale, getResources());
 
         mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
         mDragLayer = (TaskbarDragLayer) mLayoutInflater
                 .inflate(R.layout.taskbar, null, false);
-
         mRotationButtonController = new RotationButtonController(this,
                 R.color.popup_color_primary_light, R.color.popup_color_primary_light);
-        mIconController = new TaskbarIconController(this, mDragLayer,
-                mRotationButtonController);
+        mNavbarButtonUIController = new NavbarButtonUIController(this);
+        mIconController = new TaskbarIconController(this, mDragLayer, mNavbarButtonUIController);
 
         Display display = windowContext.getDisplay();
         Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY
                 ? windowContext.getApplicationContext()
                 : windowContext.getApplicationContext().createDisplayContext(display);
         mWindowManager = c.getSystemService(WindowManager.class);
+
+        mTaskbarIconAlpha = new MultiValueAlpha(mDragLayer.findViewById(R.id.taskbar_view), 3);
+        mTaskbarIconAlpha.setUpdateVisibility(true);
     }
 
     public void init() {
@@ -187,12 +172,12 @@
                 new int[] { ITYPE_EXTRA_NAVIGATION_BAR, ITYPE_BOTTOM_TAPPABLE_ELEMENT }
         );
 
-        mIconController.init(mOnTaskbarIconClickListener, mOnTaskbarIconLongClickListener,
-                mNavMode);
+        mIconController.init(mOnTaskbarIconClickListener, mOnTaskbarIconLongClickListener);
+        mNavbarButtonUIController.init(mDragLayer, mNavButtonController, mRotationButtonController,
+                mBgNavbar, mTaskbarIconAlpha.getProperty(ALPHA_INDEX_IME));
         mWindowManager.addView(mDragLayer, mWindowLayoutParams);
-        if (mNavMode == Mode.THREE_BUTTONS) {
-            mSystemTaskbarNotificationManager
-                    .registerSystemTaskbarNotifications(mSystemTaskbarNotifier);
+        if (canShowNavButtons()) {
+            mRotationButtonController.init();
         }
     }
 
@@ -232,10 +217,8 @@
         mUIController.onDestroy();
         mUIController = uiController;
         mIconController.setUIController(mUIController);
-        mUIController.onCreate(mRotationButtonController::onTaskBarVisibilityChange);
-        if (mNavMode == Mode.THREE_BUTTONS) {
-            mRotationButtonController.init();
-        }
+        mUIController.init(mBgTaskbar, mTaskbarIconAlpha.getProperty(ALPHA_INDEX_LAUNCHER_STATE),
+                mTaskbarIconAlpha.getProperty(ALPHA_INDEX_HOME));
     }
 
     /**
@@ -244,12 +227,8 @@
     public void onDestroy() {
         setUIController(TaskbarUIController.DEFAULT);
         mIconController.onDestroy();
+        mRotationButtonController.onDestroy();
         mWindowManager.removeViewImmediate(mDragLayer);
-        if (mNavMode == Mode.THREE_BUTTONS) {
-            mSystemTaskbarNotificationManager.removeSystemTaskbarNotifications(
-                    mSystemTaskbarNotifier);
-            mRotationButtonController.cleanup();
-        }
     }
 
     void onNavigationButtonClick(@TaskbarButton int buttonType) {
@@ -261,6 +240,36 @@
      */
     public void setImeIsVisible(boolean isImeVisible) {
         mIconController.setImeIsVisible(isImeVisible);
+        mNavbarButtonUIController.setImeIsVisible(isImeVisible);
+    }
+
+    /**
+     * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from
+     * instantiating at all, which is what's responsible for sending sysui state flags over.
+     *
+     * @param vis IME visibility flag
+     */
+    public void updateImeStatus(int displayId, int vis, boolean showImeSwitcher) {
+        if (displayId != getDisplayId() || !canShowNavButtons()) {
+            return;
+        }
+        mNavbarButtonUIController.setImeSwitcherVisible(showImeSwitcher);
+        setImeIsVisible((vis & InputMethodService.IME_VISIBLE) != 0);
+    }
+
+    public void onRotationProposal(int rotation, boolean isValid) {
+        mRotationButtonController.onRotationProposal(rotation, isValid);
+    }
+
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != getDisplayId()) {
+            return;
+        }
+        mRotationButtonController.onDisable2FlagChanged(state2);
+    }
+
+    public void onSystemBarAttributesChanged(int displayId, int behavior) {
+        mRotationButtonController.onBehaviorChanged(displayId, behavior);
     }
 
     /**
@@ -316,7 +325,9 @@
             });
         } else if (tag instanceof WorkspaceItemInfo) {
             WorkspaceItemInfo info = (WorkspaceItemInfo) tag;
-            if (!(info.isDisabled() && ItemClickHandler.handleDisabledItemClicked(info, this))) {
+            if (info.isDisabled()) {
+                ItemClickHandler.handleDisabledItemClicked(info, this);
+            } else {
                 Intent intent = new Intent(info.getIntent())
                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 try {
@@ -352,4 +363,9 @@
 
         AbstractFloatingView.closeAllOpenViews(this);
     }
+
+    private void updateBackgroundAlpha() {
+        mDragLayer.setTaskbarBackgroundAlpha(Math.max(mBgNavbar.value, mBgTaskbar.value));
+    }
+
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
deleted file mode 100644
index e20ddf8..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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.taskbar;
-
-import static com.android.launcher3.LauncherState.TASKBAR;
-
-import android.animation.Animator;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.taskbar.LauncherTaskbarUIController.TaskbarAnimationControllerCallbacks;
-import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.SystemUiProxy;
-import com.android.systemui.shared.system.QuickStepContract;
-
-/**
- * Works with TaskbarController to update the TaskbarView's visual properties based on factors such
- * as LauncherState, whether Launcher is in the foreground, etc.
- */
-public class TaskbarAnimationController {
-
-    private static final long IME_VISIBILITY_ALPHA_DURATION = 120;
-
-    private final BaseQuickstepLauncher mLauncher;
-    private final TaskbarAnimationControllerCallbacks mTaskbarCallbacks;
-
-    // Background alpha.
-    private final AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat(
-            this::onTaskbarBackgroundAlphaChanged);
-
-    // Overall visibility.
-    private final AnimatedFloat mTaskbarVisibilityAlphaForLauncherState = new AnimatedFloat(
-            this::updateVisibilityAlpha);
-    private final AnimatedFloat mTaskbarVisibilityAlphaForIme = new AnimatedFloat(
-            this::updateVisibilityAlphaForIme);
-
-    // Scale.
-    private final AnimatedFloat mTaskbarScaleForLauncherState = new AnimatedFloat(
-            this::updateScale);
-
-    // TranslationY.
-    private final AnimatedFloat mTaskbarTranslationYForLauncherState = new AnimatedFloat(
-            this::updateTranslationY);
-
-    public TaskbarAnimationController(BaseQuickstepLauncher launcher,
-            TaskbarAnimationControllerCallbacks taskbarCallbacks) {
-        mLauncher = launcher;
-        mTaskbarCallbacks = taskbarCallbacks;
-    }
-
-    protected void init() {
-        mTaskbarBackgroundAlpha.updateValue(mLauncher.hasBeenResumed() ? 0f : 1f);
-        boolean isVisibleForLauncherState = (mLauncher.getStateManager().getState()
-                .getVisibleElements(mLauncher) & TASKBAR) != 0;
-        mTaskbarVisibilityAlphaForLauncherState.updateValue(isVisibleForLauncherState ? 1f : 0f);
-        boolean isImeVisible = (SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags()
-                & QuickStepContract.SYSUI_STATE_IME_SHOWING) != 0;
-        mTaskbarVisibilityAlphaForIme.updateValue(isImeVisible ? 0f : 1f);
-
-        onTaskbarBackgroundAlphaChanged();
-        updateVisibilityAlpha();
-    }
-
-    protected void cleanup() {
-        setNavBarButtonAlpha(1f);
-    }
-
-    protected AnimatedFloat getTaskbarVisibilityForLauncherState() {
-        return mTaskbarVisibilityAlphaForLauncherState;
-    }
-
-    protected AnimatedFloat getTaskbarScaleForLauncherState() {
-        return mTaskbarScaleForLauncherState;
-    }
-
-    protected AnimatedFloat getTaskbarTranslationYForLauncherState() {
-        return mTaskbarTranslationYForLauncherState;
-    }
-
-    protected Animator createAnimToBackgroundAlpha(float toAlpha, long duration) {
-        return mTaskbarBackgroundAlpha.animateToValue(mTaskbarBackgroundAlpha.value, toAlpha)
-                .setDuration(duration);
-    }
-
-    protected void animateToVisibilityForIme(float toAlpha) {
-        mTaskbarVisibilityAlphaForIme.animateToValue(mTaskbarVisibilityAlphaForIme.value, toAlpha)
-                .setDuration(IME_VISIBILITY_ALPHA_DURATION).start();
-    }
-
-    private void onTaskbarBackgroundAlphaChanged() {
-        mTaskbarCallbacks.updateTaskbarBackgroundAlpha(mTaskbarBackgroundAlpha.value);
-        updateVisibilityAlpha();
-        updateScale();
-        updateTranslationY();
-    }
-
-    private void updateVisibilityAlpha() {
-        // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
-        // assumption being that Taskbar should always be visible regardless of the current
-        // LauncherState if Launcher is paused.
-        float alphaDueToIme = mTaskbarVisibilityAlphaForIme.value;
-        float alphaDueToLauncher = Math.max(mTaskbarBackgroundAlpha.value,
-                mTaskbarVisibilityAlphaForLauncherState.value);
-        float taskbarAlpha = alphaDueToLauncher * alphaDueToIme;
-        mTaskbarCallbacks.updateTaskbarVisibilityAlpha(taskbarAlpha);
-
-        // Make the nav bar invisible if taskbar is visible.
-        setNavBarButtonAlpha(1f - taskbarAlpha);
-    }
-
-    private void updateVisibilityAlphaForIme() {
-        updateVisibilityAlpha();
-        float taskbarAlphaDueToIme = mTaskbarVisibilityAlphaForIme.value;
-        mTaskbarCallbacks.updateImeBarVisibilityAlpha(1f - taskbarAlphaDueToIme);
-    }
-
-    private void updateScale() {
-        // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
-        // assumption being that Taskbar should always be at scale 1f regardless of the current
-        // LauncherState if Launcher is paused.
-        float scale = mTaskbarScaleForLauncherState.value;
-        scale = Utilities.mapRange(mTaskbarBackgroundAlpha.value, scale, 1f);
-        mTaskbarCallbacks.updateTaskbarScale(scale);
-    }
-
-    private void updateTranslationY() {
-        // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
-        // assumption being that Taskbar should always be at translationY 0f regardless of the
-        // current LauncherState if Launcher is paused.
-        float translationY = mTaskbarTranslationYForLauncherState.value;
-        translationY = Utilities.mapRange(mTaskbarBackgroundAlpha.value, translationY, 0f);
-        mTaskbarCallbacks.updateTaskbarTranslationY(translationY);
-    }
-
-    private void setNavBarButtonAlpha(float navBarAlpha) {
-        SystemUiProxy.INSTANCE.get(mLauncher).setNavBarButtonAlpha(navBarAlpha, false);
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 855c507..4294eb5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -15,9 +15,6 @@
  */
 package com.android.launcher3.taskbar;
 
-import static android.view.View.INVISIBLE;
-import static android.view.View.VISIBLE;
-
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Intent;
@@ -89,7 +86,7 @@
         mActivity.setTaskbarWindowFullscreen(true);
         view.post(() -> {
             startInternalDrag(btv);
-            btv.setVisibility(INVISIBLE);
+            btv.setAlpha(0);
         });
         return true;
     }
@@ -294,16 +291,9 @@
         return super.isDragging() || mIsSystemDragInProgress;
     }
 
-    /**
-     * Whether we started dragging the given view and the drag is still in progress.
-     */
-    public boolean isDraggingView(View child) {
-        return isDragging() && mDragObject != null && mDragObject.originalView == child;
-    }
-
     private void maybeOnDragEnd() {
         if (!isDragging()) {
-            ((View) mDragObject.originalView).setVisibility(VISIBLE);
+            ((View) mDragObject.originalView).setAlpha(1);
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 2469f95..52a2c86 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -64,6 +64,7 @@
         mFolderMargin = getResources().getDimensionPixelSize(R.dimen.taskbar_folder_margin);
         mTaskbarBackgroundPaint = new Paint();
         mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background));
+        mTaskbarBackgroundPaint.setAlpha(0);
         recreateControllers();
     }
 
@@ -109,12 +110,6 @@
         return true;
     }
 
-    public void updateImeBarVisibilityAlpha(float alpha) {
-        if (mControllerCallbacks != null) {
-            mControllerCallbacks.updateImeBarVisibilityAlpha(alpha);
-        }
-    }
-
     @Override
     public void onViewRemoved(View child) {
         super.onViewRemoved(child);
@@ -139,7 +134,6 @@
         return boundingBox;
     }
 
-
     /**
      * Sets the alpha of the background color behind all the Taskbar contents.
      * @param alpha 0 is fully transparent, 1 is fully opaque.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java
index 549e26c..7dca19c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java
@@ -15,24 +15,18 @@
  */
 package com.android.launcher3.taskbar;
 
-import static android.view.View.GONE;
-import static android.view.View.VISIBLE;
-
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
 import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
 
-import android.graphics.Rect;
-import android.inputmethodservice.InputMethodService;
-import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
 
 import androidx.annotation.NonNull;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AlphaUpdateListener;
-import com.android.launcher3.taskbar.contextual.RotationButtonController;
-import com.android.quickstep.SysUINavigationMode;
 import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
 
 /**
@@ -40,42 +34,28 @@
  */
 public class TaskbarIconController {
 
-    private final Rect mTempRect = new Rect();
-
     private final TaskbarActivityContext mActivity;
     private final TaskbarDragLayer mDragLayer;
+    private final NavbarButtonUIController mNavbarButtonUIController;
 
     private final TaskbarView mTaskbarView;
-    private final ImeBarView mImeBarView;
-    private final RotationButtonController mRotationButtonController;
 
     @NonNull
     private TaskbarUIController mUIController = TaskbarUIController.DEFAULT;
 
     TaskbarIconController(TaskbarActivityContext activity, TaskbarDragLayer dragLayer,
-            RotationButtonController rotationButtonController) {
+            NavbarButtonUIController navbarButtonUIController) {
         mActivity = activity;
         mDragLayer = dragLayer;
+        mNavbarButtonUIController = navbarButtonUIController;
         mTaskbarView = mDragLayer.findViewById(R.id.taskbar_view);
-        mImeBarView = mDragLayer.findViewById(R.id.ime_bar_view);
-        mRotationButtonController = rotationButtonController;
     }
 
-    public void init(OnClickListener clickListener, OnLongClickListener longClickListener,
-            SysUINavigationMode.Mode navMode) {
-        mDragLayer.addOnLayoutChangeListener((v, a, b, c, d, e, f, g, h) ->
-                mUIController.alignRealHotseatWithTaskbar());
-
-        ButtonProvider buttonProvider = new ButtonProvider(mActivity);
-        mImeBarView.init(buttonProvider);
-        mTaskbarView.init(new TaskbarViewCallbacks(), clickListener, longClickListener,
-                buttonProvider);
+    public void init(OnClickListener clickListener, OnLongClickListener longClickListener) {
+        mTaskbarView.init(clickListener, longClickListener);
         mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
 
         mDragLayer.init(new TaskbarDragLayerCallbacks(), mTaskbarView);
-        if (mActivity.canShowNavButtons()) {
-            mRotationButtonController.setRotationButton(mTaskbarView.getContextualRotationButton());
-        }
     }
 
     public void onDestroy() {
@@ -87,26 +67,10 @@
     }
 
     /**
-     * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from
-     * instantiating at all, which is what's responsible for sending sysui state flags over.
-     *
-     * @param vis IME visibility flag
-     */
-    public void updateImeStatus(int displayId, int vis, boolean showImeSwitcher) {
-        if (displayId != mActivity.getDisplayId() || !mActivity.canShowNavButtons()) {
-            return;
-        }
-
-        mImeBarView.setImeSwitcherVisibility(showImeSwitcher);
-        setImeIsVisible((vis & InputMethodService.IME_VISIBLE) != 0);
-    }
-
-    /**
      * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
      */
     public void setImeIsVisible(boolean isImeVisible) {
         mTaskbarView.setTouchesEnabled(!isImeVisible);
-        mUIController.onImeVisible(mDragLayer, isImeVisible);
     }
 
     /**
@@ -122,7 +86,7 @@
             if (mDragLayer.getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
                 // Let touches pass through us.
                 insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
-            } else if (mImeBarView.getVisibility() == VISIBLE) {
+            } else if (mNavbarButtonUIController.isImeVisible()) {
                 insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
             } else if (!mUIController.isTaskbarTouchable()) {
                 // Let touches pass through us.
@@ -131,17 +95,8 @@
                 // Buttons are visible, take over the full taskbar area
                 insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
             } else {
-                if (mTaskbarView.mSystemButtonContainer.getVisibility() == VISIBLE) {
-                    mDragLayer.getDescendantRectRelativeToSelf(
-                            mTaskbarView.mSystemButtonContainer, mTempRect);
-                    insetsInfo.touchableRegion.set(mTempRect);
-                }
-                if (mTaskbarView.mContextualButtonContainer.getVisibility() == VISIBLE) {
-                    mDragLayer.getDescendantRectRelativeToSelf(
-                            mTaskbarView.mContextualButtonContainer, mTempRect);
-                    insetsInfo.touchableRegion.union(mTempRect);
-                }
-
+                mNavbarButtonUIController.addVisibleButtonsRegion(
+                        mDragLayer, insetsInfo.touchableRegion);
                 insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
             }
 
@@ -156,36 +111,9 @@
         }
 
         public void onDragLayerViewRemoved() {
-            int count = mDragLayer.getChildCount();
-            // Ensure no other children present (like Folders, etc)
-            for (int i = 0; i < count; i++) {
-                View v = mDragLayer.getChildAt(i);
-                if (!((v instanceof TaskbarView) || (v instanceof ImeBarView))) {
-                    return;
-                }
+            if (AbstractFloatingView.getOpenView(mActivity, TYPE_ALL) == null) {
+                mActivity.setTaskbarWindowFullscreen(false);
             }
-            mActivity.setTaskbarWindowFullscreen(false);
-        }
-
-        public void updateImeBarVisibilityAlpha(float alpha) {
-            if (!mActivity.canShowNavButtons()) {
-                // TODO Remove sysui IME bar for gesture nav as well
-                return;
-            }
-            mImeBarView.setAlpha(alpha);
-            mImeBarView.setVisibility(alpha == 0 ? GONE : VISIBLE);
-        }
-    }
-
-    /**
-     * Callbacks for {@link TaskbarView} to interact with the icon controller
-     */
-    public class TaskbarViewCallbacks {
-        /**
-         * Returns whether no other controller is currently handling the given View's visibility.
-         */
-        public boolean canUpdateViewVisibility(View child) {
-            return !mActivity.getDragController().isDraggingView(child);
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index b9acee8..36bccee 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -40,14 +40,11 @@
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.TouchInteractionService;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Class to manage taskbar lifecycle
  */
 public class TaskbarManager implements DisplayController.DisplayInfoChangeListener,
-        SysUINavigationMode.NavigationModeChangeListener, SystemTaskbarNotificationManager {
+        SysUINavigationMode.NavigationModeChangeListener {
 
     private final Context mContext;
     private final DisplayController mDisplayController;
@@ -62,8 +59,6 @@
 
     private boolean mUserUnlocked = false;
 
-    private List<SystemTaskbarNotifier> mSystemTaskbarNotifiers = new ArrayList<>();
-
     public TaskbarManager(TouchInteractionService service) {
         mDisplayController = DisplayController.INSTANCE.get(service);
         mSysUINavigationMode = SysUINavigationMode.INSTANCE.get(service);
@@ -129,7 +124,7 @@
             return;
         }
         mTaskbarActivityContext = new TaskbarActivityContext(
-                mContext, dp.copy(mContext), mNavButtonController, this);
+                mContext, dp.copy(mContext), mNavButtonController);
         mTaskbarActivityContext.init();
         if (mLauncher != null) {
             mTaskbarActivityContext.setUIController(
@@ -137,9 +132,6 @@
         }
     }
 
-    // TODO - I don't think this is the best place for these pass through methods,
-    //  maybe directly in TaskbarIconController?
-
     /**
      * See {@link com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags}
      * @param systemUiStateFlags The latest SystemUiStateFlags
@@ -151,16 +143,6 @@
         }
     }
 
-    public void registerSystemTaskbarNotifications(SystemTaskbarNotifier notifier) {
-        if (!mSystemTaskbarNotifiers.contains(notifier)) {
-            mSystemTaskbarNotifiers.add(notifier);
-        }
-    }
-
-    public void removeSystemTaskbarNotifications(SystemTaskbarNotifier notifier) {
-        mSystemTaskbarNotifiers.remove(notifier);
-    }
-
     /**
      * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from
      * instantiating at all, which is what's responsible for sending sysui state flags over.
@@ -171,26 +153,26 @@
      */
     public void updateImeStatus(int displayId, int vis, int backDisposition,
             boolean showImeSwitcher) {
-        for (SystemTaskbarNotifier notifier : mSystemTaskbarNotifiers) {
-            notifier.updateImeStatus(displayId, vis, backDisposition, showImeSwitcher);
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.updateImeStatus(displayId, vis, showImeSwitcher);
         }
     }
 
     public void onRotationProposal(int rotation, boolean isValid) {
-        for (SystemTaskbarNotifier notifier : mSystemTaskbarNotifiers) {
-            notifier.onRotationProposal(rotation, isValid);
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.onRotationProposal(rotation, isValid);
         }
     }
 
     public void disable(int displayId, int state1, int state2, boolean animate) {
-        for (SystemTaskbarNotifier notifier : mSystemTaskbarNotifiers) {
-            notifier.disable(displayId, state1, state2, animate);
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.disable(displayId, state1, state2, animate);
         }
     }
 
     public void onSystemBarAttributesChanged(int displayId, int behavior) {
-        for (SystemTaskbarNotifier notifier : mSystemTaskbarNotifiers) {
-            notifier.onSystemBarAttributesChanged(displayId, behavior);
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.onSystemBarAttributesChanged(displayId, behavior);
         }
     }
 
@@ -202,18 +184,4 @@
         mDisplayController.removeChangeListener(this);
         mSysUINavigationMode.removeModeChangeListener(this);
     }
-
-    public interface SystemTaskbarNotifier {
-        void updateImeStatus(int displayId, int vis, int backDisposition,
-                boolean showImeSwitcher);
-        void onRotationProposal(int rotation, boolean isValid);
-        void disable(int displayId, int state1, int state2, boolean animate);
-        void onSystemBarAttributesChanged(int displayId, int behavior);
-
-    }
 }
-
-interface SystemTaskbarNotificationManager {
-    void registerSystemTaskbarNotifications(TaskbarManager.SystemTaskbarNotifier notifier);
-    void removeSystemTaskbarNotifications(TaskbarManager.SystemTaskbarNotifier notifier);
-}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
index a701aae..20d4133 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.LauncherState.TASKBAR;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 
 import androidx.annotation.Nullable;
@@ -27,7 +26,9 @@
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SystemUiProxy;
 
 /**
  * StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar
@@ -37,15 +38,20 @@
 
     private final BaseQuickstepLauncher mLauncher;
 
-    // Contains Taskbar-related methods and fields we should aniamte. If null, don't do anything.
-    private @Nullable TaskbarAnimationController mAnimationController = null;
+    // Contains Taskbar-related properties we should aniamte. If null, don't do anything.
+    private @Nullable MultiValueAlpha.AlphaProperty mTaskbarAlpha = null;
+
+    private AnimatedFloat mNavbarButtonAlpha = new AnimatedFloat(this::updateNavbarButtonAlpha);
 
     public TaskbarStateHandler(BaseQuickstepLauncher launcher) {
         mLauncher = launcher;
     }
 
-    public void setAnimationController(TaskbarAnimationController callbacks) {
-        mAnimationController = callbacks;
+    public void setAnimationController(MultiValueAlpha.AlphaProperty taskbarAlpha) {
+        mTaskbarAlpha = taskbarAlpha;
+        // Reapply state.
+        setState(mLauncher.getStateManager().getState());
+        updateNavbarButtonAlpha();
     }
 
     @Override
@@ -59,17 +65,24 @@
         setState(toState, animation);
     }
 
-    private void setState(LauncherState toState, PropertySetter setter) {
-        if (mAnimationController == null) {
+    /**
+     * Sets the provided state
+     */
+    public void setState(LauncherState toState, PropertySetter setter) {
+        if (mTaskbarAlpha == null) {
             return;
         }
 
         boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
-        setter.setFloat(mAnimationController.getTaskbarVisibilityForLauncherState(),
-                AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR);
-        setter.setFloat(mAnimationController.getTaskbarScaleForLauncherState(),
-                AnimatedFloat.VALUE, toState.getTaskbarScale(mLauncher), LINEAR);
-        setter.setFloat(mAnimationController.getTaskbarTranslationYForLauncherState(),
-                AnimatedFloat.VALUE, toState.getTaskbarTranslationY(mLauncher), ACCEL_DEACCEL);
+        setter.setFloat(mTaskbarAlpha, MultiValueAlpha.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR);
+        // Make the nav bar visible in states that taskbar isn't visible.
+        // TODO: We should draw our own handle instead of showing the nav bar.
+        float navbarButtonAlpha = isTaskbarVisible ? 0f : 1f;
+        setter.setFloat(mNavbarButtonAlpha, AnimatedFloat.VALUE, navbarButtonAlpha, LINEAR);
+    }
+
+
+    private void updateNavbarButtonAlpha() {
+        SystemUiProxy.INSTANCE.get(mLauncher).setNavBarButtonAlpha(mNavbarButtonAlpha.value, false);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index f7a5618..34f66ba 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.taskbar;
 
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.quickstep.AnimatedFloat;
+
 /**
  * Base class for providing different taskbar UI
  */
@@ -22,20 +25,12 @@
 
     public static final TaskbarUIController DEFAULT = new TaskbarUIController();
 
-    /**
-     * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
-     */
-    public void alignRealHotseatWithTaskbar() { }
-
-    protected void onCreate(LauncherTaskbarUIController.ContextualRotationNotifier notifier) { }
+    protected void init(AnimatedFloat taskBarAlpha, AlphaProperty iconAlphaForLauncherState,
+            AlphaProperty iconAlphaForHome) { }
 
     protected void onDestroy() { }
 
     protected boolean isTaskbarTouchable() {
         return true;
     }
-
-    protected void onImeVisible(TaskbarDragLayer container, boolean isVisible) {
-        container.updateImeBarVisibilityAlpha(isVisible ? 1 : 0);
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 5c89f8c..d415502 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -15,77 +15,60 @@
  */
 package com.android.launcher3.taskbar;
 
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.util.AttributeSet;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
 
 import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.taskbar.contextual.RotationContextButton;
+import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.views.ActivityContext;
 
 /**
  * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
  */
-public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
+public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconParent, Insettable {
 
-    private final int mIconTouchSize;
-    private final boolean mIsRtl;
-    private final int mTouchSlop;
-    private final RectF mTempDelegateBounds = new RectF();
-    private final RectF mDelegateSlopBounds = new RectF();
     private final int[] mTempOutLocation = new int[2];
 
+    private final Rect mIconLayoutBounds = new Rect();
+    private final int mIconTouchSize;
     private final int mItemMarginLeftRight;
+    private final int mItemPadding;
 
     private final TaskbarActivityContext mActivityContext;
 
     // Initialized in init.
-    private TaskbarIconController.TaskbarViewCallbacks mControllerCallbacks;
     private View.OnClickListener mIconClickListener;
     private View.OnLongClickListener mIconLongClickListener;
 
-    LinearLayout mSystemButtonContainer;
-    LinearLayout mHotseatIconsContainer;
-    LinearLayout mContextualButtonContainer;
-
-    // Delegate touches to the closest view if within mIconTouchSize.
-    private boolean mDelegateTargeted;
-    private View mDelegateView;
     // Prevents dispatching touches to children if true
     private boolean mTouchEnabled = true;
 
     // Only non-null when the corresponding Folder is open.
     private @Nullable FolderIcon mLeaveBehindFolderIcon;
 
-    /** Provider of buttons added to taskbar in 3 button nav */
-    private ButtonProvider mButtonProvider;
-    private RotationContextButton mContextualRotationButton;
-
-    private boolean mDisableRelayout;
-    private boolean mAreHolesAllowed;
-
     public TaskbarView(@NonNull Context context) {
         this(context, null);
     }
@@ -106,61 +89,68 @@
 
         Resources resources = getResources();
         mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
-        mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
 
-        mIsRtl = Utilities.isRtl(resources);
-        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+        int actualIconSize = mActivityContext.getDeviceProfile().iconSizePx;
+
+        // We layout the icons to be of mIconTouchSize in width and height
+        mItemMarginLeftRight = actualMargin - (mIconTouchSize - actualIconSize) / 2;
+        mItemPadding = (mIconTouchSize - actualIconSize) / 2;
     }
 
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mSystemButtonContainer = findViewById(R.id.nav_button_layout);
-        mHotseatIconsContainer = findViewById(R.id.hotseat_icons_layout);
-        mContextualButtonContainer = findViewById(R.id.contextual_button_layout);
-    }
-
-    protected void init(TaskbarIconController.TaskbarViewCallbacks callbacks,
-            OnClickListener clickListener, OnLongClickListener longClickListener,
-            ButtonProvider buttonProvider) {
-        mControllerCallbacks = callbacks;
+    protected void init(OnClickListener clickListener, OnLongClickListener longClickListener) {
         mIconClickListener = clickListener;
         mIconLongClickListener = longClickListener;
-        mButtonProvider = buttonProvider;
-
-        if (mActivityContext.canShowNavButtons()) {
-            createNavButtons();
-        } else {
-            mSystemButtonContainer.setVisibility(GONE);
-        }
 
         int numHotseatIcons = mActivityContext.getDeviceProfile().numShownHotseatIcons;
         updateHotseatItems(new ItemInfo[numHotseatIcons]);
+    }
 
-        if (mActivityContext.canShowNavButtons()) {
-            createContextualRegion();
+    /**
+     * Aligns the icons in the taskbar to that of Launcher.
+     */
+    public void alignIconsWithLauncher(DeviceProfile launcherDp, PropertySetter setter) {
+        Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(getContext());
+        float scaleUp = ((float) launcherDp.iconSizePx)
+                / mActivityContext.getDeviceProfile().iconSizePx;
+        int hotseatCellSize =
+                (launcherDp.availableWidthPx - hotseatPadding.left - hotseatPadding.right)
+                        / launcherDp.numShownHotseatIcons;
+
+        int offsetY = launcherDp.getTaskbarOffsetY();
+        setter.setFloat(this, VIEW_TRANSLATE_Y, -offsetY, LINEAR);
+        mActivityContext.setTaskbarWindowHeight(
+                mActivityContext.getDeviceProfile().taskbarSize + offsetY);
+
+        int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != VISIBLE) {
+                continue;
+            }
+            setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
+
+            float childCenter = (child.getLeft() + child.getRight()) / 2;
+            float hotseatIconCenter = hotseatPadding.left + hotseatCellSize * (i)
+                    + hotseatCellSize / 2;
+            setter.setFloat(child, VIEW_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
         }
     }
 
     /**
-     * Enables/disables empty icons in taskbar so that the layout matches with Launcher
+     * Aligns the icons in the taskbar to that of Launcher.
+     * @return a callback to be executed at the end of the setter
      */
-    public void setHolesAllowedInLayout(boolean areHolesAllowed) {
-        if (mAreHolesAllowed != areHolesAllowed) {
-            mAreHolesAllowed = areHolesAllowed;
-            updateHotseatItemsVisibility();
-            // TODO: Add animation
+    public Runnable resetIconPosition(PropertySetter setter) {
+        int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            setter.setFloat(child, SCALE_PROPERTY, 1, LINEAR);
+            setter.setFloat(child, VIEW_TRANSLATE_X, 0, LINEAR);
         }
-    }
-
-    private void setHolesAllowedInLayoutNoAnimation(boolean areHolesAllowed) {
-        if (mAreHolesAllowed != areHolesAllowed) {
-            mAreHolesAllowed = areHolesAllowed;
-            updateHotseatItemsVisibility();
-            onMeasure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
-                    makeMeasureSpec(getMeasuredHeight(), EXACTLY));
-            onLayout(false, getLeft(), getTop(), getRight(), getBottom());
-        }
+        setter.setFloat(this, VIEW_TRANSLATE_Y, 0, LINEAR);
+        return () -> mActivityContext.setTaskbarWindowHeight(
+                mActivityContext.getDeviceProfile().taskbarSize);
     }
 
     /**
@@ -168,9 +158,8 @@
      */
     protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
         for (int i = 0; i < hotseatItemInfos.length; i++) {
-            ItemInfo hotseatItemInfo = hotseatItemInfos[
-                    !mIsRtl ? i : hotseatItemInfos.length - i - 1];
-            View hotseatView = mHotseatIconsContainer.getChildAt(i);
+            ItemInfo hotseatItemInfo = hotseatItemInfos[i];
+            View hotseatView = getChildAt(i);
 
             // Replace any Hotseat views with the appropriate type if it's not already that type.
             final int expectedLayoutResId;
@@ -191,7 +180,7 @@
             if (hotseatView == null
                     || hotseatView.getSourceLayoutResId() != expectedLayoutResId
                     || needsReinflate) {
-                mHotseatIconsContainer.removeView(hotseatView);
+                removeView(hotseatView);
                 if (isFolder) {
                     FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
                     FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
@@ -201,10 +190,9 @@
                 } else {
                     hotseatView = inflate(expectedLayoutResId);
                 }
-                int iconSize = mActivityContext.getDeviceProfile().iconSizePx;
-                LayoutParams lp = new LayoutParams(iconSize, iconSize);
-                lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
-                mHotseatIconsContainer.addView(hotseatView, i, lp);
+                LayoutParams lp = new LayoutParams(mIconTouchSize, mIconTouchSize);
+                hotseatView.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
+                addView(hotseatView, i, lp);
             }
 
             // Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
@@ -222,22 +210,42 @@
                 hotseatView.setOnLongClickListener(null);
                 hotseatView.setTag(null);
             }
-            updateHotseatItemVisibility(hotseatView);
+            hotseatView.setVisibility(hotseatView.getTag() != null ? VISIBLE : INVISIBLE);
         }
     }
 
-    protected void updateHotseatItemsVisibility() {
-        for (int i = mHotseatIconsContainer.getChildCount() - 1; i >= 0; i--) {
-            updateHotseatItemVisibility(mHotseatIconsContainer.getChildAt(i));
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        int count = getChildCount();
+        // Find total visible children
+        int visibleChildren = 0;
+        for (int i = 0; i < count; i++) {
+            if (getChildAt(i).getVisibility() == VISIBLE) {
+                visibleChildren++;
+            }
         }
-    }
 
-    private void updateHotseatItemVisibility(View hotseatView) {
-        if (!mControllerCallbacks.canUpdateViewVisibility(hotseatView)) {
-            return;
+        int spaceNeeded = visibleChildren * (mItemMarginLeftRight * 2 + mIconTouchSize);
+        int iconStart = (right - left - spaceNeeded) / 2;
+        int startOffset = ApiWrapper.getHotseatStartOffset(getContext());
+        if (startOffset > iconStart) {
+            int diff = startOffset - iconStart;
+            iconStart = isLayoutRtl() ? (iconStart - diff) : iconStart + diff;
         }
-        hotseatView.setVisibility(
-                hotseatView.getTag() != null ? VISIBLE : (mAreHolesAllowed ? INVISIBLE : GONE));
+        // Layout the children
+        mIconLayoutBounds.left = iconStart;
+        mIconLayoutBounds.top = (bottom - top - mIconTouchSize) / 2;
+        mIconLayoutBounds.bottom = mIconLayoutBounds.top + mIconTouchSize;
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() == VISIBLE) {
+                iconStart += mItemMarginLeftRight;
+                int iconEnd = iconStart + mIconTouchSize;
+                child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom);
+                iconStart = iconEnd + mItemMarginLeftRight;
+            }
+        }
+        mIconLayoutBounds.right = iconStart;
     }
 
     @Override
@@ -248,154 +256,21 @@
         return super.dispatchTouchEvent(ev);
     }
 
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        boolean handled = delegateTouchIfNecessary(event);
-        return super.onTouchEvent(event) || handled;
-    }
-
     public void setTouchesEnabled(boolean touchEnabled) {
         this.mTouchEnabled = touchEnabled;
     }
 
     /**
-     * User touched the Taskbar background. Determine whether the touch is close enough to a view
-     * that we should forward the touches to it.
-     * @return Whether a delegate view was chosen and it handled the touch event.
-     */
-    private boolean delegateTouchIfNecessary(MotionEvent event) {
-        final float x = event.getX();
-        final float y = event.getY();
-        if (mDelegateView == null && event.getAction() == MotionEvent.ACTION_DOWN) {
-            View delegateView = findDelegateView(x, y);
-            if (delegateView != null) {
-                mDelegateTargeted = true;
-                mDelegateView = delegateView;
-                mDelegateSlopBounds.set(mTempDelegateBounds);
-                mDelegateSlopBounds.inset(-mTouchSlop, -mTouchSlop);
-            }
-        }
-
-        boolean sendToDelegate = mDelegateTargeted;
-        boolean inBounds = true;
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_MOVE:
-                inBounds = mDelegateSlopBounds.contains(x, y);
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                mDelegateTargeted = false;
-                break;
-        }
-
-        boolean handled = false;
-        if (sendToDelegate) {
-            if (inBounds) {
-                // Offset event coordinates to be inside the target view
-                event.setLocation(mDelegateView.getWidth() / 2f, mDelegateView.getHeight() / 2f);
-            } else {
-                // Offset event coordinates to be outside the target view (in case it does
-                // something like tracking pressed state)
-                event.setLocation(-mTouchSlop * 2, -mTouchSlop * 2);
-            }
-            handled = mDelegateView.dispatchTouchEvent(event);
-            // Cleanup if this was the last event to send to the delegate.
-            if (!mDelegateTargeted) {
-                mDelegateView = null;
-            }
-        }
-        return handled;
-    }
-
-    /**
-     * Return an item whose touch bounds contain the given coordinates,
-     * or null if no such item exists.
-     *
-     * Also sets {@link #mTempDelegateBounds} to be the touch bounds of the chosen delegate view.
-     */
-    private @Nullable View findDelegateView(float x, float y) {
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-            if (!child.isShown() || !child.isClickable()) {
-                continue;
-            }
-            int childCenterX = child.getLeft() + child.getWidth() / 2;
-            int childCenterY = child.getTop() + child.getHeight() / 2;
-            mTempDelegateBounds.set(
-                    childCenterX - mIconTouchSize / 2f,
-                    childCenterY - mIconTouchSize / 2f,
-                    childCenterX + mIconTouchSize / 2f,
-                    childCenterY + mIconTouchSize / 2f);
-            if (mTempDelegateBounds.contains(x, y)) {
-                return child;
-            }
-        }
-        return null;
-    }
-
-    /**
      * Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's
      * touch bounds.
      */
     public boolean isEventOverAnyItem(MotionEvent ev) {
         getLocationOnScreen(mTempOutLocation);
-        float xInOurCoordinates = ev.getX() - mTempOutLocation[0];
-        float yInOurCoorindates = ev.getY() - mTempOutLocation[1];
-        return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null;
+        int xInOurCoordinates = (int) ev.getX() - mTempOutLocation[0];
+        int yInOurCoorindates = (int) ev.getY() - mTempOutLocation[1];
+        return isShown() && mIconLayoutBounds.contains(xInOurCoordinates, yInOurCoorindates);
     }
 
-    /**
-     * Add back/home/recents buttons into a single ViewGroup that will be inserted at
-     * {@param navButtonStartIndex}
-     */
-    private void createNavButtons() {
-        LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
-                mActivityContext.getDeviceProfile().iconSizePx,
-                mActivityContext.getDeviceProfile().iconSizePx
-        );
-        buttonParams.gravity = Gravity.CENTER;
-
-        mSystemButtonContainer.addView(mButtonProvider.getBack(), buttonParams);
-        mSystemButtonContainer.addView(mButtonProvider.getHome(), buttonParams);
-        mSystemButtonContainer.addView(mButtonProvider.getRecents(), buttonParams);
-    }
-
-    /**
-     * @return The bounding box of where the hotseat elements are relative to this TaskbarView.
-     */
-    protected RectF getHotseatBounds() {
-        RectF result;
-        mDisableRelayout = true;
-        boolean wereHolesAllowed = mAreHolesAllowed;
-        setHolesAllowedInLayoutNoAnimation(true);
-        result = new RectF(
-                mHotseatIconsContainer.getLeft(),
-                mHotseatIconsContainer.getTop(),
-                mHotseatIconsContainer.getRight(),
-                mHotseatIconsContainer.getBottom());
-        setHolesAllowedInLayoutNoAnimation(wereHolesAllowed);
-        mDisableRelayout = false;
-
-        return result;
-    }
-
-    @Override
-    public void requestLayout() {
-        if (!mDisableRelayout) {
-            super.requestLayout();
-        }
-    }
-
-    private void createContextualRegion() {
-        mContextualRotationButton = mButtonProvider.getContextualRotation();
-        mContextualRotationButton.setVisibility(GONE);
-        mContextualButtonContainer.addView(mContextualRotationButton);
-    }
-
-    @Nullable
-    public RotationContextButton getContextualRotationButton() {
-        return mContextualRotationButton;
-    }
     // FolderIconParent implemented methods.
 
     @Override
@@ -432,11 +307,8 @@
         // Ignore, we just implement Insettable to draw behind system insets.
     }
 
-    public void setIconsVisibility(boolean isVisible) {
-        mHotseatIconsContainer.setVisibility(isVisible ? VISIBLE : INVISIBLE);
-    }
-
     public boolean areIconsVisible() {
-        return mHotseatIconsContainer.getVisibility() == VISIBLE;
+        // Consider the overall visibility
+        return getVisibility() == VISIBLE;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButton.java b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButton.java
index d421077..4093097 100644
--- a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButton.java
+++ b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButton.java
@@ -30,16 +30,24 @@
  *  * Directly use AnimatedVectorDrawable instead of KeyButtonDrawable
  */
 public interface RotationButton {
-    void setRotationButtonController(RotationButtonController rotationButtonController);
-    View getCurrentView();
-    boolean show();
-    boolean hide();
-    boolean isVisible();
-    void updateIcon(int lightIconColor, int darkIconColor);
-    void setOnClickListener(View.OnClickListener onClickListener);
-    void setOnHoverListener(View.OnHoverListener onHoverListener);
-    AnimatedVectorDrawable getImageDrawable();
-    void setDarkIntensity(float darkIntensity);
+    default void setRotationButtonController(RotationButtonController rotationButtonController) { }
+
+    default View getCurrentView() {
+        return null;
+    }
+    default void show() { }
+    default void hide() { }
+    default boolean isVisible() {
+        return false;
+    }
+
+    default void updateIcon(int lightIconColor, int darkIconColor) { }
+    default void setOnClickListener(View.OnClickListener onClickListener) { }
+    default void setOnHoverListener(View.OnHoverListener onHoverListener) { }
+    default AnimatedVectorDrawable getImageDrawable() {
+        return null;
+    }
+    default void setDarkIntensity(float darkIntensity) { }
     default boolean acceptRotationProposal() {
         return getCurrentView() != null;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButtonController.java b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButtonController.java
index 6f6abc2..99dc282 100644
--- a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationButtonController.java
@@ -104,7 +104,7 @@
 
     private final IRotationWatcher.Stub mRotationWatcher = new IRotationWatcher.Stub() {
         @Override
-        public void onRotationChanged(final int rotation) throws RemoteException {
+        public void onRotationChanged(final int rotation) {
             // We need this to be scheduled as early as possible to beat the redrawing of
             // window in response to the orientation change.
             mMainThreadHandler.postAtFrontOfQueue(() -> {
@@ -137,7 +137,7 @@
 
         mAccessibilityManager = AccessibilityManager.getInstance(context);
         mTaskStackListener = new TaskStackListenerImpl();
-        mDisplayController = DisplayController.INSTANCE.getNoCreate();
+        mDisplayController = DisplayController.INSTANCE.get(context);
     }
 
     public void setRotationButton(RotationButton rotationButton) {
@@ -156,7 +156,7 @@
         }
     }
 
-    public void cleanup() {
+    public void onDestroy() {
         unregisterListeners();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationContextButton.java b/quickstep/src/com/android/launcher3/taskbar/contextual/RotationContextButton.java
deleted file mode 100644
index 7ad3191..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/contextual/RotationContextButton.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright 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.taskbar.contextual;
-
-import android.content.Context;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.android.launcher3.R;
-
-/** Containing logic for the rotation button in nav bar. */
-public class RotationContextButton extends ImageView implements RotationButton {
-
-    private AnimatedVectorDrawable mImageDrawable;
-
-    public RotationContextButton(Context context) {
-        super(context);
-        setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
-    }
-
-    @Override
-    public void setRotationButtonController(RotationButtonController rotationButtonController) {
-        // TODO(b/187754252) UI polish, different icons based on light/dark context, etc
-        mImageDrawable = (AnimatedVectorDrawable) getContext()
-                .getDrawable(rotationButtonController.getIconResId());
-        setImageDrawable(mImageDrawable);
-        mImageDrawable.setCallback(this);
-    }
-
-    @Override
-    public View getCurrentView() {
-        return this;
-    }
-
-    @Override
-    public boolean show() {
-        setVisibility(VISIBLE);
-        return true;
-    }
-
-    @Override
-    public boolean hide() {
-        setVisibility(GONE);
-        return true;
-    }
-
-    @Override
-    public boolean isVisible() {
-        return getVisibility() == VISIBLE;
-    }
-
-    @Override
-    public void updateIcon(int lightIconColor, int darkIconColor) {
-        // TODO(b/187754252): UI Polish
-    }
-
-    @Override
-    public void setOnClickListener(View.OnClickListener onClickListener) {
-        super.setOnClickListener(onClickListener);
-    }
-
-    @Override
-    public void setOnHoverListener(View.OnHoverListener onHoverListener) {
-        super.setOnHoverListener(onHoverListener);
-    }
-
-    @Override
-    public AnimatedVectorDrawable getImageDrawable() {
-        return mImageDrawable;
-    }
-
-    @Override
-    public void setDarkIntensity(float darkIntensity) {
-        // TODO(b/187754252) UI polish
-    }
-
-    @Override
-    public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-
-        if (visibility != View.VISIBLE && mImageDrawable != null) {
-            mImageDrawable.clearAnimationCallbacks();
-            mImageDrawable.reset();
-        }
-
-        // Start the rotation animation once it becomes visible
-        if (visibility == View.VISIBLE && mImageDrawable != null) {
-            mImageDrawable.reset();
-            mImageDrawable.start();
-        }
-    }
-
-    @Override
-    public boolean acceptRotationProposal() {
-        return isAttachedToWindow();
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 76a5782..a595f54 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,10 +17,15 @@
 package com.android.launcher3.uioverrides;
 
 import android.app.Person;
+import android.content.Context;
 import android.content.pm.ShortcutInfo;
+import android.content.res.Resources;
 import android.view.Display;
 
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
 
 public class ApiWrapper {
 
@@ -37,4 +42,18 @@
     public static boolean isInternalDisplay(Display display) {
         return display.getType() == Display.TYPE_INTERNAL;
     }
+
+    /**
+     * Returns the minimum space that should be left empty at the start of hotseat
+     */
+    public static int getHotseatStartOffset(Context context) {
+        if (SysUINavigationMode.INSTANCE.get(context).getMode() == Mode.THREE_BUTTONS) {
+            Resources res = context.getResources();
+            return 2 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_spacing)
+                    + 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size);
+        } else {
+            return 0;
+        }
+
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 1304033..d151131 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
+
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
@@ -26,6 +28,7 @@
 import android.widget.RemoteViews;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -72,9 +75,24 @@
             mLauncher.addLaunchCookie((ItemInfo) itemInfo, activityOptions.options);
         }
         options = Pair.create(options.first, activityOptions.options);
+        if (pendingIntent.isActivity()) {
+            logAppLaunch(itemInfo);
+        }
         return RemoteViews.startPendingIntent(hostView, pendingIntent, options);
     }
 
+    /**
+     * Logs that the app was launched from the widget.
+     * @param itemInfo the widget info.
+     */
+    private void logAppLaunch(Object itemInfo) {
+        StatsLogManager.StatsLogger logger = mLauncher.getStatsLogManager().logger();
+        if (itemInfo instanceof ItemInfo) {
+            logger.withItemInfo((ItemInfo) itemInfo);
+        }
+        logger.log(LAUNCHER_APP_LAUNCH_TAP);
+    }
+
     private LauncherAppWidgetHostView findHostViewAncestor(View v) {
         while (v != null) {
             if (v instanceof LauncherAppWidgetHostView) return (LauncherAppWidgetHostView) v;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 8c128c8..90e17c0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -83,16 +83,6 @@
     }
 
     @Override
-    public float getTaskbarScale(Launcher launcher) {
-        return 1f;
-    }
-
-    @Override
-    public float getTaskbarTranslationY(Launcher launcher) {
-        return 0f;
-    }
-
-    @Override
     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
         return new PageAlphaProvider(DEACCEL_2) {
             @Override
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 306032c..88db274 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -745,10 +745,25 @@
         TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
 
         if (mRecentsView != null) {
-            InteractionJankMonitorWrapper.begin(mRecentsView,
-                    InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
-            InteractionJankMonitorWrapper.begin(mRecentsView,
-                    InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+            mRecentsView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
+                boolean mHandled = false;
+
+                @Override
+                public void onDraw() {
+                    if (mHandled) {
+                        return;
+                    }
+                    mHandled = true;
+
+                    InteractionJankMonitorWrapper.begin(mRecentsView,
+                            InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
+                    InteractionJankMonitorWrapper.begin(mRecentsView,
+                            InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+
+                    mRecentsView.post(() ->
+                            mRecentsView.getViewTreeObserver().removeOnDrawListener(this));
+                }
+            });
         }
         notifyGestureStartedAsync();
         setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 0e9e3ad..d43bb24 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -350,8 +350,7 @@
     public void startHome() {
         if (LIVE_TILE.get()) {
             RecentsView recentsView = getOverviewPanel();
-            recentsView.switchToScreenshot(() -> recentsView.finishRecentsAnimation(true,
-                    this::startHomeInternal));
+            recentsView.switchToScreenshotAndFinishAnimationToRecents(this::startHomeInternal);
         } else {
             startHomeInternal();
         }
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index d040904..090fd01 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -85,6 +85,7 @@
     private float mLastNavButtonAlpha;
     private boolean mLastNavButtonAnimate;
     private boolean mHasNavButtonAlphaBeenSet = false;
+    private Runnable mPendingSetNavButtonAlpha = null;
 
     // TODO(141886704): Find a way to remove this
     private int mLastSystemUiStateFlags;
@@ -157,6 +158,11 @@
             setSmartspaceCallback(mPendingSmartspaceCallback);
             mPendingSmartspaceCallback = null;
         }
+
+        if (mPendingSetNavButtonAlpha != null) {
+            mPendingSetNavButtonAlpha.run();
+            mPendingSetNavButtonAlpha = null;
+        }
     }
 
     public void clearProxy() {
@@ -240,14 +246,18 @@
         boolean changed = Float.compare(alpha, mLastNavButtonAlpha) != 0
                 || animate != mLastNavButtonAnimate
                 || !mHasNavButtonAlphaBeenSet;
-        if (mSystemUiProxy != null && changed) {
-            mLastNavButtonAlpha = alpha;
-            mLastNavButtonAnimate = animate;
-            mHasNavButtonAlphaBeenSet = true;
-            try {
-                mSystemUiProxy.setNavBarButtonAlpha(alpha, animate);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setNavBarButtonAlpha", e);
+        if (changed) {
+            if (mSystemUiProxy == null) {
+                mPendingSetNavButtonAlpha = () -> setNavBarButtonAlpha(alpha, animate);
+            } else {
+                mLastNavButtonAlpha = alpha;
+                mLastNavButtonAnimate = animate;
+                mHasNavButtonAlphaBeenSet = true;
+                try {
+                    mSystemUiProxy.setNavBarButtonAlpha(alpha, animate);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed call setNavBarButtonAlpha", e);
+                }
             }
         }
     }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index b5ffdbe..e9e6ab6 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -176,6 +176,10 @@
         TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
         TaskVisualsChangeListener, SplitScreenBounds.OnChangeListener {
 
+    // TODO(b/184899234): We use this timeout to wait a fixed period after switching to the
+    // screenshot when dismissing the current live task to ensure the app can try and get stopped.
+    private static final int REMOVE_TASK_WAIT_FOR_APP_STOP_MS = 100;
+
     public static final FloatProperty<RecentsView> CONTENT_ALPHA =
             new FloatProperty<RecentsView>("contentAlpha") {
                 @Override
@@ -2432,8 +2436,11 @@
                 if (success) {
                     if (shouldRemoveTask) {
                         if (dismissedTaskView.getTask() != null) {
-                            UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
-                                    .removeTask(dismissedTaskId));
+                            switchToScreenshotAndFinishAnimationToRecents(() -> {
+                                UI_HELPER_EXECUTOR.getHandler().postDelayed(() ->
+                                        ActivityManagerWrapper.getInstance().removeTask(
+                                                dismissedTaskId), REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
+                            });
                             mActivity.getStatsLogManager().logger()
                                     .withItemInfo(dismissedTaskView.getItemInfo())
                                     .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
@@ -2539,10 +2546,13 @@
         mPendingAnimation.addEndListener(isSuccess -> {
             if (isSuccess) {
                 // Remove all the task views now
-                UI_HELPER_EXECUTOR.execute(
-                        ActivityManagerWrapper.getInstance()::removeAllRecentTasks);
-                removeTasksViewsAndClearAllButton();
-                startHome();
+                switchToScreenshotAndFinishAnimationToRecents(() -> {
+                    UI_HELPER_EXECUTOR.getHandler().postDelayed(
+                            ActivityManagerWrapper.getInstance()::removeAllRecentTasks,
+                            REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
+                    removeTasksViewsAndClearAllButton();
+                    startHome();
+                });
             }
             mPendingAnimation = null;
         });
@@ -2697,9 +2707,7 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         if (LIVE_TILE.get() && mEnableDrawingLiveTile && newConfig.orientation != mOrientation) {
-            switchToScreenshot(
-                    () -> finishRecentsAnimation(true /* toRecents */,
-                            this::updateRecentsRotation));
+            switchToScreenshotAndFinishAnimationToRecents(this::updateRecentsRotation);
             mEnableDrawingLiveTile = false;
         } else {
             updateRecentsRotation();
@@ -3697,6 +3705,10 @@
         }
     }
 
+    public void switchToScreenshotAndFinishAnimationToRecents(Runnable onFinishRunnable) {
+        switchToScreenshot(() -> finishRecentsAnimation(true /* toRecents */, onFinishRunnable));
+    }
+
     /**
      * Switch the current running task view to static snapshot mode,
      * capturing the snapshot at the same time.
diff --git a/res/drawable/widgets_list_bottom_ripple.xml b/res/drawable/widgets_list_bottom_ripple.xml
deleted file mode 100644
index c2debb1..0000000
--- a/res/drawable/widgets_list_bottom_ripple.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
-    <item android:id="@android:id/mask">
-        <shape android:shape="rectangle">
-            <solid android:color="@color/surface" />
-            <corners
-                android:topLeftRadius="@dimen/widget_list_content_corner_radius"
-                android:topRightRadius="@dimen/widget_list_content_corner_radius"
-                android:bottomLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
-                android:bottomRightRadius="@dimen/widget_list_top_bottom_corner_radius" />
-        </shape>
-    </item>
-    <item android:id="@android:id/background">
-        <shape android:shape="rectangle">
-            <solid android:color="@color/surface" />
-            <corners
-                android:topLeftRadius="@dimen/widget_list_content_corner_radius"
-                android:topRightRadius="@dimen/widget_list_content_corner_radius"
-                android:bottomLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
-                android:bottomRightRadius="@dimen/widget_list_top_bottom_corner_radius" />
-        </shape>
-    </item>
-</ripple>
\ No newline at end of file
diff --git a/res/drawable/widgets_list_middle_ripple.xml b/res/drawable/widgets_list_middle_ripple.xml
deleted file mode 100644
index 83f96a0..0000000
--- a/res/drawable/widgets_list_middle_ripple.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
-    <item android:id="@android:id/mask">
-        <shape android:shape="rectangle">
-            <solid android:color="@color/surface" />
-            <corners
-                android:topLeftRadius="@dimen/widget_list_content_corner_radius"
-                android:topRightRadius="@dimen/widget_list_content_corner_radius"
-                android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
-                android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
-        </shape>
-    </item>
-
-    <item android:id="@android:id/background">
-        <shape android:shape="rectangle">
-            <solid android:color="@color/surface" />
-            <corners
-                android:topLeftRadius="@dimen/widget_list_content_corner_radius"
-                android:topRightRadius="@dimen/widget_list_content_corner_radius"
-                android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
-                android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
-        </shape>
-    </item>
-</ripple>
\ No newline at end of file
diff --git a/res/drawable/widgets_list_single_item_ripple.xml b/res/drawable/widgets_list_single_item_ripple.xml
deleted file mode 100644
index a4223a8..0000000
--- a/res/drawable/widgets_list_single_item_ripple.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
-    <item android:id="@android:id/mask">
-        <shape android:shape="rectangle">
-            <solid android:color="@color/surface" />
-            <corners
-                android:topLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
-                android:topRightRadius="@dimen/widget_list_top_bottom_corner_radius"
-                android:bottomLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
-                android:bottomRightRadius="@dimen/widget_list_top_bottom_corner_radius" />
-        </shape>
-    </item>
-    <item android:id="@android:id/background">
-        <shape android:shape="rectangle">
-            <solid android:color="@color/surface" />
-            <corners
-                android:topLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
-                android:topRightRadius="@dimen/widget_list_top_bottom_corner_radius"
-                android:bottomLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
-                android:bottomRightRadius="@dimen/widget_list_top_bottom_corner_radius" />
-        </shape>
-    </item>
-</ripple>
\ No newline at end of file
diff --git a/res/drawable/widgets_list_top_ripple.xml b/res/drawable/widgets_list_top_ripple.xml
deleted file mode 100644
index bc0876e..0000000
--- a/res/drawable/widgets_list_top_ripple.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
-    <item android:id="@android:id/mask">
-        <shape android:shape="rectangle">
-            <solid android:color="@color/surface" />
-            <corners
-                android:topLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
-                android:topRightRadius="@dimen/widget_list_top_bottom_corner_radius"
-                android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
-                android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
-        </shape>
-    </item>
-
-    <item android:id="@android:id/background">
-        <shape android:shape="rectangle">
-            <solid android:color="@color/surface" />
-            <corners
-                android:topLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
-                android:topRightRadius="@dimen/widget_list_top_bottom_corner_radius"
-                android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
-                android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
-        </shape>
-    </item>
-</ripple>
\ No newline at end of file
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index b570464..9ac6ed0 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -36,6 +36,7 @@
         android:layout_below="@id/search_container_all_apps"
         android:clipToPadding="false"
         android:paddingTop="@dimen/all_apps_header_top_padding"
+        android:paddingBottom="@dimen/all_apps_header_bottom_padding"
         android:orientation="vertical" >
 
         <include layout="@layout/floating_header_content" />
diff --git a/res/layout/all_apps_personal_work_tabs.xml b/res/layout/all_apps_personal_work_tabs.xml
index ebb69f6..cfaa261 100644
--- a/res/layout/all_apps_personal_work_tabs.xml
+++ b/res/layout/all_apps_personal_work_tabs.xml
@@ -31,7 +31,7 @@
         android:background="@drawable/personal_work_tabs_ripple"
         android:text="@string/all_apps_personal_tab"
         android:textColor="@color/all_apps_tab_text"
-        android:textSize="16sp" />
+        android:textSize="14sp" />
 
     <Button
         android:id="@+id/tab_work"
@@ -41,5 +41,5 @@
         android:background="@drawable/personal_work_tabs_ripple"
         android:text="@string/all_apps_work_tab"
         android:textColor="@color/all_apps_tab_text"
-        android:textSize="16sp" />
+        android:textSize="14sp" />
 </com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
\ No newline at end of file
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 8b18857..15131f1 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -18,7 +18,6 @@
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:background="@drawable/round_rect_folder"
     android:orientation="vertical" >
 
     <com.android.launcher3.folder.FolderPagedView
diff --git a/res/layout/widgets_list_row_header.xml b/res/layout/widgets_list_row_header.xml
index 8ab086c..0bfa2b2 100644
--- a/res/layout/widgets_list_row_header.xml
+++ b/res/layout/widgets_list_row_header.xml
@@ -20,7 +20,6 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_marginHorizontal="16dp"
-    android:background="@drawable/widgets_list_middle_ripple"
     android:layout_marginBottom="@dimen/widget_list_entry_bottom_margin"
     android:paddingVertical="@dimen/widget_list_header_view_vertical_padding"
     android:orientation="horizontal"
diff --git a/res/layout/widgets_table_container.xml b/res/layout/widgets_table_container.xml
index e63483d..c6b70aa 100644
--- a/res/layout/widgets_table_container.xml
+++ b/res/layout/widgets_table_container.xml
@@ -19,5 +19,4 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_marginHorizontal="16dp"
-    android:background="@drawable/widgets_list_middle_ripple"
     android:layout_marginBottom="@dimen/widget_list_entry_bottom_margin"/>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index c1ed288..558d22f 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -160,8 +160,8 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobní"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovní"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovní profil"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Osobní údaje jsou oddělené a jsou před pracovními aplikacemi skryty"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"K datům pracovních aplikací má přístup váš administrátor IT"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Osobní údaje jsou oddělené a před pracovními aplikacemi jsou skryty"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Pracovní aplikace a údaje může vidět váš administrátor IT"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Další"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Rozumím"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Pracovní profil je pozastaven"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 3a20d17..72929cf 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -154,7 +154,7 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Privat"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Geschäftlich"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbeitsprofil"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Personenbezogene Daten sind für geschäftlichen Apps nicht sichtbar oder zugänglich"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Personenbezogene Daten sind für geschäftliche Apps nicht sichtbar oder zugänglich"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Geschäftliche Apps und Daten können von deinem IT-Administrator eingesehen werden"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Weiter"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 812f007..735f6e3 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -154,7 +154,7 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Pertsonalak"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Lanekoak"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Laneko profila"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Datu pertsonalak bananduta daude eta ez daude laneko aplikazioen artean ikusgai"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Datu pertsonalak ez daude laneko aplikazioetan, eta ezin dira haien bidez ikusi"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"IKT saileko administratzaileak laneko aplikazioak eta datuak ikus ditzake"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Hurrengoa"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ados"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 9522a38..e7801c9 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -51,12 +51,9 @@
     <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personnels"</string>
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Professionnels"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
-    <!-- no translation found for widget_education_header (4874760613775913787) -->
-    <skip />
-    <!-- no translation found for widget_education_content (745542879510751525) -->
-    <skip />
-    <!-- no translation found for widget_education_close_button (8676165703104836580) -->
-    <skip />
+    <string name="widget_education_header" msgid="4874760613775913787">"Renseignements utiles à portée de main"</string>
+    <string name="widget_education_content" msgid="745542879510751525">"Pour obtenir des renseignements sans ouvrir aucune application, vous pouvez ajouter des widgets à votre écran d\'accueil"</string>
+    <string name="widget_education_close_button" msgid="8676165703104836580">"OK"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Rechercher dans les applications"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Chargement des applications en cours…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Aucune application trouvée correspondant à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 9c909a0..d9ffa68 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -51,12 +51,9 @@
     <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"વ્યક્તિગત"</string>
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ઑફિસ"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"વાતચીતો"</string>
-    <!-- no translation found for widget_education_header (4874760613775913787) -->
-    <skip />
-    <!-- no translation found for widget_education_content (745542879510751525) -->
-    <skip />
-    <!-- no translation found for widget_education_close_button (8676165703104836580) -->
-    <skip />
+    <string name="widget_education_header" msgid="4874760613775913787">"ઉપયોગી માહિતી તમારી આંગળીના ટેરવે"</string>
+    <string name="widget_education_content" msgid="745542879510751525">"ઍપને ખોલ્યા વિના માહિતી મેળવવા માટે, તમે તમારી હોમ સ્ક્રીન પર વિજેટ ઉમેરી શકો છો"</string>
+    <string name="widget_education_close_button" msgid="8676165703104836580">"સમજાઈ ગયું"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"શોધ ઍપ્લિકેશનો"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ઍપ્લિકેશનો લોડ કરી રહ્યું છે…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index bc68e3e..f7d6b31 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -32,7 +32,7 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Կրկնակի հպեք և պահեք՝ վիջեթ տեղափոխելու համար, կամ օգտվեք հատուկ գործողություններից։"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Լայնությունը՝ %1$d, բարձրությունը՝ %2$d"</string>
-    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Հպեք վիջեթին և պահեք՝ հիմնական էկրանին տեղափոխելու համար"</string>
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Հպեք վիջեթին և պահեք տեղափոխելու համար"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Ավելացնել հիմնական էկրանին"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> վիջեթ</item>
@@ -154,13 +154,13 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Անձնական"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Աշխատանքային"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Աշխատանքային պրոֆիլ"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Անձնական տվյալները թաքցված են և առանձնացված աշխատանքային հավելվածներից"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Անձնական տվյալները առանձին են և թաքցված են, երբ ցուցադրվում են աշխատանքայինները"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Աշխատանքային հավելվածներն ու դրանց տվյալները տեսանելի են ձեր ադմինիստրատորին"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Առաջ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Եղավ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Աշխատանքային պրոֆիլը դադարեցված է"</string>
-    <string name="work_apps_paused_body" msgid="4209084728264328628">"Աշխատանքային հավելվածները չեն կարող ձեզ ծանուցումներ ուղարկել, օգտագործել ձեր մարտկոցը և ձեր տեղադրության մասին տվյալներ ստանալ։"</string>
-    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Աշխատանքային պրոֆիլը դադարեցված է։ Աշխատանքային հավելվածները չեն կարող ձեզ ծանուցումներ ուղարկել, օգտագործել ձեր մարտկոցը և ձեր տեղադրության մասին տվյալներ ստանալ։"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Աշխատանքային հավելվածները չեն կարող ծանուցումներ ուղարկել ձեզ, օգտագործել ձեր մարտկոցը և ձեր տեղադրության մասին տվյալներ ստանալ։"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Աշխատանքային պրոֆիլը դադարեցված է։ Աշխատանքային հավելվածները չեն կարող ծանուցումներ ուղարկել ձեզ, օգտագործել ձեր մարտկոցը և ձեր տեղադրության մասին տվյալներ ստանալ։"</string>
     <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Աշխատանքային հավելվածները նշանակներ ունեն և տեսանելի են ձեր ՏՏ ադմինիստրատորին"</string>
     <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Եղավ"</string>
     <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Դադարեցնել աշխատանքային հավելվածները"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index fb72d55..8ec7de3 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -161,7 +161,7 @@
     <string name="all_apps_work_tab" msgid="4884822796154055118">"עבודה"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"פרופיל עבודה"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"מידע אישי מאוחסן בנפרד ומוסתר מאפליקציות לעבודה"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"‏אפליקציות לעבודה ונתוני העבודה שלך גלויים למנהל ה-IT"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"‏אפליקציות לעבודה ונתוני פרופיל העבודה שלך גלויים למנהל ה-IT"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"הבא"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"הבנתי"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"פרופיל העבודה מושהה"</string>
@@ -172,6 +172,6 @@
     <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"השהיית האפליקציות לעבודה"</string>
     <string name="work_apps_enable_btn_text" msgid="82111102541971856">"הפעלה"</string>
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"סינון"</string>
-    <string name="work_switch_tip" msgid="808075064383839144">"השהיה של התראות ואפליקציות לעבודה"</string>
+    <string name="work_switch_tip" msgid="808075064383839144">"השהיה של ההתראות והאפליקציות בפרופיל העבודה"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"הפעולה נכשלה: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 60fe453..d54f884 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -154,13 +154,13 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"個人用"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"仕事用"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"仕事用プロファイル"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"個人データは仕事用アプリとは別に保存され、一緒に表示されません"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"個人用と仕事用のデータは分離され、アプリは別々に表示されます"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"仕事用アプリと仕事用データは IT 管理者に公開されます"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"次へ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"仕事用プロファイルが一時停止しています"</string>
-    <string name="work_apps_paused_body" msgid="4209084728264328628">"仕事用アプリは、通知の送信、バッテリーの使用、位置情報へのアクセスを行えません"</string>
-    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"仕事用プロファイルが一時停止しています。仕事用アプリは、通知の送信、バッテリーの使用、位置情報へのアクセスを行えません"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"仕事用アプリは、通知の送信、バッテリーの使用、位置情報の取得を行えません"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"仕事用プロファイルが一時停止しています。仕事用アプリは、通知の送信、バッテリーの使用、位置情報の取得を行えません"</string>
     <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"仕事用アプリはバッジが付き、IT 管理者に公開されます"</string>
     <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
     <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"仕事用アプリを一時停止"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 2b1b89f..640ffca 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -51,12 +51,9 @@
     <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Жеке виджеттер"</string>
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Жұмыс виджеттері"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"Әңгімелер"</string>
-    <!-- no translation found for widget_education_header (4874760613775913787) -->
-    <skip />
-    <!-- no translation found for widget_education_content (745542879510751525) -->
-    <skip />
-    <!-- no translation found for widget_education_close_button (8676165703104836580) -->
-    <skip />
+    <string name="widget_education_header" msgid="4874760613775913787">"Саусақпен түртсеңіз болғаны – пайдалы ақпарат көз алдыңызда"</string>
+    <string name="widget_education_content" msgid="745542879510751525">"Қолданбаларды ашпай-ақ ақпарат алу үшін негізгі экранға тиісті виджеттерді қосыңыз."</string>
+    <string name="widget_education_close_button" msgid="8676165703104836580">"Түсінікті"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Қолданбаларды іздеу"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Қолданбалар жүктелуде…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" сұрауына сәйкес келетін қолданбалар жоқ"</string>
@@ -158,7 +155,7 @@
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Жұмыс"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Жұмыс профилі"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Жеке деректер бөлек орналасқан және жұмыс қолданбаларынан жасырылған"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Әкімшіңіз жұмыс қолданбалары мен деректерді көре алады"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Әкімшіңіз жұмыс қолданбалары мен деректерін көре алады"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Келесі"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Түсінікті"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Жұмыс профилі кідіртілді"</string>
@@ -169,6 +166,6 @@
     <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Жұмыс қолданбаларын тоқтата тұру"</string>
     <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Қосу"</string>
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Сүзгі"</string>
-    <string name="work_switch_tip" msgid="808075064383839144">"Жұмыс қолданбалары мен хабарландыруларды кідірту"</string>
+    <string name="work_switch_tip" msgid="808075064383839144">"Жұмыс қолданбалары мен хабарландыруларын кідірту"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Қате шықты: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 059620c..ad4082b 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -50,7 +50,7 @@
     <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Notīrīt tekstu no meklēšanas lodziņa"</string>
     <string name="no_widgets_available" msgid="9140948620298620513">"Nav pieejams neviens logrīks"</string>
     <string name="no_search_results" msgid="6518732304311458580">"Nav meklēšanas rezultātu"</string>
-    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personīgie"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personīgs"</string>
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Darba"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"Sarunas"</string>
     <string name="widget_education_header" msgid="4874760613775913787">"Ērta piekļuve noderīgai informācijai"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 1a8650c..c120c24 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -157,7 +157,7 @@
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"ကိုယ်ပိုင်ဒေတာများသည် သီးသန့်ဖြစ်ပြီး အလုပ်အက်ပ်များမှ ဖျောက်ထားသည်"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"အလုပ်သုံးအက်ပ်နှင့် ဒေတာများကို သင်၏ IT စီမံခန့်ခွဲသူက မြင်ရပါသည်"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"ရှေ့သို့"</string>
-    <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ok"</string>
+    <string name="work_profile_edu_accept" msgid="6069788082535149071">"ရပါပြီ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"အလုပ်ပရိုဖိုင် ခဏရပ်ထားသည်"</string>
     <string name="work_apps_paused_body" msgid="4209084728264328628">"အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
     <string name="work_apps_paused_content_description" msgid="4473292417145736203">"အလုပ်ပရိုဖိုင် ခဏရပ်ထားသည်။ အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 474a452..929e302 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -161,9 +161,9 @@
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"तपाईंका IT एड्मिनले कामसम्पबन्धी एपहरू र डेटा हेर्न सक्छन्"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"अर्को"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"बुझेँ"</string>
-    <string name="work_apps_paused_title" msgid="2389865654362803723">"कार्यालयको प्रोफाइल अस्थायी रूपमा रोक्का गरिएको छ"</string>
-    <string name="work_apps_paused_body" msgid="4209084728264328628">"कामसम्बन्धी एपहरूले तपाईंलाई सूचना पठाउन, तपाईंको डिभाइसको ब्याट्री प्रयोग गर्न वा तपाईंको स्थान हेर्न सक्दैनन्"</string>
-    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"कामसम्बन्धी प्रोफाइल अस्थायी रूपमा रोक्का गरिएको छ। कामसम्बन्धी एपहरूले तपाईंलाई सूचना पठाउन, तपाईंको डिभाइसको ब्याट्री प्रयोग गर्न वा तपाईंको स्थान हेर्न सक्दैनन्"</string>
+    <string name="work_apps_paused_title" msgid="2389865654362803723">"कार्यालयको प्रोफाइल पज गरिएको छ"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"कामसम्बन्धी एपहरूले तपाईंलाई सूचना पठाउन, तपाईंको डिभाइसको ब्याट्री प्रयोग गर्न वा तपाईंको लोकेसन हेर्न सक्दैनन्"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"कामसम्बन्धी प्रोफाइल पज गरिएको छ। कामसम्बन्धी एपहरूले तपाईंलाई सूचना पठाउन, तपाईंको डिभाइसको ब्याट्री प्रयोग गर्न वा तपाईंको लोकेसन हेर्न सक्दैनन्"</string>
     <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"कामसम्बन्धी एपमा ब्याज अङ्कित हुन्छ र तपाईंका IT एड्मिन ती एप हेर्न सक्नुहुन्छ"</string>
     <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"बुझेँ"</string>
     <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"कामसम्बन्धी एपहरू अस्थायी रूपमा रोक्का गर्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index f3c5538..7573102 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -111,7 +111,7 @@
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Toegang tot meldingen vereist"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Als je meldingsstipjes wilt tonen, zet je app-meldingen aan voor <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Instellingen wijzigen"</string>
-    <string name="notification_dots_service_title" msgid="4284221181793592871">"Meldingsstipjes tonen"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Toon meldingsstipjes"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"App-iconen toevoegen aan startscherm"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Voor nieuwe apps"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
@@ -159,8 +159,8 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Volgende"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Werkprofiel is onderbroken"</string>
-    <string name="work_apps_paused_body" msgid="4209084728264328628">"Werk-apps kunnen je geen meldingen sturen, niet je batterij gebruiken en geen toegang krijgen tot je locatie"</string>
-    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Werkprofiel is gepauzeerd. Werk-apps kunnen je geen meldingen sturen, niet je batterij gebruiken en geen toegang krijgen tot je locatie."</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Werk-apps kunnen je geen meldingen sturen, je batterij niet gebruiken en geen toegang krijgen tot je locatie"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Werkprofiel is gepauzeerd. Werk-apps kunnen je geen meldingen sturen, je batterij niet gebruiken en geen toegang krijgen tot je locatie."</string>
     <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Werk-apps hebben badges en zijn zichtbaar voor je IT-beheerder"</string>
     <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
     <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Werk-apps pauzeren"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 3517dab..5c1d498 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -51,12 +51,9 @@
     <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ବ୍ୟକ୍ତିଗତ"</string>
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ୱାର୍କ"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ"</string>
-    <!-- no translation found for widget_education_header (4874760613775913787) -->
-    <skip />
-    <!-- no translation found for widget_education_content (745542879510751525) -->
-    <skip />
-    <!-- no translation found for widget_education_close_button (8676165703104836580) -->
-    <skip />
+    <string name="widget_education_header" msgid="4874760613775913787">"ଉପଯୋଗୀ ସୂଚନା ଆପଣଙ୍କ ପାଖରେ ସହଜରେ ଉପଲବ୍ଧ"</string>
+    <string name="widget_education_content" msgid="745542879510751525">"ଆପଗୁଡ଼ିକୁ ନଖୋଲି ସୂଚନା ପାଇବା ପାଇଁ, ଆପଣ ଆପଣଙ୍କ ମୂଳସ୍କ୍ରିନରେ ୱିଜେଟଗୁଡ଼ିକୁ ଯୋଗ କରିପାରିବେ"</string>
+    <string name="widget_education_close_button" msgid="8676165703104836580">"ବୁଝିଗଲି"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ଆପ୍‌ ଖୋଜନ୍ତୁ"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ଆପ୍‌ ଲୋଡ୍‌ ହେଉଛି..."</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ସହିତ ମେଳ ହେଉଥିବା କୌଣସି ଆପ୍‌ ମିଳିଲା ନାହିଁ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 7b47cde..770f344 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -172,6 +172,6 @@
     <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Wstrzymaj aplikacje służbowe"</string>
     <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Włącz"</string>
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtruj"</string>
-    <string name="work_switch_tip" msgid="808075064383839144">"Wstrzymaj aplikacje służbowe i powiadomienia"</string>
+    <string name="work_switch_tip" msgid="808075064383839144">"Wstrzymaj służbowe aplikacje i powiadomienia"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Niepowodzenie: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 70606a9..6d2f9e5 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -161,10 +161,10 @@
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Рабочие"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Рабочий профиль"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Личные данные скрыты от рабочих приложений и недоступны им."</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Рабочие приложения и данные видны системному администратору."</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Рабочие приложения и их данные видны системному администратору."</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Далее"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ОК"</string>
-    <string name="work_apps_paused_title" msgid="2389865654362803723">"Действие рабочего профиля приостановлено."</string>
+    <string name="work_apps_paused_title" msgid="2389865654362803723">"Рабочий профиль приостановлен"</string>
     <string name="work_apps_paused_body" msgid="4209084728264328628">"Рабочие приложения не могут отправлять уведомления, расходовать заряд батареи и получать доступ к данным о вашем местоположении."</string>
     <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Рабочий профиль приостановлен. Рабочие приложения не могут отправлять уведомления, расходовать заряд батареи и получать доступ к данным о вашем местоположении."</string>
     <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"У рабочих приложений есть специальная пометка. Они видны системному администратору."</string>
@@ -172,6 +172,6 @@
     <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Приостановить рабочие приложения"</string>
     <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Включить"</string>
     <string name="developer_options_filter_hint" msgid="5896817443635989056">"Фильтр"</string>
-    <string name="work_switch_tip" msgid="808075064383839144">"Приостановить рабочие приложения и уведомления"</string>
+    <string name="work_switch_tip" msgid="808075064383839144">"Приостановить рабочие приложения и уведомления от них"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Не удалось выполнить действие (<xliff:g id="WHAT">%1$s</xliff:g>)."</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 08e7bb6..cbb082a 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -165,7 +165,7 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Ďalej"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Dobre"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Pracovný profil je pozastavený"</string>
-    <string name="work_apps_paused_body" msgid="4209084728264328628">"Pracovné aplikácie nemôžu posielať upozornenia, používať batériu ani polohu"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Pracovné aplikácie vám nemôžu posielať upozornenia, používať vašu batériu ani vašu polohu"</string>
     <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Pracovný profil je pozastavený. Pracovné aplikácie nemôžu posielať upozornenia, používať batériu ani polohu."</string>
     <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Pracovné aplikácie majú odznak a zobrazujú sa správcovi IT"</string>
     <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Dobre"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 338aa8b..1054092 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -51,12 +51,9 @@
     <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personale"</string>
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Puna"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"Bisedat"</string>
-    <!-- no translation found for widget_education_header (4874760613775913787) -->
-    <skip />
-    <!-- no translation found for widget_education_content (745542879510751525) -->
-    <skip />
-    <!-- no translation found for widget_education_close_button (8676165703104836580) -->
-    <skip />
+    <string name="widget_education_header" msgid="4874760613775913787">"Informacione të dobishme në majë të gishtave të tu"</string>
+    <string name="widget_education_content" msgid="745542879510751525">"Për të marrë informacione pa i hapur aplikacionet, mund të shtosh miniaplikacione në ekranin bazë"</string>
+    <string name="widget_education_close_button" msgid="8676165703104836580">"E kuptova"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Kërko për aplikacione"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Po ngarkon aplikacionet..."</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nuk u gjet asnjë aplikacion që përputhet me \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -157,8 +154,8 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personale"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Punë"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profili i punës"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Të dhënat personale janë të ndara dhe të fshehura nga aplikacionet e punës"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Aplikacionet e punës dhe të dhënat janë të dukshme për administratorin e teknologjisë së informacionit."</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Të dhënat personale janë të veçuara dhe të fshehura nga aplikacionet e punës"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Aplikacionet e punës dhe të dhënat janë të dukshme për administratorin e teknologjisë së informacionit"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Para"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"E kuptova"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Profili i punës është në pauzë"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index a474661..7a85179 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -51,12 +51,9 @@
     <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"వ్యక్తిగతం"</string>
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ఆఫీస్"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"సంభాషణలు"</string>
-    <!-- no translation found for widget_education_header (4874760613775913787) -->
-    <skip />
-    <!-- no translation found for widget_education_content (745542879510751525) -->
-    <skip />
-    <!-- no translation found for widget_education_close_button (8676165703104836580) -->
-    <skip />
+    <string name="widget_education_header" msgid="4874760613775913787">"మీ చేతివేళ్ల మీద ఉపయోగకరమైన సమాచారం"</string>
+    <string name="widget_education_content" msgid="745542879510751525">"యాప్‌లను తెరవకుండా సమాచారం పొందడానికి, మీరు మీ మొదటి స్క్రీన్‌కు విడ్జెట్‌లను జోడించవచ్చు"</string>
+    <string name="widget_education_close_button" msgid="8676165703104836580">"అర్థమైంది"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"అప్లికేషన్‌లను శోధించండి"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"అప్లికేషన్‌లను లోడ్ చేస్తోంది…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి సరిపోలే అప్లికేషన్‌లేవీ కనుగొనబడలేదు"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 3d5db54..99bdd4b 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -155,7 +155,7 @@
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Ish"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Ish profili"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Shaxsiy maʼlumotlar ishga oid ilovalardan alohida va berkitilgan"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Ishga oid ilovalar va maʼlumotlarni AT administratoringiz koʻra oladi"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Administratoringiz ishga oid ilovalar va maʼlumotlarni koʻra oladi"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Keyingisi"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Ish profili pauzada"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index c7d67af..c0bb552 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -32,7 +32,7 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"㩒兩下之後㩒住,就可以郁小工具或者用自訂操作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d 闊,%2$d 高"</string>
-    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"按住小工具即可將其移至主畫面上任何位置"</string>
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"按住小工具即可隨意在主畫面上移動"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"新增至主畫面"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> 個小工具</item>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 6941c2c..9685d89 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -152,15 +152,15 @@
     <string name="accessibility_close" msgid="2277148124685870734">"關閉"</string>
     <string name="notification_dismissed" msgid="6002233469409822874">"已關閉通知"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"個人"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"公司"</string>
+    <string name="all_apps_work_tab" msgid="4884822796154055118">"工作"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"工作資料夾"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"系統會區隔個人資料與工作資料,因此兩者不會同時顯示"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"你的 IT 管理員可以查看工作應用程式和工作資料"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"繼續"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"我知道了"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"工作資料夾已暫停"</string>
-    <string name="work_apps_paused_body" msgid="4209084728264328628">"工作應用程式將無法傳送通知,也無法存取你的位置資訊。你還可以省下這類應用程式消耗的電量"</string>
-    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"工作資料夾已暫停。工作應用程式將無法傳送通知,也無法存取你的位置資訊。你還可以省下這類應用程式消耗的電量"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"工作應用程式不會消耗電量、無法傳送通知,也無法存取你的位置資訊。"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"系統已暫停使用工作資料夾。在這種情況下,工作應用程式不會消耗電量、無法傳送通知,也無法存取你的位置資訊。"</string>
     <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"你的 IT 管理員可以看見工作應用程式和相關標記"</string>
     <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"我知道了"</string>
     <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"暫停工作應用程式"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index dc33ab8..92deb68 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -57,8 +57,9 @@
             <enum name="folder" value="2" />
             <enum name="widget_section" value="3" />
             <enum name="shortcut_popup" value="4" />
-            <enum name="hero_app" value="5" />
-            <enum name="taskbar" value="6" />
+            <enum name="taskbar" value="5" />
+            <enum name="search_result_tall" value="6" />
+            <enum name="search_result_small" value="7" />
         </attr>
         <attr name="centerVertically" format="boolean" />
     </declare-styleable>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d6a6f43..eccc600 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -97,6 +97,7 @@
     <dimen name="all_apps_header_tab_height">48dp</dimen>
     <dimen name="all_apps_tabs_indicator_height">2dp</dimen>
     <dimen name="all_apps_header_top_padding">36dp</dimen>
+    <dimen name="all_apps_header_bottom_padding">16dp</dimen>
     <dimen name="all_apps_work_profile_tab_footer_top_padding">16dp</dimen>
     <dimen name="all_apps_work_profile_tab_footer_bottom_padding">20dp</dimen>
     <dimen name="all_apps_tabs_vertical_padding">6dp</dimen>
@@ -131,6 +132,7 @@
 
     <dimen name="widget_list_top_bottom_corner_radius">28dp</dimen>
     <dimen name="widget_list_content_corner_radius">4dp</dimen>
+    <dimen name="widget_list_content_joined_corner_radius">0dp</dimen>
 
     <dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
     <dimen name="widget_list_entry_bottom_margin">2dp</dimen>
@@ -295,6 +297,10 @@
 
 <!-- Taskbar related (placeholders to compile in Launcher3 without Quickstep) -->
     <dimen name="taskbar_size">0dp</dimen>
+    <dimen name="qsb_widget_height">0dp</dimen>
+    <dimen name="taskbar_icon_size">44dp</dimen>
+    <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
+    <dimen name="taskbar_icon_spacing">8dp</dimen>
 
     <!-- Size of the maximum radius for the enforced rounded rectangles. -->
     <dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
@@ -310,4 +316,7 @@
     <dimen name="grid_visualization_rounding_radius">22dp</dimen>
     <dimen name="grid_visualization_cell_spacing">6dp</dimen>
 
+<!-- Search results related parameters -->
+    <dimen name="search_row_icon_size">48dp</dimen>
+    <dimen name="search_row_small_icon_size">32dp</dimen>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 0c389aa..86dcf4c 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -315,5 +315,6 @@
 
     <style name="AddItemActivityTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
         <item name="widgetsTheme">@style/WidgetContainerTheme</item>
+        <item name="android:windowLightStatusBar">true</item>
     </style>
 </resources>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index c1f3ac5..5d9797f 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -5,6 +5,8 @@
 import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT;
 import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH;
 import static com.android.launcher3.Utilities.ATLEAST_S;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RESIZE_COMPLETED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RESIZE_STARTED;
 import static com.android.launcher3.views.BaseDragLayer.LAYOUT_X;
 import static com.android.launcher3.views.BaseDragLayer.LAYOUT_Y;
 
@@ -31,6 +33,10 @@
 
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.PendingRequestArgs;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
@@ -96,6 +102,8 @@
     private final IntRange mDeltaYRange = new IntRange();
     private final IntRange mBaselineY = new IntRange();
 
+    private final InstanceId logInstanceId = new InstanceIdSequence().newInstanceId();
+
     private boolean mLeftBorderActive;
     private boolean mRightBorderActive;
     private boolean mTopBorderActive;
@@ -222,12 +230,21 @@
         mReconfigureButton = (ImageButton) findViewById(R.id.widget_reconfigure_button);
         if (info.isReconfigurable()) {
             mReconfigureButton.setVisibility(VISIBLE);
-            mReconfigureButton.setOnClickListener(view -> mLauncher
+            mReconfigureButton.setOnClickListener(view -> {
+                mLauncher.setWaitingForResult(
+                        PendingRequestArgs.forWidgetInfo(
+                                mWidgetView.getAppWidgetId(),
+                                // Widget add handler is null since we're reconfiguring an existing
+                                // widget.
+                                /* widgetHandler= */ null,
+                                (ItemInfo) mWidgetView.getTag()));
+                mLauncher
                     .getAppWidgetHost()
                     .startConfigActivity(
                             mLauncher,
                             mWidgetView.getAppWidgetId(),
-                            Launcher.REQUEST_RECONFIGURE_APPWIDGET));
+                            Launcher.REQUEST_RECONFIGURE_APPWIDGET);
+            });
         }
 
         // When we create the resize frame, we first mark all cells as unoccupied. The appropriate
@@ -235,6 +252,12 @@
         // frame is dismissed.
         mCellLayout.markCellsAsUnoccupiedForView(mWidgetView);
 
+        mLauncher.getStatsLogManager()
+                .logger()
+                .withInstanceId(logInstanceId)
+                .withItemInfo((ItemInfo) mWidgetView.getTag())
+                .log(LAUNCHER_WIDGET_RESIZE_STARTED);
+
         setOnKeyListener(this);
     }
 
@@ -482,6 +505,11 @@
 
         // We are done with resizing the widget. Save the widget size & position to LauncherModel
         resizeWidgetIfNeeded(true);
+        mLauncher.getStatsLogManager()
+                .logger()
+                .withInstanceId(logInstanceId)
+                .withItemInfo((ItemInfo) mWidgetView.getTag())
+                .log(LAUNCHER_WIDGET_RESIZE_COMPLETED);
     }
 
     private void onTouchUp() {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index d49dd73..f800cf6 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -80,8 +80,9 @@
     private static final int DISPLAY_WORKSPACE = 0;
     private static final int DISPLAY_ALL_APPS = 1;
     private static final int DISPLAY_FOLDER = 2;
-    private static final int DISPLAY_HERO_APP = 5;
-    protected static final int DISPLAY_TASKBAR = 6;
+    protected static final int DISPLAY_TASKBAR = 5;
+    private static final int DISPLAY_SEARCH_RESULT = 6;
+    private static final int DISPLAY_SEARCH_RESULT_SMALL = 7;
 
     private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
     private static final float HIGHLIGHT_SCALE = 1.16f;
@@ -187,8 +188,11 @@
             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
             setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
             defaultIconSize = grid.folderChildIconSizePx;
-        } else if (mDisplay == DISPLAY_HERO_APP) {
-            defaultIconSize = grid.allAppsIconSizePx;
+        } else if (mDisplay == DISPLAY_SEARCH_RESULT) {
+            defaultIconSize = getResources().getDimensionPixelSize(R.dimen.search_row_icon_size);
+        } else if (mDisplay == DISPLAY_SEARCH_RESULT_SMALL) {
+            defaultIconSize = getResources().getDimensionPixelSize(
+                    R.dimen.search_row_small_icon_size);
         } else if (mDisplay == DISPLAY_TASKBAR) {
             defaultIconSize = grid.iconSizePx;
         } else {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index e9245b0..2da7ac3 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.icons.IconNormalizer;
+import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.WindowBounds;
@@ -54,6 +55,8 @@
 public class DeviceProfile {
 
     private static final int DEFAULT_DOT_SIZE = 100;
+    // Ratio of empty space, qsb should take up to appear visually centered.
+    private static final float QSB_CENTER_FACTOR = .325f;
 
     public final InvariantDeviceProfile inv;
     private final Info mInfo;
@@ -156,6 +159,7 @@
     // Start is the side next to the nav bar, end is the side next to the workspace
     public final int hotseatBarSidePaddingStartPx;
     public final int hotseatBarSidePaddingEndPx;
+    public final int hotseatQsbHeight;
 
     // All apps
     public int allAppsOpenVerticalTranslate;
@@ -240,6 +244,7 @@
         mMetrics = context.getResources().getDisplayMetrics();
         final Resources res = context.getResources();
 
+        hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
         isTaskbarPresent = isTablet && FeatureFlags.ENABLE_TASKBAR.get();
         if (isTaskbarPresent) {
             // Taskbar will be added later, but provides bottom insets that we should subtract
@@ -740,7 +745,10 @@
         }
     }
 
-    public Rect getHotseatLayoutPadding() {
+    /**
+     * Returns the padding for hotseat view
+     */
+    public Rect getHotseatLayoutPadding(Context context) {
         if (isVerticalBarLayout()) {
             if (isSeascape()) {
                 mHotseatPadding.set(mInsets.left + hotseatBarSidePaddingStartPx,
@@ -749,6 +757,30 @@
                 mHotseatPadding.set(hotseatBarSidePaddingEndPx, mInsets.top,
                         mInsets.right + hotseatBarSidePaddingStartPx, mInsets.bottom);
             }
+        } else if (isTaskbarPresent) {
+            int hotseatHeight = workspacePadding.bottom + taskbarSize;
+            int taskbarOffset = getTaskbarOffsetY();
+            int hotseatTopDiff = hotseatHeight - taskbarSize - taskbarOffset;
+
+            int startOffset = ApiWrapper.getHotseatStartOffset(context);
+            int requiredWidth = iconSizePx * numShownHotseatIcons;
+
+            Resources res = context.getResources();
+            float taskbarIconSize = res.getDimension(R.dimen.taskbar_icon_size);
+            float taskbarIconSpacing = 2 * res.getDimension(R.dimen.taskbar_icon_spacing);
+            int maxSize = (int) (requiredWidth
+                    * (taskbarIconSize + taskbarIconSpacing) / taskbarIconSize);
+            int hotseatSize = Math.min(maxSize, availableWidthPx - startOffset);
+            int sideSpacing = (availableWidthPx - hotseatSize) / 2;
+            mHotseatPadding.set(sideSpacing, hotseatTopDiff, sideSpacing, taskbarOffset);
+
+            if (startOffset > sideSpacing) {
+                int diff = Utilities.isRtl(context.getResources())
+                        ? sideSpacing - startOffset
+                        : startOffset - sideSpacing;
+                mHotseatPadding.left += diff;
+                mHotseatPadding.right -= diff;
+            }
         } else {
             // We want the edges of the hotseat to line up with the edges of the workspace, but the
             // icons in the hotseat are a different size, and so don't line up perfectly. To account
@@ -769,6 +801,24 @@
     }
 
     /**
+     * Returns the number of pixels the QSB is translated from the bottom of the screen.
+     */
+    public int getQsbOffsetY() {
+        int freeSpace = isTaskbarPresent
+                ? workspacePadding.bottom
+                : hotseatBarSizePx - hotseatCellHeightPx - hotseatQsbHeight;
+        return (int) (freeSpace * QSB_CENTER_FACTOR)
+                + (isTaskbarPresent ? taskbarSize : getInsets().bottom);
+    }
+
+    /**
+     * Returns the number of pixels the taskbar is translated from the bottom of the screen.
+     */
+    public int getTaskbarOffsetY() {
+        return (getQsbOffsetY() - taskbarSize) / 2;
+    }
+
+    /**
      * @return the bounds for which the open folders should be contained within
      */
     public Rect getAbsoluteOpenFolderBounds() {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index ff380ce..b3ae15e 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -49,8 +49,6 @@
     private final View mQsb;
     private final int mQsbHeight;
 
-    private final int mTaskbarViewHeight;
-
     public Hotseat(Context context) {
         this(context, null);
     }
@@ -63,10 +61,9 @@
         super(context, attrs, defStyle);
 
         mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
-        mQsbHeight = mQsb.getLayoutParams().height;
         addView(mQsb);
 
-        mTaskbarViewHeight = context.getResources().getDimensionPixelSize(R.dimen.taskbar_size);
+        mQsbHeight = getResources().getDimensionPixelSize(R.dimen.qsb_widget_height);
     }
 
     /**
@@ -114,18 +111,13 @@
             lp.gravity = Gravity.BOTTOM;
             lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
             lp.height = (grid.isTaskbarPresent
-                        ? grid.workspacePadding.bottom
+                    ? grid.workspacePadding.bottom
                         : grid.hotseatBarSizePx)
                     + (grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom);
         }
 
-        if (!grid.isTaskbarPresent) {
-            // When taskbar is present, we set the padding separately to ensure a seamless visual
-            // handoff between taskbar and hotseat during drag and drop.
-            Rect padding = grid.getHotseatLayoutPadding();
-            setPadding(padding.left, padding.top, padding.right, padding.bottom);
-        }
-
+        Rect padding = grid.getHotseatLayoutPadding(getContext());
+        setPadding(padding.left, padding.top, padding.right, padding.bottom);
         setLayoutParams(lp);
         InsettableFrameLayout.dispatchInsets(this, insets);
     }
@@ -193,31 +185,12 @@
         int left = (r - l - qsbWidth) / 2;
         int right = left + qsbWidth;
 
-        int bottom = b - t - getQsbOffsetY();
+        int bottom = b - t - mActivity.getDeviceProfile().getQsbOffsetY();
         int top = bottom - mQsbHeight;
         mQsb.layout(left, top, right, bottom);
     }
 
     /**
-     * Returns the number of pixels the QSB is translated from the bottom of the screen.
-     */
-    private int getQsbOffsetY() {
-        DeviceProfile dp = mActivity.getDeviceProfile();
-        int freeSpace = dp.isTaskbarPresent
-                ? dp.workspacePadding.bottom
-                : dp.hotseatBarSizePx - dp.hotseatCellHeightPx - mQsbHeight;
-        return (int) (freeSpace * QSB_CENTER_FACTOR)
-                + (dp.isTaskbarPresent ? dp.taskbarSize : dp.getInsets().bottom);
-    }
-
-    /**
-     * Returns the number of pixels the taskbar is translated from the bottom of the screen.
-     */
-    public int getTaskbarOffsetY() {
-        return (getQsbOffsetY() - mTaskbarViewHeight) / 2;
-    }
-
-    /**
      * Sets the alpha value of just our ShortcutAndWidgetContainer.
      */
     public void setIconsAlpha(float alpha) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3e0e2f8..0db435b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -45,6 +45,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RECONFIGURED;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING;
@@ -682,6 +683,7 @@
                 completeAddAppWidget(appWidgetId, info, null, null);
                 break;
             case REQUEST_RECONFIGURE_APPWIDGET:
+                mStatsLogManager.logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED);
                 completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED);
                 break;
             case REQUEST_BIND_PENDING_APPWIDGET: {
@@ -2809,13 +2811,6 @@
         return new float[] {NO_SCALE, NO_OFFSET};
     }
 
-    /**
-     * @see LauncherState#getTaskbarScale(Launcher)
-     */
-    public float getNormalTaskbarScale() {
-        return 1f;
-    }
-
     public static Launcher getLauncher(Context context) {
         return fromContext(context);
     }
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index dabbdd3..b3d096c 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -99,8 +99,6 @@
         }
         mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
 
-        // TODO: remove listener on terminate
-        FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
         CustomWidgetManager.INSTANCE.get(mContext)
                 .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
 
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 3399ce9..7985ab5 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -181,14 +181,6 @@
         return launcher.getNormalOverviewScaleAndOffset();
     }
 
-    public float getTaskbarScale(Launcher launcher) {
-        return launcher.getNormalTaskbarScale();
-    }
-
-    public float getTaskbarTranslationY(Launcher launcher) {
-        return -launcher.getHotseat().getTaskbarOffsetY();
-    }
-
     public float getOverviewFullscreenProgress() {
         return 0;
     }
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 6ccfa7e..cd06414 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -45,6 +45,7 @@
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PendingRequestArgs;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
 import java.net.URISyntaxException;
@@ -267,6 +268,8 @@
         if (mCurrentAccessibilityAction == RECONFIGURE) {
             int widgetId = getReconfigurableWidgetId(view);
             if (widgetId != INVALID_APPWIDGET_ID) {
+                mLauncher.setWaitingForResult(
+                        PendingRequestArgs.forWidgetInfo(widgetId, null, info));
                 mLauncher.getAppWidgetHost().startConfigActivity(mLauncher, widgetId,
                         REQUEST_RECONFIGURE_APPWIDGET);
             }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index c6c9c9b..d536c3a 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -21,6 +21,7 @@
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
+import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -31,7 +32,6 @@
 import android.util.SparseIntArray;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.WindowInsets;
 
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -41,6 +41,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 
 import java.util.ArrayList;
@@ -189,8 +190,8 @@
             case SCROLL_STATE_DRAGGING:
                 mgr.logger().sendToInteractionJankMonitor(
                         LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
-                requestFocus();
-                getWindowInsetsController().hide(WindowInsets.Type.ime());
+                hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
+                        getApplicationWindowToken());
                 break;
             case SCROLL_STATE_IDLE:
                 mgr.logger().sendToInteractionJankMonitor(
diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java
index eabd283..8dad1b4 100644
--- a/src/com/android/launcher3/anim/AlphaUpdateListener.java
+++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.anim;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.view.View;
@@ -25,7 +26,7 @@
 /**
  * A convenience class to update a view's visibility state after an alpha animation.
  */
-public class AlphaUpdateListener extends AnimationSuccessListener
+public class AlphaUpdateListener extends AnimatorListenerAdapter
         implements AnimatorUpdateListener {
     public static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
 
@@ -41,7 +42,7 @@
     }
 
     @Override
-    public void onAnimationSuccess(Animator animator) {
+    public void onAnimationEnd(Animator animator) {
         updateVisibility(mView);
     }
 
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index cebdc1f..7795dd4 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -111,10 +111,6 @@
             "FOLDER_NAME_MAJORITY_RANKING", true,
             "Suggests folder names based on majority based ranking.");
 
-    public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag(
-            "APP_SEARCH_IMPROVEMENTS", true,
-            "Adds localized title and keyword search and ranking");
-
     public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
             "ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list");
 
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index c67efef..68bed44 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -5,10 +5,10 @@
     public static final int MAX_NUM_ITEMS_IN_PREVIEW = 4;
     private static final int MIN_NUM_ITEMS_IN_PREVIEW = 2;
 
-    private static final float MIN_SCALE = 0.48f;
-    private static final float MAX_SCALE = 0.58f;
-    private static final float MAX_RADIUS_DILATION = 0.15f;
-    private static final float ITEM_RADIUS_SCALE_FACTOR = 1.33f;
+    private static final float MIN_SCALE = 0.44f;
+    private static final float MAX_SCALE = 0.54f;
+    private static final float MAX_RADIUS_DILATION = 0.10f;
+    private static final float ITEM_RADIUS_SCALE_FACTOR = 1.2f;
 
     public static final int EXIT_INDEX = -2;
     public static final int ENTER_INDEX = -3;
@@ -130,10 +130,8 @@
     public float scaleForItem(int numItems) {
         // Scale is determined by the number of items in the preview.
         final float scale;
-        if (numItems <= 2) {
+        if (numItems <= 3) {
             scale = MAX_SCALE;
-        } else if (numItems == 3) {
-            scale = (MAX_SCALE + MIN_SCALE) / 2;
         } else {
             scale = MIN_SCALE;
         }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index e387627..17c1329 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -41,6 +41,7 @@
 import android.graphics.Path;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.os.Build;
 import android.text.InputType;
@@ -67,6 +68,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.core.content.res.ResourcesCompat;
 import androidx.core.graphics.ColorUtils;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -250,6 +252,8 @@
     // so that we can cancel it when starting mColorChangeAnimator.
     private ObjectAnimator mOpenAnimationColorChangeAnimator;
 
+    private GradientDrawable mBackground;
+
     /**
      * Used to inflate the Workspace from XML.
      *
@@ -268,6 +272,12 @@
         // name is complete, we have something to focus on, thus hiding the cursor and giving
         // reliable behavior when clicking the text field (since it will always gain focus on click).
         setFocusableInTouchMode(true);
+
+    }
+
+    @Override
+    public Drawable getBackground() {
+        return mBackground;
     }
 
     @Override
@@ -276,6 +286,9 @@
         final DeviceProfile dp = mActivityContext.getDeviceProfile();
         final int paddingLeftRight = dp.folderContentPaddingLeftRight;
 
+        mBackground = (GradientDrawable) ResourcesCompat.getDrawable(getResources(),
+                R.drawable.round_rect_folder, getContext().getTheme());
+
         mContent = findViewById(R.id.folder_content);
         mContent.setPadding(paddingLeftRight, dp.folderContentPaddingTop, paddingLeftRight, 0);
         mContent.setFolder(this);
@@ -1213,6 +1226,8 @@
         lp.x = left;
         lp.y = top;
 
+        mBackground.setBounds(0, 0, width, height);
+
         if (mColorExtractor != null) {
             mColorExtractor.removeLocations();
             mColorExtractor.setListener(mColorListener);
@@ -1714,14 +1729,16 @@
     }
 
     @Override
-    public void draw(Canvas canvas) {
+    protected void dispatchDraw(Canvas canvas) {
         if (mClipPath != null) {
             int count = canvas.save();
             canvas.clipPath(mClipPath);
-            super.draw(canvas);
+            mBackground.draw(canvas);
             canvas.restoreToCount(count);
+            super.dispatchDraw(canvas);
         } else {
-            super.draw(canvas);
+            mBackground.draw(canvas);
+            super.dispatchDraw(canvas);
         }
     }
 
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 7fbfb89..bd0dbfd 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -37,6 +37,7 @@
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Utilities;
@@ -80,6 +81,8 @@
 
     private ObjectAnimator mBgColorAnimator;
 
+    private DeviceProfile mDeviceProfile;
+
     public FolderAnimationManager(Folder folder, boolean isOpening) {
         mFolder = folder;
         mContent = folder.mContent;
@@ -89,7 +92,8 @@
         mPreviewBackground = mFolderIcon.mBackground;
 
         mContext = folder.getContext();
-        mPreviewVerifier = new FolderGridOrganizer(folder.mActivityContext.getDeviceProfile().inv);
+        mDeviceProfile = folder.mActivityContext.getDeviceProfile();
+        mPreviewVerifier = new FolderGridOrganizer(mDeviceProfile.inv);
 
         mIsOpening = isOpening;
 
@@ -211,8 +215,21 @@
         play(a, getAnimator(mFolder.mContent, SCALE_PROPERTY, initialScale, finalScale));
         play(a, getAnimator(mFolder.mFooter, SCALE_PROPERTY, initialScale, finalScale));
         play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
+
+        // Create reveal animator for the folder background
         play(a, getShape().createRevealAnimator(
                 mFolder, startRect, endRect, finalRadius, !mIsOpening));
+
+        // Create reveal animator for the folder content (capture the top 4 icons 2x2)
+        int width = mContent.getPaddingLeft() + mDeviceProfile.folderCellLayoutBorderSpacingPx
+                + mDeviceProfile.folderCellWidthPx * 2;
+        int height = mContent.getPaddingTop() + mDeviceProfile.folderCellLayoutBorderSpacingPx
+                + mDeviceProfile.folderCellHeightPx * 2;
+        Rect startRect2 = new Rect(0, 0, width, height);
+        play(a, getShape().createRevealAnimator(
+                mFolder.getContent(), startRect2, endRect, finalRadius, !mIsOpening));
+
+
         // Fade in the folder name, as the text can overlap the icons when grid size is small.
         mFolder.mFolderName.setAlpha(mIsOpening ? 0f : 1f);
         play(a, getAnimator(mFolder.mFolderName, View.ALPHA, 0, 1),
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 279c445..6b12d86 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -614,10 +614,7 @@
 
         if (mCurrentPreviewItems.isEmpty() && !mAnimating) return;
 
-        final int saveCount = canvas.save();
-        canvas.clipPath(mBackground.getClipPath());
         mPreviewItemManager.draw(canvas);
-        canvas.restoreToCount(saveCount);
 
         if (!mBackground.drawingDelegated()) {
             mBackground.drawBackgroundStroke(canvas);
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 7fc3740..3d2884a 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -22,6 +22,7 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Path;
 import android.graphics.drawable.Drawable;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
@@ -49,6 +50,7 @@
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.ViewCache;
 import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.ClipPathView;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -57,7 +59,7 @@
 import java.util.function.ToIntFunction;
 import java.util.stream.Collectors;
 
-public class FolderPagedView extends PagedView<PageIndicatorDots> {
+public class FolderPagedView extends PagedView<PageIndicatorDots> implements ClipPathView {
 
     private static final String TAG = "FolderPagedView";
 
@@ -89,6 +91,8 @@
 
     private Folder mFolder;
 
+    private Path mClipPath;
+
     // If the views are attached to the folder or not. A folder should be bound when its
     // animating or is open.
     private boolean mViewsBound = false;
@@ -128,8 +132,16 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        mFocusIndicatorHelper.draw(canvas);
-        super.dispatchDraw(canvas);
+        if (mClipPath != null) {
+            int count = canvas.save();
+            canvas.clipPath(mClipPath);
+            mFocusIndicatorHelper.draw(canvas);
+            super.dispatchDraw(canvas);
+            canvas.restoreToCount(count);
+        } else {
+            mFocusIndicatorHelper.draw(canvas);
+            super.dispatchDraw(canvas);
+        }
     }
 
     /**
@@ -628,4 +640,10 @@
     public int itemsPerPage() {
         return mOrganizer.getMaxItemsPerPage();
     }
+
+    @Override
+    public void setClipPath(Path clipPath) {
+        mClipPath = clipPath;
+        invalidate();
+    }
 }
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 4eab63e..204decb 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -51,6 +51,7 @@
 public class PreviewBackground extends CellLayout.DelegatedCellDrawing {
 
     private static final boolean DRAW_SHADOW = false;
+    private static final boolean DRAW_STROKE = false;
 
     private static final int CONSUMPTION_ANIMATION_DURATION = 100;
 
@@ -303,6 +304,10 @@
     }
 
     public void animateBackgroundStroke() {
+        if (!DRAW_STROKE) {
+            return;
+        }
+
         if (mStrokeAlphaAnimator != null) {
             mStrokeAlphaAnimator.cancel();
         }
@@ -319,6 +324,9 @@
     }
 
     public void drawBackgroundStroke(Canvas canvas) {
+        if (!DRAW_STROKE) {
+            return;
+        }
         mPaint.setColor(setColorAlphaBound(mStrokeColor, mStrokeAlpha));
         mPaint.setStyle(Paint.Style.STROKE);
         mPaint.setStrokeWidth(mStrokeWidth);
@@ -363,7 +371,7 @@
         }
 
         mDrawingDelegate = null;
-        isClipping = true;
+        isClipping = false;
         invalidate();
     }
 
diff --git a/src/com/android/launcher3/graphics/IconShape.java b/src/com/android/launcher3/graphics/IconShape.java
index 2da679c..f82b07e 100644
--- a/src/com/android/launcher3/graphics/IconShape.java
+++ b/src/com/android/launcher3/graphics/IconShape.java
@@ -156,19 +156,43 @@
         }
     }
 
-    public static final class Circle extends SimpleRectShape {
+    public static final class Circle extends PathShape {
 
-        @Override
-        public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
-            canvas.drawCircle(radius + offsetX, radius + offsetY, radius, p);
+        private final float[] mTempRadii = new float[8];
+
+        protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
+                float endRadius, Path outPath) {
+            float r1 = getStartRadius(startRect);
+
+            float[] startValues = new float[] {
+                    startRect.left, startRect.top, startRect.right, startRect.bottom, r1, r1};
+            float[] endValues = new float[] {
+                    endRect.left, endRect.top, endRect.right, endRect.bottom, endRadius, endRadius};
+
+            FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[6]);
+
+            return (anim) -> {
+                float progress = (Float) anim.getAnimatedValue();
+                float[] values = evaluator.evaluate(progress, startValues, endValues);
+                outPath.addRoundRect(
+                        values[0], values[1], values[2], values[3],
+                        getRadiiArray(values[4], values[5]), Path.Direction.CW);
+            };
         }
 
+        private float[] getRadiiArray(float r1, float r2) {
+            mTempRadii[0] = mTempRadii [1] = mTempRadii[2] = mTempRadii[3] =
+                    mTempRadii[6] = mTempRadii[7] = r1;
+            mTempRadii[4] = mTempRadii[5] = r2;
+            return mTempRadii;
+        }
+
+
         @Override
         public void addToPath(Path path, float offsetX, float offsetY, float radius) {
             path.addCircle(radius + offsetX, radius + offsetY, radius, Path.Direction.CW);
         }
 
-        @Override
         protected float getStartRadius(Rect startRect) {
             return startRect.width() / 2f;
         }
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 8e0a388..cd13cd0 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -336,8 +336,7 @@
 
     @Override
     protected String getIconSystemState(String packageName) {
-        return mIconProvider.getSystemStateForPackage(mSystemState, packageName)
-                + ",flags_asi:" + FeatureFlags.APP_SEARCH_IMPROVEMENTS.get();
+        return mIconProvider.getSystemStateForPackage(mSystemState, packageName);
     }
 
     /**
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index d065469..418e46d 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -135,6 +135,12 @@
         @UiEvent(doc = "User tapped or long pressed on widget tray icon inside launcher settings.")
         LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS(464),
 
+        @UiEvent(doc = "User expanded the list of widgets for a single app in the widget picker.")
+        LAUNCHER_WIDGETSTRAY_APP_EXPANDED(818),
+
+        @UiEvent(doc = "User searched for a widget in the widget picker.")
+        LAUNCHER_WIDGETSTRAY_SEARCHED(819),
+
         @UiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
         LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
 
@@ -458,7 +464,16 @@
         LAUNCHER_OVERVIEW_SHARING_TAP_MORE_TO_SHARE_URL(777),
 
         @UiEvent(doc = "User taps the More button to share an image")
-        LAUNCHER_OVERVIEW_SHARING_TAP_MORE_TO_SHARE_IMAGE(778)
+        LAUNCHER_OVERVIEW_SHARING_TAP_MORE_TO_SHARE_IMAGE(778),
+
+        @UiEvent(doc = "User started resizing a widget on their home screen.")
+        LAUNCHER_WIDGET_RESIZE_STARTED(820),
+
+        @UiEvent(doc = "User finished resizing a widget on their home screen.")
+        LAUNCHER_WIDGET_RESIZE_COMPLETED(824),
+
+        @UiEvent(doc = "User reconfigured a widget on their home screen.")
+        LAUNCHER_WIDGET_RECONFIGURED(821)
         ;
 
         // ADD MORE
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index 947f96f..b9387a8 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.util;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.annotation.SuppressLint;
@@ -27,6 +28,7 @@
 import android.view.WindowInsets;
 import android.view.inputmethod.InputMethodManager;
 
+import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.views.ActivityContext;
 
@@ -57,6 +59,8 @@
 
         Message.obtain(HANDLER.get(root.getContext()),
                 MSG_HIDE_KEYBOARD, token).sendToTarget();
+        Launcher.cast(activityContext).getStatsLogManager().logger().log(
+                LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
     }
 
     public static void setOrientationAsync(Activity activity, int orientation) {
diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java
index c6fa98a..c2947c7 100644
--- a/src/com/android/launcher3/views/WidgetsEduView.java
+++ b/src/com/android/launcher3/views/WidgetsEduView.java
@@ -22,8 +22,8 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
-import android.view.View;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
@@ -36,8 +36,6 @@
     private static final int DEFAULT_CLOSE_DURATION = 200;
 
     private Rect mInsets = new Rect();
-    private View mEduView;
-
 
     public WidgetsEduView(Context context, AttributeSet attr) {
         this(context, attr, 0);
@@ -46,7 +44,6 @@
     public WidgetsEduView(Context context, AttributeSet attrs,
             int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mContent = this;
     }
 
     @Override
@@ -62,20 +59,16 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mEduView = findViewById(R.id.edu_view);
+        mContent = findViewById(R.id.edu_view);
         findViewById(R.id.edu_close_button)
                 .setOnClickListener(v -> close(/* animate= */ true));
     }
 
     @Override
     public void setInsets(Rect insets) {
-        int leftInset = insets.left - mInsets.left;
-        int rightInset = insets.right - mInsets.right;
-        int bottomInset = insets.bottom - mInsets.bottom;
         mInsets.set(insets);
-        setPadding(leftInset, getPaddingTop(), rightInset, 0);
-        mEduView.setPaddingRelative(mEduView.getPaddingStart(),
-                mEduView.getPaddingTop(), mEduView.getPaddingEnd(), bottomInset);
+        mContent.setPadding(mContent.getPaddingStart(),
+                mContent.getPaddingTop(), mContent.getPaddingEnd(), insets.bottom);
     }
 
     private void show() {
@@ -90,10 +83,41 @@
 
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
+        int width = r - l;
+        int height = b - t;
+
+        // Lay out the content as center bottom aligned.
+        int contentWidth = mContent.getMeasuredWidth();
+        int contentLeft = (width - contentWidth - mInsets.left - mInsets.right) / 2 + mInsets.left;
+        mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
+                contentLeft + contentWidth, height);
+
         setTranslationShift(mTranslationShift);
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+        int widthUsed;
+        if (mInsets.bottom > 0) {
+            // Extra space between this view and mContent horizontally when the sheet is shown in
+            // portrait mode.
+            widthUsed = mInsets.left + mInsets.right;
+        } else {
+            // Extra space between this view and mContent horizontally when the sheet is shown in
+            // landscape mode.
+            Rect padding = deviceProfile.workspacePadding;
+            widthUsed = Math.max(padding.left + padding.right,
+                    2 * (mInsets.left + mInsets.right));
+        }
+
+        int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
+        measureChildWithMargins(mContent, widthMeasureSpec,
+                widthUsed, heightMeasureSpec, heightUsed);
+        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
+                MeasureSpec.getSize(heightMeasureSpec));
+    }
+
     private void animateOpen() {
         if (mIsOpen || mOpenCloseAnimator.isRunning()) {
             return;
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 8685aae..50ab422 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -41,6 +41,7 @@
 import android.widget.Advanceable;
 import android.widget.RemoteViews;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
@@ -262,6 +263,10 @@
 
         mIsAttachedToWindow = true;
         checkIfAutoAdvance();
+
+        if (mLastLocationRegistered != null) {
+            mColorExtractor.addLocation(List.of(mLastLocationRegistered));
+        }
     }
 
     @Override
@@ -366,7 +371,7 @@
         if (mTempRectF.isEmpty()) {
             return;
         }
-        if (!mTempRectF.equals(mLastLocationRegistered)) {
+        if (!isSameLocation(mTempRectF, mLastLocationRegistered, /* epsilon= */ 1e-6f)) {
             if (mLastLocationRegistered != null) {
                 mColorExtractor.removeLocations();
             }
@@ -375,6 +380,20 @@
         }
     }
 
+    // Compare two location rectangles. Locations are always in the [0;1] range.
+    private static boolean isSameLocation(@NonNull RectF rect1, @Nullable RectF rect2,
+            float epsilon) {
+        if (rect2 == null) return false;
+        return isSameCoordinate(rect1.left, rect2.left, epsilon)
+                && isSameCoordinate(rect1.right, rect2.right, epsilon)
+                && isSameCoordinate(rect1.top, rect2.top, epsilon)
+                && isSameCoordinate(rect1.bottom, rect2.bottom, epsilon);
+    }
+
+    private static boolean isSameCoordinate(float c1, float c2, float epsilon) {
+        return Math.abs(c1 - c2) < epsilon;
+    }
+
     @Override
     public void onColorsChanged(RectF rectF, SparseIntArray colors) {
         // setColorResources will reapply the view, which must happen in the UI thread.
@@ -391,14 +410,6 @@
     protected void onWindowVisibilityChanged(int visibility) {
         super.onWindowVisibilityChanged(visibility);
         maybeRegisterAutoAdvance();
-
-        if (visibility == View.VISIBLE) {
-            if (mLastLocationRegistered != null) {
-                mColorExtractor.addLocation(List.of(mLastLocationRegistered));
-            }
-        } else {
-            mColorExtractor.removeLocations();
-        }
     }
 
     private void checkIfAutoAdvance() {
@@ -481,6 +492,10 @@
             return;
         }
         LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
+        if (info == null) {
+            // This occurs when LauncherAppWidgetHostView is used to render a preview layout.
+            return;
+        }
         // Remove and rebind the current widget (which was inflated in the wrong
         // orientation), but don't delete it from the database
         mLauncher.removeItem(this, info, false  /* deleteFromDb */);
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 7d04d7b..e1999c9 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.CancellationSignal;
@@ -213,12 +214,8 @@
                     return false;
                 }
             };
-            mAppWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1, item.widgetInfo);
-            Rect padding = new Rect();
-            mAppWidgetHostViewPreview.getWidgetInset(mActivity.getDeviceProfile(), padding);
-            mAppWidgetHostViewPreview.setPadding(padding.left, padding.top, padding.right,
-                    padding.bottom);
-            mAppWidgetHostViewPreview.updateAppWidget(/* remoteViews= */ mRemoteViewsPreview);
+            setAppWidgetHostViewPreview(mAppWidgetHostViewPreview, item.widgetInfo,
+                    mRemoteViewsPreview);
             return;
         }
 
@@ -234,16 +231,31 @@
             // rendering a preview layout for work profile apps yet. For non-work profile layout, a
             // proper solution is to use RemoteViews(PackageName, LayoutId).
             launcherAppWidgetProviderInfo.initialLayout = item.widgetInfo.previewLayout;
-            mAppWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1,
-                    launcherAppWidgetProviderInfo);
-            Rect padding = new Rect();
-            mAppWidgetHostViewPreview.getWidgetInset(mActivity.getDeviceProfile(), padding);
-            mAppWidgetHostViewPreview.setPadding(padding.left, padding.top, padding.right,
-                    padding.bottom);
-            mAppWidgetHostViewPreview.updateAppWidget(/* remoteViews= */ null);
+            setAppWidgetHostViewPreview(mAppWidgetHostViewPreview,
+                    launcherAppWidgetProviderInfo, /* remoteViews= */ null);
         }
     }
 
+    private void setAppWidgetHostViewPreview(
+            NavigableAppWidgetHostView appWidgetHostViewPreview,
+            LauncherAppWidgetProviderInfo providerInfo,
+            @Nullable RemoteViews remoteViews) {
+        appWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1, providerInfo);
+        Rect padding;
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+        if (deviceProfile.shouldInsetWidgets()) {
+            padding = new Rect();
+            appWidgetHostViewPreview.getWidgetInset(deviceProfile, padding);
+        } else {
+            padding = deviceProfile.inv.defaultWidgetPadding;
+        }
+        appWidgetHostViewPreview.setPadding(padding.left, padding.top, padding.right,
+                padding.bottom);
+        mPreviewWidth += padding.left + padding.right;
+        mPreviewHeight += padding.top + padding.bottom;
+        appWidgetHostViewPreview.updateAppWidget(remoteViews);
+    }
+
     public WidgetImageView getWidgetView() {
         return mWidgetImage;
     }
@@ -343,8 +355,11 @@
     /** Sets the widget preview image size, in number of cells, and preview scale. */
     public void setPreviewSize(int spanX, int spanY, float previewScale) {
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
-        mPreviewWidth = deviceProfile.cellWidthPx * spanX + mPreviewPadding;
-        mPreviewHeight = deviceProfile.cellHeightPx * spanY + mPreviewPadding;
+        Point cellSize = deviceProfile.getCellSize();
+        mPreviewWidth = cellSize.x * spanX + mPreviewPadding
+                + deviceProfile.cellLayoutBorderSpacingPx * (spanX - 1);
+        mPreviewHeight = cellSize.y * spanY + mPreviewPadding
+                + deviceProfile.cellLayoutBorderSpacingPx * (spanY - 1);
         mPreviewScale = previewScale;
     }
 
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 92f84da..1a58bb0 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.widget.picker;
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
 import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
 
 import android.animation.Animator;
@@ -448,6 +449,7 @@
         if (mIsInSearchMode) return;
         setViewVisibilityBasedOnSearch(/*isInSearchMode= */ true);
         attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView);
+        mActivityContext.getStatsLogManager().logger().log(LAUNCHER_WIDGETSTRAY_SEARCHED);
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 5010629..826c244 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.widget.picker;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_APP_EXPANDED;
+
 import android.content.Context;
 import android.os.Process;
 import android.util.Log;
@@ -33,6 +35,7 @@
 import androidx.recyclerview.widget.RecyclerView.Adapter;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
+import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.icons.IconCache;
@@ -76,6 +79,7 @@
     private static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header;
     private static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header;
 
+    private final Launcher mLauncher;
     private final WidgetsDiffReporter mDiffReporter;
     private final SparseArray<ViewHolderBinder> mViewHolderBinders = new SparseArray<>();
     private final WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
@@ -97,6 +101,7 @@
     public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
             WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
             OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
+        mLauncher = Launcher.getLauncher(context);
         mDiffReporter = new WidgetsDiffReporter(iconCache, this);
         mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(context,
                 layoutInflater, iconClickListener, iconLongClickListener,
@@ -271,6 +276,7 @@
             // Scroll the layout manager to the header position to keep it anchored to the same
             // position.
             scrollToPositionAndMaintainOffset(getSelectedHeaderPosition());
+            mLauncher.getStatsLogManager().logger().log(LAUNCHER_WIDGETSTRAY_APP_EXPANDED);
         } else if (packageUserKey.equals(mWidgetsContentVisiblePackageUserKey)) {
             OptionalInt previouslySelectedPosition = getSelectedHeaderPosition();
 
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawables.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawables.java
new file mode 100644
index 0000000..b3bb544
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListDrawables.java
@@ -0,0 +1,60 @@
+/*
+ * 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.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
+
+/** Helper class for creating drawables to use as background for list elements. */
+final class WidgetsListDrawables {
+
+    private WidgetsListDrawables() {}
+
+    /** Creates a list background drawable with the specified radii. */
+    static Drawable createListBackgroundDrawable(
+            Context context,
+            float topRadius,
+            float bottomRadius) {
+        GradientDrawable backgroundMask = new GradientDrawable();
+        backgroundMask.setColor(context.getColorStateList(R.color.surface));
+        backgroundMask.setShape(GradientDrawable.RECTANGLE);
+
+        backgroundMask.setCornerRadii(
+                new float[]{
+                        topRadius,
+                        topRadius,
+                        topRadius,
+                        topRadius,
+                        bottomRadius,
+                        bottomRadius,
+                        bottomRadius,
+                        bottomRadius
+                });
+
+        return new RippleDrawable(
+                /* color= */ ColorStateList.valueOf(
+                        Themes.getAttrColor(context, android.R.attr.colorControlHighlight)),
+                /* content= */ backgroundMask,
+                /* mask= */ backgroundMask);
+    }
+
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index 41aa437..fece359 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -60,6 +60,9 @@
     @Nullable private Drawable mIconDrawable;
     private final int mIconSize;
     private final int mBottomMarginSize;
+    private final float mTopBottomCornerRadius;
+    private final float mMiddleCornerRadius;
+    private final float mJoinedCornerRadius;
 
     private ImageView mAppIcon;
     private TextView mTitle;
@@ -87,6 +90,12 @@
                 grid.iconSizePx);
         mBottomMarginSize =
                 getResources().getDimensionPixelSize(R.dimen.widget_list_entry_bottom_margin);
+        mTopBottomCornerRadius =
+                getResources().getDimension(R.dimen.widget_list_top_bottom_corner_radius);
+        mMiddleCornerRadius =
+                getResources().getDimension(R.dimen.widget_list_content_corner_radius);
+        mJoinedCornerRadius =
+                getResources().getDimension(R.dimen.widget_list_content_joined_corner_radius);
     }
 
     @Override
@@ -254,6 +263,20 @@
         verifyHighRes();
     }
 
+    /** Updates the list to have a background drawable with the appropriate corner radii. */
+    @UiThread
+    public void updateListBackground(boolean isFirst, boolean isLast, boolean isExpanded) {
+        float topRadius = isFirst ? mTopBottomCornerRadius : mMiddleCornerRadius;
+        float bottomRadius = isLast
+                ? mTopBottomCornerRadius
+                : isExpanded
+                        ? mJoinedCornerRadius
+                        : mMiddleCornerRadius;
+        setBackground(
+                WidgetsListDrawables.createListBackgroundDrawable(
+                        getContext(), topRadius, bottomRadius));
+    }
+
     private void setTitles(WidgetsListSearchHeaderEntry entry) {
         mTitle.setText(entry.mPkgItem.title);
 
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
index e57f4d8..22d6d22 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
@@ -52,15 +52,10 @@
     public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data,
             int position) {
         WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
-        if (mWidgetsListAdapter.getItemCount() == 1) {
-            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_single_item_ripple);
-        } else if (position == 0) {
-            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_top_ripple);
-        } else if (position == mWidgetsListAdapter.getItemCount() - 1) {
-            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_bottom_ripple);
-        } else {
-            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_middle_ripple);
-        }
+        widgetsListHeader.updateListBackground(
+                /* isFirst= */ position == 0,
+                /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
+                /* isExpanded= */ data.isWidgetListShown());
         widgetsListHeader.applyFromItemInfoWithIcon(data);
         widgetsListHeader.setExpanded(data.isWidgetListShown());
         widgetsListHeader.setOnExpandChangeListener(isExpanded ->
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
index b98f5e1..d5e03a4 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
@@ -53,15 +53,10 @@
     public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder,
             WidgetsListSearchHeaderEntry data, int position) {
         WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
-        if (mWidgetsListAdapter.getItemCount() == 1) {
-            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_single_item_ripple);
-        } else if (position == 0) {
-            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_top_ripple);
-        } else if (position == mWidgetsListAdapter.getItemCount() - 1) {
-            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_bottom_ripple);
-        } else {
-            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_middle_ripple);
-        }
+        widgetsListHeader.updateListBackground(
+                /* isFirst= */ position == 0,
+                /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
+                /* isExpanded= */ data.isWidgetListShown());
         widgetsListHeader.applyFromItemInfoWithIcon(data);
         widgetsListHeader.setExpanded(data.isWidgetListShown());
         widgetsListHeader.setOnExpandChangeListener(isExpanded ->
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index c3eda13..8e310c5 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.widget.picker;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -51,6 +52,9 @@
     private final OnLongClickListener mIconLongClickListener;
     private final WidgetPreviewLoader mWidgetPreviewLoader;
     private final WidgetsListAdapter mWidgetsListAdapter;
+    private final float mTopBottomCornerRadius;
+    private final float mMiddleCornerRadius;
+    private final float mJoinedCornerRadius;
     private boolean mApplyBitmapDeferred = false;
 
     public WidgetsListTableViewHolderBinder(
@@ -65,6 +69,13 @@
         mIconLongClickListener = iconLongClickListener;
         mWidgetPreviewLoader = widgetPreviewLoader;
         mWidgetsListAdapter = listAdapter;
+        Resources resources = context.getResources();
+        mTopBottomCornerRadius =
+                resources.getDimension(R.dimen.widget_list_top_bottom_corner_radius);
+        mMiddleCornerRadius =
+                resources.getDimension(R.dimen.widget_list_content_corner_radius);
+        mJoinedCornerRadius =
+                resources.getDimension(R.dimen.widget_list_content_joined_corner_radius);
     }
 
     /**
@@ -100,13 +111,14 @@
                     entry.mWidgets.size(), table.getChildCount()));
         }
 
-        if (position == mWidgetsListAdapter.getItemCount() - 1) {
-            table.setBackgroundResource(R.drawable.widgets_list_bottom_ripple);
-        } else {
-            // WidgetsListContentEntry is never shown in position 0. There must be a header above
-            // it.
-            table.setBackgroundResource(R.drawable.widgets_list_middle_ripple);
-        }
+        // The content is always joined to an expanded header above.
+        float topRadius = mJoinedCornerRadius;
+        float bottomRadius = position == mWidgetsListAdapter.getItemCount() - 1
+                ? mTopBottomCornerRadius
+                : mMiddleCornerRadius;
+        table.setBackgroundDrawable(
+                WidgetsListDrawables.createListBackgroundDrawable(
+                        holder.itemView.getContext(), topRadius, bottomRadius));
 
         List<ArrayList<WidgetItem>> widgetItemsTable =
                 WidgetsTableUtils.groupWidgetItemsIntoTable(entry.mWidgets, mMaxSpansPerRow);
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index 4407fe1..c606861 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.uioverrides;
 
 import android.app.Person;
+import android.content.Context;
 import android.content.pm.ShortcutInfo;
 import android.view.Display;
 
@@ -36,4 +37,11 @@
     public static boolean isInternalDisplay(Display display) {
         return display.getDisplayId() == Display.DEFAULT_DISPLAY;
     }
+
+    /**
+     * Returns the minimum space that should be left empty at the start of hotseat
+     */
+    public static int getHotseatStartOffset(Context context) {
+        return 0;
+    }
 }
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
index 55e5744..67f3902 100644
--- a/tests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -22,6 +22,8 @@
 
 import androidx.test.uiautomator.UiDevice;
 
+import org.junit.Assert;
+
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -48,7 +50,10 @@
         in.close();
         out.close();
 
-        UiDevice.getInstance(getInstrumentation()).executeShellCommand("pm install " + apkFilename);
+        final String result = UiDevice.getInstance(getInstrumentation())
+                .executeShellCommand("pm install " + apkFilename);
+        Assert.assertTrue("Failed to install wellbeing test apk; make sure the device is rooted",
+                "Success".equals(result.replaceAll("\\s+", "")));
     }
 
     public static void uninstallDummyApp() throws IOException {