Snap for 7490979 from a754e678a7a2fddfa716783f8c5f2fc99da68973 to sc-release

Change-Id: Ie85e9f7b312b9c7f7388bf9e9ab096bfb3dde8a0
diff --git a/quickstep/res/drawable/task_menu_item_bg.xml b/quickstep/res/drawable/task_menu_item_bg.xml
new file mode 100644
index 0000000..b6a8b90
--- /dev/null
+++ b/quickstep/res/drawable/task_menu_item_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="?android:attr/colorPrimary"/>
+    <corners android:radius="@dimen/task_menu_item_corner_radius"/>
+</shape>
diff --git a/quickstep/res/layout/task_menu.xml b/quickstep/res/layout/task_menu.xml
index 763e45e..3a47e99 100644
--- a/quickstep/res/layout/task_menu.xml
+++ b/quickstep/res/layout/task_menu.xml
@@ -25,7 +25,7 @@
 
     <TextView
         android:id="@+id/task_name"
-        android:background="?android:attr/colorPrimary"
+        android:background="@drawable/task_menu_item_bg"
         android:textColor="?android:attr/textColorPrimary"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/quickstep/res/layout/task_view_menu_option.xml b/quickstep/res/layout/task_view_menu_option.xml
index 19ca3e3..5978b97 100644
--- a/quickstep/res/layout/task_view_menu_option.xml
+++ b/quickstep/res/layout/task_view_menu_option.xml
@@ -21,7 +21,7 @@
     android:orientation="vertical"
     android:paddingTop="@dimen/task_card_menu_option_vertical_padding"
     android:paddingBottom="@dimen/task_card_menu_option_vertical_padding"
-    android:background="?android:attr/colorPrimary"
+    android:background="@drawable/task_menu_item_bg"
     android:theme="@style/PopupItem" >
 
     <View
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 60eeaff..28262a8 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -24,6 +24,7 @@
 
     <!--  Task Menu View  -->
     <dimen name="task_menu_corner_radius">22dp</dimen>
+    <dimen name="task_menu_item_corner_radius">4dp</dimen>
     <dimen name="overview_proactive_row_height">48dp</dimen>
     <dimen name="overview_proactive_row_bottom_margin">16dp</dimen>
 
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 000a95d..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,
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/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/dimens.xml b/res/values/dimens.xml
index bc63c06..270c92e 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -41,6 +41,7 @@
 
     <!-- Scalable Grid -->
     <dimen name="scalable_grid_left_right_margin">22dp</dimen>
+    <dimen name="scalable_grid_qsb_bottom_margin">42dp</dimen>
 
     <!-- Workspace page indicator -->
     <dimen name="workspace_page_indicator_height">24dp</dimen>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index d301787..009e098 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -158,6 +158,9 @@
     public final int hotseatBarSidePaddingStartPx;
     public final int hotseatBarSidePaddingEndPx;
 
+    public final float qsbBottomMarginOriginalPx;
+    public int qsbBottomMarginPx;
+
     // All apps
     public int allAppsOpenVerticalTranslate;
     public int allAppsCellHeightPx;
@@ -331,6 +334,10 @@
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
         updateHotseatIconSize(pxFromDp(inv.iconSize, mMetrics, 1f));
 
+        qsbBottomMarginOriginalPx = isScalableGrid
+                ? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin)
+                : 0;
+
         overviewTaskMarginPx = res.getDimensionPixelSize(R.dimen.overview_task_margin);
         overviewTaskIconSizePx =
                 isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get() ? res.getDimensionPixelSize(
@@ -359,6 +366,8 @@
             extraHotseatBottomPadding = Math.round(paddingHotseatBottom * iconScale);
 
             hotseatBarSizePx += extraHotseatBottomPadding;
+
+            qsbBottomMarginPx = Math.round(qsbBottomMarginOriginalPx * iconScale);
         } 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
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index ff380ce..42b4e89 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -206,8 +206,14 @@
         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);
+
+        if (dp.isScalableGrid && dp.qsbBottomMarginPx <= freeSpace) {
+            return dp.qsbBottomMarginPx;
+        } else {
+            return (int) (freeSpace * QSB_CENTER_FACTOR) + (dp.isTaskbarPresent
+                    ? dp.taskbarSize
+                    : dp.getInsets().bottom);
+        }
     }
 
     /**
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/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index d0e69fa..37b950d 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -23,6 +23,7 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.IntProperty;
+import android.util.Log;
 import android.util.Pair;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -35,6 +36,8 @@
 import android.widget.TableRow;
 import android.widget.TextView;
 
+import androidx.annotation.GuardedBy;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.LauncherAppState;
@@ -51,6 +54,7 @@
  * Bottom sheet for the "Widgets" system shortcut in the long-press popup.
  */
 public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
+    private static final String TAG = "WidgetsBottomSheet";
 
     private static final IntProperty<View> PADDING_BOTTOM =
             new IntProperty<View>("paddingBottom") {
@@ -128,6 +132,32 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (doMeasure(widthMeasureSpec, heightMeasureSpec)) {
+            boolean hasUpdated = doMeasure(widthMeasureSpec, heightMeasureSpec);
+            if (hasUpdated) {
+                Log.w(TAG, "WidgetsBottomSheet dimension has been updated after a 2nd"
+                        + " measurement.");
+            }
+        }
+    }
+
+    /**
+     * Measures the dimension of this view and its children.
+     *
+     * <p>This function takes account of the following during measurement:
+     * <ol>
+     *     <li>status bar and system navigation bar insets</li>
+     *     <li>
+     *         number of spans that can fit in a row. This affects the number of widgets that can
+     *         fit in a row.
+     *     </li>
+     * </ol>
+     *
+     * @return {@code true} if the width or height of this view or its children have changed after
+     *          the measurement. Otherwise, returns {@code false}.
+     */
+    @GuardedBy("MainThread")
+    private boolean doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
         int widthUsed;
         if (mInsets.bottom > 0) {
@@ -153,7 +183,9 @@
             // Ensure the table layout is showing widgets in the right column after measure.
             mMaxHorizontalSpan = maxHorizontalSpan;
             onWidgetsBound();
+            return true;
         }
+        return false;
     }
 
     @Override