Merge "Measure WidgetsBottomPicker again when the number of spans changes" into sc-dev
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index e6333d1..4503e30 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -25,6 +25,7 @@
 import android.animation.ObjectAnimator;
 import android.os.IBinder;
 import android.util.FloatProperty;
+import android.view.CrossWindowBlurListeners;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewRootImpl;
@@ -41,6 +42,8 @@
 import com.android.systemui.shared.system.BlurUtils;
 import com.android.systemui.shared.system.WallpaperManagerCompat;
 
+import java.util.function.Consumer;
+
 /**
  * Controls blur and wallpaper zoom, for the Launcher surface only.
  */
@@ -96,11 +99,17 @@
                 }
             };
 
+    private final Consumer<Boolean> mCrossWindowBlurListener = (enabled) -> {
+        mCrossWindowBlursEnabled = enabled;
+        dispatchTransactionSurface();
+    };
+
     private final Launcher mLauncher;
     /**
      * Blur radius when completely zoomed out, in pixels.
      */
     private int mMaxBlurRadius;
+    private boolean mCrossWindowBlursEnabled;
     private WallpaperManagerCompat mWallpaperManager;
     private SurfaceControl mSurface;
     /**
@@ -123,6 +132,7 @@
             mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
             mWallpaperManager = new WallpaperManagerCompat(mLauncher);
         }
+
         if (mLauncher.getRootView() != null && mOnAttachListener == null) {
             mOnAttachListener = new View.OnAttachStateChangeListener() {
                 @Override
@@ -132,13 +142,20 @@
                     if (windowToken != null) {
                         mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
                     }
+                    CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(),
+                            mCrossWindowBlurListener);
                 }
 
                 @Override
                 public void onViewDetachedFromWindow(View view) {
+                    CrossWindowBlurListeners.getInstance().removeListener(mCrossWindowBlurListener);
                 }
             };
             mLauncher.getRootView().addOnAttachStateChangeListener(mOnAttachListener);
+            if (mLauncher.getRootView().isAttachedToWindow()) {
+                CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(),
+                        mCrossWindowBlurListener);
+            }
         }
     }
 
@@ -220,7 +237,8 @@
             boolean isOverview = mLauncher.isInState(LauncherState.OVERVIEW);
             boolean opaque = mLauncher.getScrimView().isFullyOpaque() && !isOverview;
 
-            int blur = opaque || isOverview ? 0 : (int) (mDepth * mMaxBlurRadius);
+            int blur = opaque || isOverview || !mCrossWindowBlursEnabled
+                    ? 0 : (int) (mDepth * mMaxBlurRadius);
             new SurfaceControl.Transaction()
                     .setBackgroundBlurRadius(mSurface, blur)
                     .setOpaque(mSurface, opaque)
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 4d47ef1..d511e6d 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -230,6 +230,7 @@
 
     // Used to control launcher components throughout the swipe gesture.
     private AnimatorControllerWithResistance mLauncherTransitionController;
+    private boolean mHasEndedLauncherTransition;
 
     private AnimationFactory mAnimationFactory = (t) -> { };
 
@@ -603,11 +604,11 @@
 
     /**
      * We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME
-     * (it has its own animation).
+     * (it has its own animation) or if we explicitly ended the controller already.
      * @return Whether we can create the launcher controller or update its progress.
      */
     private boolean canCreateNewOrUpdateExistingLauncherTransitionController() {
-        return mGestureState.getEndTarget() != HOME;
+        return mGestureState.getEndTarget() != HOME && !mHasEndedLauncherTransition;
     }
 
     @Override
@@ -1421,6 +1422,8 @@
     }
 
     private void endLauncherTransitionController() {
+        mHasEndedLauncherTransition = true;
+
         if (mLauncherTransitionController != null) {
             // End the animation, but stay at the same visual progress.
             mLauncherTransitionController.getNormalController().dispatchSetInterpolator(
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index c9b68df..84f7e83 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -64,6 +64,7 @@
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.BinderThread;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 import androidx.annotation.WorkerThread;
@@ -303,10 +304,10 @@
     private RecentsAnimationDeviceState mDeviceState;
     private TaskAnimationManager mTaskAnimationManager;
 
-    private InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
-    private InputConsumer mConsumer = InputConsumer.NO_OP;
+    private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
+    private @NonNull InputConsumer mConsumer = InputConsumer.NO_OP;
     private Choreographer mMainChoreographer;
-    private InputConsumer mResetGestureInputConsumer;
+    private @Nullable ResetGestureInputConsumer mResetGestureInputConsumer;
     private GestureState mGestureState = DEFAULT_STATE;
 
     private InputMonitorCompat mInputMonitorCompat;
@@ -635,7 +636,7 @@
                 // launched while device is locked even after exiting direct boot mode (e.g. camera).
                 return createDeviceLockedInputConsumer(newGestureState);
             } else {
-                return mResetGestureInputConsumer;
+                return getDefaultInputConsumer();
             }
         }
 
@@ -644,7 +645,7 @@
         InputConsumer base = canStartSystemGesture
                 || previousGestureState.isRecentsAnimationRunning()
                         ? newBaseConsumer(previousGestureState, newGestureState, event)
-                        : mResetGestureInputConsumer;
+                        : getDefaultInputConsumer();
         if (mDeviceState.isGesturalNavMode()) {
             handleOrientationSetup(base);
         }
@@ -698,7 +699,7 @@
             }
         } else {
             if (mDeviceState.isScreenPinningActive()) {
-                base = mResetGestureInputConsumer;
+                base = getDefaultInputConsumer();
             }
 
             if (mDeviceState.canTriggerOneHandedAction(event)) {
@@ -740,14 +741,14 @@
             return createOverviewInputConsumer(
                     previousGestureState, gestureState, event, forceOverviewInputConsumer);
         } else if (gestureState.getRunningTask() == null) {
-            return mResetGestureInputConsumer;
+            return getDefaultInputConsumer();
         } else if (previousGestureState.isRunningAnimationToLauncher()
                 || gestureState.getActivityInterface().isResumed()
                 || forceOverviewInputConsumer) {
             return createOverviewInputConsumer(
                     previousGestureState, gestureState, event, forceOverviewInputConsumer);
         } else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) {
-            return mResetGestureInputConsumer;
+            return getDefaultInputConsumer();
         } else {
             return createOtherActivityInputConsumer(gestureState, event);
         }
@@ -775,7 +776,7 @@
             return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager,
                     gestureState, mInputMonitorCompat);
         } else {
-            return mResetGestureInputConsumer;
+            return getDefaultInputConsumer();
         }
     }
 
@@ -784,7 +785,7 @@
             boolean forceOverviewInputConsumer) {
         StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity();
         if (activity == null) {
-            return mResetGestureInputConsumer;
+            return getDefaultInputConsumer();
         }
 
         if (activity.getRootView().hasWindowFocus()
@@ -813,13 +814,7 @@
     }
 
     private void reset() {
-        if (mResetGestureInputConsumer != null) {
-            mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
-        } else {
-            // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
-            // NO_OP until then (we never want these to be null).
-            mConsumer = mUncheckedConsumer = InputConsumer.NO_OP;
-        }
+        mConsumer = mUncheckedConsumer = getDefaultInputConsumer();
         mGestureState = DEFAULT_STATE;
         // By default, use batching of the input events, but check receiver before using in the rare
         // case that the monitor was disposed before the swipe settled
@@ -828,6 +823,19 @@
         }
     }
 
+    /**
+     * Returns the {@link ResetGestureInputConsumer} if user is unlocked, else NO_OP.
+     */
+    private @NonNull InputConsumer getDefaultInputConsumer() {
+        if (mResetGestureInputConsumer != null) {
+            return mResetGestureInputConsumer;
+        } else {
+            // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
+            // NO_OP until then (we never want these to be null).
+            return InputConsumer.NO_OP;
+        }
+    }
+
     private void preloadOverview(boolean fromInit) {
         if (!mDeviceState.isUserUnlocked()) {
             return;
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 719cb0a..b1c9ed0 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -151,6 +151,7 @@
         private Optional<ToState> mToState = Optional.empty();
         private Optional<String> mEditText = Optional.empty();
         private SliceItem mSliceItem;
+        private LauncherAtom.Slice mSlice;
 
         StatsCompatLogger(Context context) {
             mContext = context;
@@ -193,7 +194,7 @@
         @Override
         public StatsLogger withContainerInfo(ContainerInfo containerInfo) {
             checkState(mItemInfo == DEFAULT_ITEM_INFO,
-                        "ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
+                    "ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
             this.mContainerInfo = Optional.of(containerInfo);
             return this;
         }
@@ -218,9 +219,21 @@
 
         @Override
         public StatsLogger withSliceItem(@NonNull SliceItem sliceItem) {
+            checkState(mItemInfo == DEFAULT_ITEM_INFO && mSlice == null,
+                    "ItemInfo, Slice and SliceItem are mutual exclusive; cannot set more than one"
+                            + " of them.");
             this.mSliceItem = checkNotNull(sliceItem, "expected valid sliceItem but received null");
-            checkState(mItemInfo == DEFAULT_ITEM_INFO,
-                    "ItemInfo and SliceItem are mutual exclusive; cannot log both.");
+            return this;
+        }
+
+        @Override
+        public StatsLogger withSlice(LauncherAtom.Slice slice) {
+            checkState(mItemInfo == DEFAULT_ITEM_INFO && mSliceItem == null,
+                    "ItemInfo, Slice and SliceItem are mutual exclusive; cannot set more than one"
+                            + " of them.");
+            checkNotNull(slice, "expected valid slice but received null");
+            checkNotNull(slice.getUri(), "expected valid slice uri but received null");
+            this.mSlice = slice;
             return this;
         }
 
@@ -231,13 +244,16 @@
             }
             LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
 
-            if (mSliceItem != null) {
+            if (mSlice == null && mSliceItem != null) {
+                mSlice = LauncherAtom.Slice.newBuilder().setUri(
+                        mSliceItem.getSlice().getUri().toString()).build();
+            }
+
+            if (mSlice != null) {
                 Executors.MODEL_EXECUTOR.execute(
                         () -> {
                             LauncherAtom.ItemInfo.Builder itemInfoBuilder =
-                                    LauncherAtom.ItemInfo.newBuilder().setSlice(
-                                            LauncherAtom.Slice.newBuilder().setUri(
-                                                    mSliceItem.getSlice().getUri().toString()));
+                                    LauncherAtom.ItemInfo.newBuilder().setSlice(mSlice);
                             mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
                             write(event, applyOverwrites(itemInfoBuilder.build()));
                         });
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 97e09dd..8b7af04 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1251,7 +1251,9 @@
 
     private void updateOrientationHandler() {
         // Handle orientation changes.
+        PagedOrientationHandler oldOrientationHandler = mOrientationHandler;
         mOrientationHandler = mOrientationState.getOrientationHandler();
+
         mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
         setLayoutDirection(mIsRtl
                 ? View.LAYOUT_DIRECTION_RTL
@@ -1260,7 +1262,12 @@
                 ? View.LAYOUT_DIRECTION_LTR
                 : View.LAYOUT_DIRECTION_RTL);
         mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
-        mActivity.getDragLayer().recreateControllers();
+
+        if (!mOrientationHandler.equals(oldOrientationHandler)) {
+            // Changed orientations, update controllers so they intercept accordingly.
+            mActivity.getDragLayer().recreateControllers();
+        }
+
         boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
                 || mOrientationState.getRecentsActivityRotation() != ROTATION_0;
         mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
@@ -2372,13 +2379,12 @@
                 if (success) {
                     if (shouldRemoveTask) {
                         if (taskView.getTask() != null) {
-                            finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
-                                    () -> {
-                                UI_HELPER_EXECUTOR.getHandler().postDelayed(() ->
-                                        ActivityManagerWrapper.getInstance().removeTask(
-                                                taskView.getTask().key.id),
-                                        REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
-                            });
+                            if (LIVE_TILE.get() && taskView.isRunningTask()) {
+                                finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+                                        () -> removeTaskInternal(taskView));
+                            } else {
+                                removeTaskInternal(taskView);
+                            }
                             mActivity.getStatsLogManager().logger()
                                     .withItemInfo(taskView.getItemInfo())
                                     .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
@@ -2424,6 +2430,13 @@
         return anim;
     }
 
+    private void removeTaskInternal(TaskView taskView) {
+        UI_HELPER_EXECUTOR.getHandler().postDelayed(() ->
+                        ActivityManagerWrapper.getInstance().removeTask(
+                                taskView.getTask().key.id),
+                REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
+    }
+
     /**
      * @return {@code true} if one of the task thumbnails would intersect/overlap with the
      *         {@link #mSplitPlaceholderView}
diff --git a/res/color-night-v31/folder_background_dark.xml b/res/color-night-v31/folder_background_dark.xml
new file mode 100644
index 0000000..a5bd636
--- /dev/null
+++ b/res/color-night-v31/folder_background_dark.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_neutral2_50"
+        android:lStar="30" />
+</selector>
diff --git a/res/color-night-v31/popup_color_first.xml b/res/color-night-v31/popup_color_first.xml
new file mode 100644
index 0000000..ba74128
--- /dev/null
+++ b/res/color-night-v31/popup_color_first.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="?attr/popupColorPrimary"
+        android:lStar="20" />
+</selector>
diff --git a/res/color-night-v31/popup_color_second.xml b/res/color-night-v31/popup_color_second.xml
new file mode 100644
index 0000000..efc6205
--- /dev/null
+++ b/res/color-night-v31/popup_color_second.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="?attr/popupColorPrimary"
+        android:lStar="15" />
+</selector>
diff --git a/res/color-night-v31/popup_color_third.xml b/res/color-night-v31/popup_color_third.xml
new file mode 100644
index 0000000..591c7ed
--- /dev/null
+++ b/res/color-night-v31/popup_color_third.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="?attr/popupColorPrimary"
+        android:lStar="10" />
+</selector>
diff --git a/res/color-v31/folder_background_light.xml b/res/color-v31/folder_background_light.xml
new file mode 100644
index 0000000..e3c7e7d
--- /dev/null
+++ b/res/color-v31/folder_background_light.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_neutral1_50"
+        android:lStar="98" />
+</selector>
diff --git a/res/color-v31/popup_color_first.xml b/res/color-v31/popup_color_first.xml
new file mode 100644
index 0000000..28d9155
--- /dev/null
+++ b/res/color-v31/popup_color_first.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="?attr/popupColorPrimary"
+        android:lStar="98" />
+</selector>
diff --git a/res/color-v31/popup_color_second.xml b/res/color-v31/popup_color_second.xml
new file mode 100644
index 0000000..dec562c
--- /dev/null
+++ b/res/color-v31/popup_color_second.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="?attr/popupColorPrimary"
+        android:lStar="95" />
+</selector>
diff --git a/res/color-v31/popup_color_third.xml b/res/color-v31/popup_color_third.xml
new file mode 100644
index 0000000..582232c
--- /dev/null
+++ b/res/color-v31/popup_color_third.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="?attr/popupColorPrimary"
+        android:lStar="90" />
+</selector>
diff --git a/res/color/popup_color_first.xml b/res/color/popup_color_first.xml
new file mode 100644
index 0000000..d9999a2
--- /dev/null
+++ b/res/color/popup_color_first.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:color="?attr/popupColorPrimary" />
+</selector>
diff --git a/res/color/popup_color_second.xml b/res/color/popup_color_second.xml
new file mode 100644
index 0000000..d9999a2
--- /dev/null
+++ b/res/color/popup_color_second.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:color="?attr/popupColorPrimary" />
+</selector>
diff --git a/res/color/popup_color_third.xml b/res/color/popup_color_third.xml
new file mode 100644
index 0000000..d7e9e79
--- /dev/null
+++ b/res/color/popup_color_third.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:color="?attr/popupColorPrimary" />
+</selector>
\ No newline at end of file
diff --git a/res/drawable/work_apps_toggle_background.xml b/res/drawable/work_apps_toggle_background.xml
index b7115f8..a47c8fe 100644
--- a/res/drawable/work_apps_toggle_background.xml
+++ b/res/drawable/work_apps_toggle_background.xml
@@ -18,14 +18,18 @@
         <shape android:shape="rectangle">
             <corners android:radius="@dimen/work_fab_radius" />
             <solid android:color="?android:attr/colorControlHighlight" />
-            <padding android:left="@dimen/work_fab_radius" android:right="@dimen/work_fab_radius" />
+            <padding
+                android:left="@dimen/work_profile_footer_padding"
+                android:right="@dimen/work_profile_footer_padding" />
         </shape>
     </item>
     <item>
         <shape android:shape="rectangle">
             <corners android:radius="@dimen/work_fab_radius" />
             <solid android:color="@color/all_apps_tab_background_selected" />
-            <padding android:left="@dimen/work_fab_radius" android:right="@dimen/work_fab_radius" />
+            <padding
+                android:left="@dimen/work_profile_footer_padding"
+                android:right="@dimen/work_profile_footer_padding" />
         </shape>
     </item>
 </selector>
diff --git a/res/layout/work_apps_edu.xml b/res/layout/work_apps_edu.xml
index 97feb23..919f1b2 100644
--- a/res/layout/work_apps_edu.xml
+++ b/res/layout/work_apps_edu.xml
@@ -42,7 +42,7 @@
 
         <Button
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="@dimen/rounded_button_height"
             android:id="@+id/action_btn"
             android:textColor="?attr/workProfileOverlayTextColor"
             android:text="@string/work_profile_edu_accept"
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index 3819256..02a50ca 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -17,7 +17,7 @@
     android:layout_height="wrap_content"
     android:padding="@dimen/work_edu_card_margin"
     android:orientation="vertical"
-    android:gravity="center">
+    android:gravity="center_horizontal">
 
     <TextView
         style="@style/PrimaryHeadline"
@@ -25,8 +25,7 @@
         android:id="@+id/work_apps_paused_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginTop="8dp"
-        android:layout_marginBottom="8dp"
+        android:layout_marginTop="40dp"
         android:text="@string/work_apps_paused_title"
         android:textAlignment="center"
         android:textSize="20sp" />
@@ -38,12 +37,13 @@
         android:textColor="?attr/workProfileOverlayTextColor"
         android:text="@string/work_apps_paused_body"
         android:textAlignment="center"
-        android:layout_marginBottom="8dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginBottom="24dp"
         android:textSize="16sp" />
 
     <Button
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_height="@dimen/rounded_button_height"
         android:id="@+id/enable_work_apps"
         android:textColor="?attr/workProfileOverlayTextColor"
         android:text="@string/work_apps_enable_btn_text"
diff --git a/res/layout/work_mode_fab.xml b/res/layout/work_mode_fab.xml
index 1771d37..7183817 100644
--- a/res/layout/work_mode_fab.xml
+++ b/res/layout/work_mode_fab.xml
@@ -13,6 +13,7 @@
      limitations under the License.
 -->
 <com.android.launcher3.allapps.WorkModeSwitch xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/TextHeadline"
     android:id="@+id/work_mode_toggle"
     android:layout_alignParentBottom="true"
     android:layout_alignParentEnd="true"
@@ -22,6 +23,7 @@
     android:includeFontPadding="false"
     android:drawableTint="@color/all_apps_tab_text"
     android:textColor="@color/all_apps_tab_text"
+    android:textSize="14sp"
     android:background="@drawable/work_apps_toggle_background"
     android:drawablePadding="16dp"
     android:drawableStart="@drawable/ic_corp_off"
diff --git a/res/values-v29/styles.xml b/res/values-v29/styles.xml
index 8254d48..5370b79 100644
--- a/res/values-v29/styles.xml
+++ b/res/values-v29/styles.xml
@@ -26,8 +26,6 @@
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowShowWallpaper">true</item>
-        <item name="folderTextColor">?attr/workspaceTextColor</item>
-        <item name="isFolderDarkText">?attr/isWorkspaceDarkText</item>
         <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
         <item name="android:enforceStatusBarContrast">false</item>
         <item name="android:enforceNavigationBarContrast">false</item>
diff --git a/res/values-v30/styles.xml b/res/values-v30/styles.xml
index a144363..ec5c113 100644
--- a/res/values-v30/styles.xml
+++ b/res/values-v30/styles.xml
@@ -26,8 +26,6 @@
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowShowWallpaper">true</item>
-        <item name="folderTextColor">?attr/workspaceTextColor</item>
-        <item name="isFolderDarkText">?attr/isWorkspaceDarkText</item>
         <item name="android:windowLayoutInDisplayCutoutMode">always</item>
         <item name="android:enforceStatusBarContrast">false</item>
         <item name="android:enforceNavigationBarContrast">false</item>
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 1785623..71eaa9e 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -39,8 +39,6 @@
     <color name="text_color_tertiary_dark">@android:color/system_neutral2_400</color>
 
     <color name="wallpaper_popup_scrim">@android:color/system_neutral1_900</color>
-    <color name="folder_background_light" android:lstar="98">@android:color/system_neutral1_50</color>
-    <color name="folder_background_dark" android:lstar="30">@android:color/system_neutral2_800</color>
-  
+
     <color name="folder_dot_color">@android:color/system_accent2_50</color>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3dbd760..4f4a389 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -140,8 +140,8 @@
     <string name="long_accessible_way_to_add_shortcut">Double-tap &amp; hold to move a shortcut or use custom actions.</string>
 
     <skip />
-    <!-- Error message when user has filled a home screen -->
-    <string name="out_of_space">No more room on this Home screen.</string>
+    <!-- Error message when a user can't add more apps, widgets, or shortcuts to a Home screen. -->
+    <string name="out_of_space">No room on this Home screen</string>
     <!-- Error message when user has filled the hotseat -->
     <string name="hotseat_out_of_space">No more room in the Favorites tray</string>
 
@@ -394,7 +394,7 @@
     <string name="work_profile_edu_accept">Got it</string>
 
     <!--- heading shown when user opens work apps tab while work apps are paused -->
-    <string name="work_apps_paused_title">Work apps are off</string>
+    <string name="work_apps_paused_title">Work apps are paused</string>
     <!--- body shown when user opens work apps tab while work apps are paused -->
     <string name="work_apps_paused_body">Your work apps can’t send you notifications, use your battery, or access your location</string>
     <!-- content description for paused work apps list -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 1cacc28..871214f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -26,8 +26,6 @@
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowShowWallpaper">true</item>
-        <item name="folderTextColor">?attr/workspaceTextColor</item>
-        <item name="isFolderDarkText">?attr/isWorkspaceDarkText</item>
     </style>
 
     <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
@@ -72,10 +70,6 @@
     </style>
 
     <style name="LauncherTheme.DarkMainColor" parent="@style/LauncherTheme">
-        <item name="folderFillColor">@color/folder_background_dark</item>
-        <item name="folderTextColor">@color/workspace_text_color_light</item>
-        <item name="isFolderDarkText">false</item>
-        <item name="folderHintColor">@color/folder_hint_text_color_light</item>
         <item name="disabledIconAlpha">.254</item>
 
     </style>
@@ -87,12 +81,6 @@
         <item name="workspaceKeyShadowColor">@android:color/transparent</item>
         <item name="isWorkspaceDarkText">true</item>
         <item name="workspaceStatusBarScrim">@null</item>
-        <item name="folderDotColor">@color/folder_dot_color</item>
-        <item name="folderFillColor">@color/folder_background_light</item>
-        <item name="folderIconBorderColor">#FF80868B</item>
-        <item name="folderTextColor">@color/workspace_text_color_dark</item>
-        <item name="isFolderDarkText">true</item>
-        <item name="folderHintColor">@color/folder_hint_text_color_dark</item>
     </style>
 
     <style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
@@ -125,25 +113,17 @@
     </style>
 
     <style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
-        <item name="folderFillColor">@color/folder_background_dark</item>
-        <item name="folderTextColor">@color/workspace_text_color_light</item>
-        <item name="isFolderDarkText">false</item>
-        <item name="folderHintColor">@color/folder_hint_text_color_light</item>
         <item name="disabledIconAlpha">.54</item>
     </style>
 
     <style name="LauncherTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark">
         <item name="android:colorControlHighlight">#19212121</item>
-        <item name="folderFillColor">@color/folder_background_light</item>
         <item name="workspaceTextColor">@color/workspace_text_color_dark</item>
         <item name="workspaceShadowColor">@android:color/transparent</item>
         <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
         <item name="workspaceKeyShadowColor">@android:color/transparent</item>
         <item name="isWorkspaceDarkText">true</item>
         <item name="workspaceStatusBarScrim">@null</item>
-        <item name="folderTextColor">@color/workspace_text_color_dark</item>
-        <item name="isFolderDarkText">true</item>
-        <item name="folderHintColor">@color/folder_hint_text_color_dark</item>
     </style>
 
     <!-- A derivative project can extend these themes to customize the application theme without
diff --git a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
index 2d87957..a6f892c 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
@@ -122,6 +122,8 @@
     @Test
     public void initSpans_minResizeWidthSmallerThanCellWidth_shouldInitializeMinSpansToOne() {
         LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = 100;
+        info.minHeight = 100;
         info.minResizeWidth = 20;
         info.minResizeHeight = 20;
         InvariantDeviceProfile idp = createIDP();
@@ -135,6 +137,8 @@
     @Test
     public void initSpans_minResizeWidthLargerThanCellWidth_shouldInitializeMinSpans() {
         LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = 100;
+        info.minHeight = 100;
         info.minResizeWidth = 80;
         info.minResizeHeight = 80;
         InvariantDeviceProfile idp = createIDP();
@@ -157,6 +161,8 @@
         Mockito.when(dp.shouldInsetWidgets()).thenReturn(true);
 
         LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = CELL_SIZE * 3;
+        info.minHeight = CELL_SIZE * 3;
         info.minResizeWidth = CELL_SIZE * 2 + maxPadding;
         info.minResizeHeight = CELL_SIZE * 2 + maxPadding;
 
@@ -177,6 +183,8 @@
         dp.cellLayoutBorderSpacingPx = maxPadding - 1;
         Mockito.when(dp.shouldInsetWidgets()).thenReturn(false);
         LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = CELL_SIZE * 3;
+        info.minHeight = CELL_SIZE * 3;
         info.minResizeWidth = CELL_SIZE * 2 + maxPadding;
         info.minResizeHeight = CELL_SIZE * 2 + maxPadding;
 
@@ -187,6 +195,22 @@
     }
 
     @Test
+    public void
+            initSpans_minResizeWidthHeightLargerThanMinWidth_shouldUseMinWidthHeightAsMinSpans() {
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = 20;
+        info.minHeight = 20;
+        info.minResizeWidth = 80;
+        info.minResizeHeight = 80;
+        InvariantDeviceProfile idp = createIDP();
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.minSpanX).isEqualTo(1);
+        assertThat(info.minSpanY).isEqualTo(1);
+    }
+
+    @Test
     public void isMinSizeFulfilled_minWidthAndHeightWithinGridSize_shouldReturnTrue() {
         LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
         info.minWidth = 80;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index e9245b0..d301787 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -149,10 +149,11 @@
     // Hotseat
     public final int numShownHotseatIcons;
     public int hotseatCellHeightPx;
+    private final int hotseatExtraVerticalSize;
     // In portrait: size = height, in landscape: size = width
     public int hotseatBarSizePx;
     public final int hotseatBarTopPaddingPx;
-    public int hotseatBarBottomPaddingPx;
+    public final int hotseatBarBottomPaddingPx;
     // 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;
@@ -326,13 +327,9 @@
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
         // Add a bit of space between nav bar and hotseat in vertical bar layout.
         hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
-        int hotseatExtraVerticalSize =
+        hotseatExtraVerticalSize =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
-        hotseatBarSizePx = pxFromDp(inv.iconSize, mMetrics, 1f)
-                + (isVerticalBarLayout()
-                ? (hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx)
-                : (hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx
-                        + (isScalableGrid ? 0 : hotseatExtraVerticalSize)));
+        updateHotseatIconSize(pxFromDp(inv.iconSize, mMetrics, 1f));
 
         overviewTaskMarginPx = res.getDimensionPixelSize(R.dimen.overview_task_margin);
         overviewTaskIconSizePx =
@@ -362,7 +359,6 @@
             extraHotseatBottomPadding = Math.round(paddingHotseatBottom * iconScale);
 
             hotseatBarSizePx += extraHotseatBottomPadding;
-            hotseatBarBottomPaddingPx += extraHotseatBottomPadding;
         } else if (!isVerticalBarLayout() && isPhone && isTallDevice) {
             // We increase the hotseat size when there is extra space.
             // ie. For a display with a large aspect ratio, we can keep the icons on the workspace
@@ -371,7 +367,6 @@
             int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2
                     - workspacePageIndicatorHeight;
             hotseatBarSizePx += extraSpace;
-            hotseatBarBottomPaddingPx += extraSpace;
 
             // Recalculate the available dimensions using the new hotseat size.
             updateAvailableDimensions(res);
@@ -388,6 +383,17 @@
                 new DotRenderer(allAppsIconSizePx, dotPath, DEFAULT_DOT_SIZE);
     }
 
+    private void updateHotseatIconSize(int hotseatIconSizePx) {
+        hotseatCellHeightPx = hotseatIconSizePx;
+        if (isVerticalBarLayout()) {
+            hotseatBarSizePx = hotseatIconSizePx + hotseatBarSidePaddingStartPx
+                    + hotseatBarSidePaddingEndPx;
+        } else {
+            hotseatBarSizePx = hotseatIconSizePx + hotseatBarTopPaddingPx
+                    + hotseatBarBottomPaddingPx + (isScalableGrid ? 0 : hotseatExtraVerticalSize);
+        }
+    }
+
     private void setCellLayoutBorderSpacing(int borderSpacing) {
         cellLayoutBorderSpacingPx = isScalableGrid ? borderSpacing : 0;
     }
@@ -573,11 +579,7 @@
         }
 
         // Hotseat
-        if (isVerticalLayout) {
-            hotseatBarSizePx = iconSizePx + hotseatBarSidePaddingStartPx
-                    + hotseatBarSidePaddingEndPx;
-        }
-        hotseatCellHeightPx = iconSizePx;
+        updateHotseatIconSize(iconSizePx);
 
         if (!isVerticalLayout) {
             int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx
@@ -763,7 +765,8 @@
                     hotseatBarTopPaddingPx,
                     hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx
                             + mInsets.right,
-                    hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx);
+                    hotseatBarSizePx - hotseatCellHeightPx - hotseatBarTopPaddingPx
+                            + cellLayoutBottomPaddingPx + mInsets.bottom);
         }
         return mHotseatPadding;
     }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c5d280d..11ddafb 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1079,7 +1079,7 @@
         if (ALL_APPS.equals(mPrevLauncherState) && !ALL_APPS.equals(state)
                 // Making sure mAllAppsSessionLogId is not null to avoid double logging.
                 && mAllAppsSessionLogId != null) {
-            getAppsView().getSearchUiManager().resetSearch();
+            getAppsView().reset(false);
             getStatsLogManager().logger()
                     .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
                             .setWorkspace(
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 303bb01..f28f54a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2792,7 +2792,7 @@
             }
         } else if (mDragInfo != null) {
             // When drag is cancelled, reattach content view back to its original parent.
-            if (mDragInfo.cell instanceof LauncherAppWidgetHostView) {
+            if (mDragInfo.cell instanceof LauncherAppWidgetHostView && d.dragView != null) {
                 d.dragView.detachContentView(/* reattachToPreviousParent= */ true);
             }
             final CellLayout cellLayout = mLauncher.getCellLayout(
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index aabbe65..ab3ea0a 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -255,7 +255,6 @@
             mWorkModeSwitch.updateCurrentState(isEnabled);
         }
         mWorkAdapterProvider.updateCurrentState(isEnabled);
-        mAH[AdapterHolder.WORK].applyPadding();
     }
 
     private void hideInput() {
@@ -509,7 +508,10 @@
                     R.layout.work_mode_fab, this, false);
             this.addView(mWorkModeSwitch);
             mWorkModeSwitch.setInsets(mInsets);
-            mWorkModeSwitch.post(this::resetWorkProfile);
+            mWorkModeSwitch.post(() -> {
+                mAH[AdapterHolder.WORK].applyPadding();
+                resetWorkProfile();
+            });
         }
     }
 
@@ -633,6 +635,7 @@
             mSearchModeWhileUsingTabs = true;
             rebindAdapters(false); // hide tabs
         }
+        mHeader.setCollapsed(true);
     }
 
     public void onClearSearchResult() {
@@ -715,7 +718,7 @@
         if (mHeaderPaint.getColor() != mScrimColor && mHeaderPaint.getColor() != 0) {
             int bottom = mUsingTabs && mHeader.mHeaderCollapsed ? mHeader.getVisibleBottomBound()
                     : mSearchContainer.getBottom();
-            canvas.drawRect(0, 0, getWidth(), bottom + getTranslationY(),
+            canvas.drawRect(0, 0, canvas.getWidth(), bottom + getTranslationY(),
                     mHeaderPaint);
 
             if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && getTranslationY() == 0) {
@@ -787,7 +790,6 @@
                 int bottomOffset = mWorkModeSwitch != null && mIsWork ? switchH : 0;
                 recyclerView.setPadding(padding.left, padding.top, padding.right,
                         padding.bottom + bottomOffset);
-                recyclerView.scrollToTop();
             }
         }
 
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index d65eb22..66f5c87 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -78,6 +78,9 @@
     public static final BooleanFlag UNSTABLE_SPRINGS = getDebugFlag(
             "UNSTABLE_SPRINGS", false, "Enable unstable springs for quickstep animations");
 
+    public static final BooleanFlag ENABLE_LOCAL_COLOR_POPUPS = getDebugFlag(
+            "ENABLE_LOCAL_COLOR_POPUPS", false, "Enable local color extraction for popups.");
+
     public static final BooleanFlag KEYGUARD_ANIMATION = getDebugFlag(
             "KEYGUARD_ANIMATION", false, "Enable animation for keyguard going away on wallpaper");
 
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index ddff338..79e5b5d 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -27,6 +27,7 @@
 import androidx.slice.SliceItem;
 
 import com.android.launcher3.R;
+import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
 import com.android.launcher3.logger.LauncherAtom.FromState;
 import com.android.launcher3.logger.LauncherAtom.ToState;
@@ -600,6 +601,13 @@
         }
 
         /**
+         * Sets logging fields from provided {@link LauncherAtom.Slice}.
+         */
+        default StatsLogger withSlice(LauncherAtom.Slice slice) {
+            return this;
+        }
+
+        /**
          * Builds the final message and logs it as {@link EventEnum}.
          */
         default void log(EventEnum event) {
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index cb35f74..a89fb3b 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -16,9 +16,12 @@
 
 package com.android.launcher3.popup;
 
+import static androidx.core.content.ContextCompat.getColorStateList;
+
 import static com.android.launcher3.anim.Interpolators.ACCELERATED_EASE;
 import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_LOCAL_COLOR_POPUPS;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -28,6 +31,7 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
@@ -133,6 +137,8 @@
 
     private final String mIterateChildrenTag;
 
+    private final int[] mColors;
+
     public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mInflater = LayoutInflater.from(context);
@@ -171,9 +177,19 @@
 
         boolean isAboveAnotherSurface = getTopOpenViewWithType(mLauncher, TYPE_FOLDER) != null
                 || mLauncher.getStateManager().getState() == LauncherState.ALL_APPS;
-        if (!isAboveAnotherSurface && Utilities.ATLEAST_S) {
+        if (!isAboveAnotherSurface && Utilities.ATLEAST_S && ENABLE_LOCAL_COLOR_POPUPS.get()) {
             setupColorExtraction();
         }
+
+        if (isAboveAnotherSurface) {
+            mColors = new int[] {
+                    getColorStateList(context, R.color.popup_color_first).getDefaultColor()};
+        } else {
+            mColors = new int[] {
+                    getColorStateList(context, R.color.popup_color_first).getDefaultColor(),
+                    getColorStateList(context, R.color.popup_color_second).getDefaultColor(),
+                    getColorStateList(context, R.color.popup_color_third).getDefaultColor()};
+        }
     }
 
     public ArrowPopup(Context context, AttributeSet attrs) {
@@ -220,6 +236,16 @@
      * Set the margins and radius of backgrounds after views are properly ordered.
      */
     public void assignMarginsAndBackgrounds(ViewGroup viewGroup) {
+        assignMarginsAndBackgrounds(viewGroup, Color.TRANSPARENT);
+    }
+
+    /**
+     * @param backgroundColor When Color.TRANSPARENT, we get color from {@link #mColors}.
+     *                        Otherwise, we will use this color for all child views.
+     */
+    private void assignMarginsAndBackgrounds(ViewGroup viewGroup, int backgroundColor) {
+        final boolean getColorFromColorArray = backgroundColor == Color.TRANSPARENT;
+
         int count = viewGroup.getChildCount();
         int totalVisibleShortcuts = 0;
         for (int i = 0; i < count; i++) {
@@ -229,8 +255,10 @@
             }
         }
 
+        int numVisibleChild = 0;
         int numVisibleShortcut = 0;
         View lastView = null;
+        AnimatorSet colorAnimator = new AnimatorSet();
         for (int i = 0; i < count; i++) {
             View view = viewGroup.getChildAt(i);
             if (view.getVisibility() == VISIBLE) {
@@ -242,8 +270,14 @@
                 MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
                 mlp.bottomMargin = 0;
 
+
+                if (getColorFromColorArray) {
+                    backgroundColor = mColors[numVisibleChild % mColors.length];
+                }
+
                 if (view instanceof ViewGroup && mIterateChildrenTag.equals(view.getTag())) {
-                    assignMarginsAndBackgrounds((ViewGroup) view);
+                    assignMarginsAndBackgrounds((ViewGroup) view, backgroundColor);
+                    numVisibleChild++;
                     continue;
                 }
 
@@ -261,8 +295,22 @@
                         numVisibleShortcut++;
                     }
                 }
+
+                if (!ENABLE_LOCAL_COLOR_POPUPS.get()) {
+                    setChildColor(view, backgroundColor, colorAnimator);
+                    // Arrow color matches the first child or the last child.
+                    if (!mIsAboveIcon && numVisibleChild == 0) {
+                        mArrowColor = backgroundColor;
+                    } else if (mIsAboveIcon) {
+                        mArrowColor = backgroundColor;
+                    }
+                }
+
+                numVisibleChild++;
             }
         }
+
+        colorAnimator.setDuration(0).start();
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
     }
 
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index 8f814a1..923eb19 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -105,9 +105,8 @@
 
         @NonNull @Override
         protected EdgeEffect createEdgeEffect(RecyclerView view, int direction) {
-            switch (direction) {
-                case DIRECTION_TOP:
-                    return new EdgeEffectProxy(getContext(), mEdgeGlowTop);
+            if (direction == DIRECTION_TOP) {
+                return new EdgeEffectProxy(getContext(), mEdgeGlowTop);
             }
             return super.createEdgeEffect(view, direction);
         }
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index 5a29171..d77d99d 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -160,8 +160,11 @@
             }
         }
 
-        this.minSpanX = minSpanX;
-        this.minSpanY = minSpanY;
+        // If minSpanX/Y > spanX/Y, ignore the minSpanX/Y to match the behavior described in
+        // minResizeWidth & minResizeHeight Android documentation. See
+        // https://developer.android.com/reference/android/appwidget/AppWidgetProviderInfo
+        this.minSpanX = Math.min(spanX, minSpanX);
+        this.minSpanY = Math.min(spanY, minSpanY);
         this.maxSpanX = maxSpanX;
         this.maxSpanY = maxSpanY;
         this.mIsMinSizeFulfilled = Math.min(spanX, minSpanX) <= idp.numColumns